from typing import List def _digit_value(ch: str) -> int: # Compared by character range rather than int(ch, 16), which would accept # full-width and other scripts' digits that the other languages refuse. if "0" <= ch <= "9": return ord(ch) - 48 if "a" <= ch <= "f": return ord(ch) - 87 if "A" <= ch <= "F": return ord(ch) - 55 return -1 def hex_decode(text: str) -> List[int]: """Hexadecimal text back to bytes, either case, strictly. A 0x prefix, whitespace or an odd digit count is an error, not skipped. """ if not isinstance(text, str): raise TypeError("hex text must be a string") # Characters are checked before the length, so the answer for non-ASCII # input does not depend on how a language counts its length. digits = [] for ch in text: d = _digit_value(ch) if d < 0: raise ValueError("hex text may only contain the digits 0-9, a-f and A-F") digits.append(d) if len(digits) % 2 != 0: raise ValueError("hex text must have an even number of digits, received %d" % len(digits)) return [digits[i] * 16 + digits[i + 1] for i in range(0, len(digits), 2)]