Skip to content

Commit abf0909

Browse files
authored
Write a proper implementation for base16 (TheAlgorithms#6909)
According to CONTRIBUTING.md: "Algorithms in this repo should not be how-to examples for existing Python packages."
1 parent 68f6e9a commit abf0909

File tree

1 file changed

+52
-23
lines changed

1 file changed

+52
-23
lines changed

ciphers/base16.py

+52-23
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,63 @@
1-
import base64
2-
3-
4-
def base16_encode(inp: str) -> bytes:
1+
def base16_encode(data: bytes) -> str:
52
"""
6-
Encodes a given utf-8 string into base-16.
3+
Encodes the given bytes into base16.
74
8-
>>> base16_encode('Hello World!')
9-
b'48656C6C6F20576F726C6421'
10-
>>> base16_encode('HELLO WORLD!')
11-
b'48454C4C4F20574F524C4421'
12-
>>> base16_encode('')
13-
b''
5+
>>> base16_encode(b'Hello World!')
6+
'48656C6C6F20576F726C6421'
7+
>>> base16_encode(b'HELLO WORLD!')
8+
'48454C4C4F20574F524C4421'
9+
>>> base16_encode(b'')
10+
''
1411
"""
15-
# encode the input into a bytes-like object and then encode b16encode that
16-
return base64.b16encode(inp.encode("utf-8"))
12+
# Turn the data into a list of integers (where each integer is a byte),
13+
# Then turn each byte into its hexadecimal representation, make sure
14+
# it is uppercase, and then join everything together and return it.
15+
return "".join([hex(byte)[2:].zfill(2).upper() for byte in list(data)])
1716

1817

19-
def base16_decode(b16encoded: bytes) -> str:
18+
def base16_decode(data: str) -> bytes:
2019
"""
21-
Decodes from base-16 to a utf-8 string.
20+
Decodes the given base16 encoded data into bytes.
2221
23-
>>> base16_decode(b'48656C6C6F20576F726C6421')
24-
'Hello World!'
25-
>>> base16_decode(b'48454C4C4F20574F524C4421')
26-
'HELLO WORLD!'
27-
>>> base16_decode(b'')
28-
''
22+
>>> base16_decode('48656C6C6F20576F726C6421')
23+
b'Hello World!'
24+
>>> base16_decode('48454C4C4F20574F524C4421')
25+
b'HELLO WORLD!'
26+
>>> base16_decode('')
27+
b''
28+
>>> base16_decode('486')
29+
Traceback (most recent call last):
30+
...
31+
ValueError: Base16 encoded data is invalid:
32+
Data does not have an even number of hex digits.
33+
>>> base16_decode('48656c6c6f20576f726c6421')
34+
Traceback (most recent call last):
35+
...
36+
ValueError: Base16 encoded data is invalid:
37+
Data is not uppercase hex or it contains invalid characters.
38+
>>> base16_decode('This is not base64 encoded data.')
39+
Traceback (most recent call last):
40+
...
41+
ValueError: Base16 encoded data is invalid:
42+
Data is not uppercase hex or it contains invalid characters.
2943
"""
30-
# b16decode the input into bytes and decode that into a human readable string
31-
return base64.b16decode(b16encoded).decode("utf-8")
44+
# Check data validity, following RFC3548
45+
# https://www.ietf.org/rfc/rfc3548.txt
46+
if (len(data) % 2) != 0:
47+
raise ValueError(
48+
"""Base16 encoded data is invalid:
49+
Data does not have an even number of hex digits."""
50+
)
51+
# Check the character set - the standard base16 alphabet
52+
# is uppercase according to RFC3548 section 6
53+
if not set(data) <= set("0123456789ABCDEF"):
54+
raise ValueError(
55+
"""Base16 encoded data is invalid:
56+
Data is not uppercase hex or it contains invalid characters."""
57+
)
58+
# For every two hexadecimal digits (= a byte), turn it into an integer.
59+
# Then, string the result together into bytes, and return it.
60+
return bytes(int(data[i] + data[i + 1], 16) for i in range(0, len(data), 2))
3261

3362

3463
if __name__ == "__main__":

0 commit comments

Comments
 (0)