#
# Jasy - Web Tooling Framework
# Copyright 2013 Sebastian Fastner
# Copyright 2013-2014 Sebastian Werner
#
base62Table = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
base62InvertedTable = {}
i = 0
while i < 62:
base62InvertedTable[base62Table[i]] = i
i += 1
# And mask for 6, 6, 6, 5, 4, 3, 2, 1 leading bits
bitMask = [252, 252, 252, 248, 240, 224, 192, 128]
[docs]def encodeArrayOfBytes(arr):
# This works like a bit register. Take first 6 bits and append it to result. Take next 6 bits and so on.
# A special case is if the 6 bits represents 60, 61, 62 or 63. In this case one more bit is used to
# reduce 6 bit (= 64 different values) by two values.
result = []
charLength = len(arr)
bitLength = charLength * 8
bitPos = 0
specialBit = None
while bitPos < bitLength:
charOffset = int(bitPos / 8)
bitOffset = bitPos % 8
if charOffset + 1 >= charLength:
# Special case : no more next char so no more bits
remainingBits = bitLength - bitPos
if remainingBits >= 6:
moveRight = 2
else:
moveRight = 8 - remainingBits
extractedBits = ((arr[charOffset] << bitOffset) & 252) >> moveRight
else:
leftoverBits = bitOffset - 2
# print("LEFT-OVER-BITS: %s" % leftoverBits)
if (8 - leftoverBits) >= 0 and (8 - leftoverBits) < 8:
bitMaskValue = bitMask[8 - leftoverBits]
else:
bitMaskValue = 0
extractedBits = (
((arr[charOffset] << bitOffset) & bitMask[bitOffset]) +
((arr[charOffset + 1] & bitMaskValue) >> (6 - leftoverBits))
) >> 2
if (extractedBits & 62) == 60:
extractedBits = 60
bitPos -= 1
elif (extractedBits & 62) == 62:
extractedBits = 61
bitPos -= 1
result.append(extractedBits)
bitPos += 6
return result
[docs]def decodeToArrayOfBytes(arr):
result = []
current = 0
bitOffset = 0
charOffset = 0
charLength = len(arr)
charOffset = 0
while charOffset < charLength:
char = arr[charOffset]
bitsNeeded = 8 - bitOffset
if char == 60 or char == 61:
correctBits = 30 if char == 60 else 31
if bitsNeeded <= 5:
current = ((current << bitsNeeded) + (correctBits >> (5 - bitsNeeded))) & 255
result.append(current)
current = (((correctBits << bitsNeeded) & 255) >> bitsNeeded) & 63
bitOffset = 5 - bitsNeeded
else:
current = (current << 5) + correctBits
bitOffset += 5
if bitOffset == 8:
result.append(current)
current = 0
else:
if bitsNeeded <= 6:
last = charOffset == charLength - 1
charShift = char
if not last:
charShift = char >> (6 - bitsNeeded)
current = ((current << bitsNeeded) + charShift) & 255
result.append(current)
if not last:
current = (((char << bitsNeeded) & 255) >> bitsNeeded) & 63
bitOffset = 6 - bitsNeeded
else:
current = (current << 6) + char
bitOffset += 6
if bitOffset == 8:
result.append(current)
current = 0
charOffset += 1
return result
[docs]def encodeArrayToString(arr):
result = encodeArrayOfBytes(arr)
i = 0
ii = len(result)
while i < ii:
result[i] = base62Table[result[i]]
i += 1
return "".join(result)
[docs]def decodeStringToArray(str):
ii = len(str)
byteArray = []
i = 0
while i < ii:
byteArray.append(base62InvertedTable[str[i]])
i += 1
return decodeToArrayOfBytes(byteArray)
[docs]def encode(str):
return encodeArrayToString(bytearray(str, "utf-8"))
[docs]def decode(str):
return bytearray(decodeStringToArray(str)).decode()