|
1 |
| -import base64 |
| 1 | +""" |
| 2 | +Base32 encoding and decoding |
2 | 3 |
|
| 4 | +https://en.wikipedia.org/wiki/Base32 |
| 5 | +""" |
| 6 | +B32_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" |
3 | 7 |
|
4 |
| -def base32_encode(string: str) -> bytes: |
| 8 | + |
| 9 | +def base32_encode(data: bytes) -> bytes: |
5 | 10 | """
|
6 |
| - Encodes a given string to base32, returning a bytes-like object |
7 |
| - >>> base32_encode("Hello World!") |
| 11 | + >>> base32_encode(b"Hello World!") |
8 | 12 | b'JBSWY3DPEBLW64TMMQQQ===='
|
9 |
| - >>> base32_encode("123456") |
| 13 | + >>> base32_encode(b"123456") |
10 | 14 | b'GEZDGNBVGY======'
|
11 |
| - >>> base32_encode("some long complex string") |
| 15 | + >>> base32_encode(b"some long complex string") |
12 | 16 | b'ONXW2ZJANRXW4ZZAMNXW24DMMV4CA43UOJUW4ZY='
|
13 | 17 | """
|
14 |
| - |
15 |
| - # encoded the input (we need a bytes like object) |
16 |
| - # then, b32encoded the bytes-like object |
17 |
| - return base64.b32encode(string.encode("utf-8")) |
| 18 | + binary_data = "".join(bin(ord(d))[2:].zfill(8) for d in data.decode("utf-8")) |
| 19 | + binary_data = binary_data.ljust(5 * ((len(binary_data) // 5) + 1), "0") |
| 20 | + b32_chunks = map("".join, zip(*[iter(binary_data)] * 5)) |
| 21 | + b32_result = "".join(B32_CHARSET[int(chunk, 2)] for chunk in b32_chunks) |
| 22 | + return bytes(b32_result.ljust(8 * ((len(b32_result) // 8) + 1), "="), "utf-8") |
18 | 23 |
|
19 | 24 |
|
20 |
| -def base32_decode(encoded_bytes: bytes) -> str: |
| 25 | +def base32_decode(data: bytes) -> bytes: |
21 | 26 | """
|
22 |
| - Decodes a given bytes-like object to a string, returning a string |
23 | 27 | >>> base32_decode(b'JBSWY3DPEBLW64TMMQQQ====')
|
24 |
| - 'Hello World!' |
| 28 | + b'Hello World!' |
25 | 29 | >>> base32_decode(b'GEZDGNBVGY======')
|
26 |
| - '123456' |
| 30 | + b'123456' |
27 | 31 | >>> base32_decode(b'ONXW2ZJANRXW4ZZAMNXW24DMMV4CA43UOJUW4ZY=')
|
28 |
| - 'some long complex string' |
| 32 | + b'some long complex string' |
29 | 33 | """
|
30 |
| - |
31 |
| - # decode the bytes from base32 |
32 |
| - # then, decode the bytes-like object to return as a string |
33 |
| - return base64.b32decode(encoded_bytes).decode("utf-8") |
| 34 | + binary_chunks = "".join( |
| 35 | + bin(B32_CHARSET.index(_d))[2:].zfill(5) |
| 36 | + for _d in data.decode("utf-8").strip("=") |
| 37 | + ) |
| 38 | + binary_data = list(map("".join, zip(*[iter(binary_chunks)] * 8))) |
| 39 | + return bytes("".join([chr(int(_d, 2)) for _d in binary_data]), "utf-8") |
34 | 40 |
|
35 | 41 |
|
36 | 42 | if __name__ == "__main__":
|
37 |
| - test = "Hello World!" |
38 |
| - encoded = base32_encode(test) |
39 |
| - print(encoded) |
| 43 | + import doctest |
40 | 44 |
|
41 |
| - decoded = base32_decode(encoded) |
42 |
| - print(decoded) |
| 45 | + doctest.testmod() |
0 commit comments