From ff3e8eddb72d633abf233aa2ea90fb5e9e028dab Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Wed, 12 Feb 2025 18:19:49 +0100 Subject: [PATCH] Add code and packaging structure --- LICENSE | 2 +- base122.py | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 21 +++++++++++ 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 base122.py create mode 100644 setup.py diff --git a/LICENSE b/LICENSE index 261eeb9..b4940f6 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2025 Bart Broere Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/base122.py b/base122.py new file mode 100644 index 0000000..8a41e51 --- /dev/null +++ b/base122.py @@ -0,0 +1,106 @@ +""" +Implementation of Base122 in pure Python. +It attempts to mirror behaviour of the base64 built-in module. + +Its API is not yet complete or final. This is a first version for testing. +""" + +illegals = [0, 10, 13, 34, 38, 92] + +def b122encode(raw_data): + if isinstance(raw_data, str): + raise ValueError("Input should be bytes") + + out_data = [] + cur_index = 0 + cur_bit = 0 + + def get_7(): + nonlocal cur_index, cur_bit + if cur_index >= len(raw_data): + return False + + first_byte = raw_data[cur_index] + first_part = ((0b11111110 >> cur_bit) & first_byte) << cur_bit + first_part >>= 1 + + cur_bit += 7 + if cur_bit < 8: + return first_part + + cur_bit -= 8 + cur_index += 1 + + if cur_index >= len(raw_data): + return first_part + + second_byte = raw_data[cur_index] + second_part = ((0xFF00 >> cur_bit) & second_byte) & 0xFF + second_part >>= 8 - cur_bit + return first_part | second_part + + while True: + bits = get_7() + if bits is False: + break + + illegal_index = illegals.index(bits) if bits in illegals else None + if illegal_index is not None: + next_bits = get_7() + + b1 = 0b11000010 + b2 = 0b10000000 + if next_bits is False: + b1 |= 7 << 2 + next_bits = bits + else: + b1 |= (illegal_index & 0b111) << 2 + + first_bit = (next_bits & 0b01000000) > 0 + b1 |= first_bit + b2 |= next_bits & 0b00111111 + out_data.append(b1) + out_data.append(b2) + else: + out_data.append(bits) + return bytes(out_data) + + +def b122decode(base122_data): + decoded = [] + cur_byte = 0 + bit_of_byte = 0 + + def push7(byte): + nonlocal cur_byte, bit_of_byte + byte <<= 1 + cur_byte |= (byte >> bit_of_byte) + bit_of_byte += 7 + if bit_of_byte >= 8: + decoded.append(cur_byte & 0xFF) + bit_of_byte -= 8 + cur_byte = (byte << (7 - bit_of_byte)) & 0xFF + + for c in base122_data: + if c > 127: + illegal_index = (c >> 8) & 7 + if illegal_index != 7: + push7(illegals[illegal_index]) + push7(c & 127) + else: + push7(c) + return bytes(decoded) + + +if __name__ == "__main__": + original_data = b"Hello World!" + encoded_data = b122encode(original_data) + print(f"Encoded data: {encoded_data}") + decoded_data = b122decode(encoded_data) + print(f"Decoded data: {decoded_data}") + + original_data = 'Something else 🫨'.encode('utf-8') + encoded_data = b122encode(original_data) + print(f"Encoded data: {encoded_data.decode('utf8')}") + decoded_data = b122decode(encoded_data) + print(f"Decoded data: {decoded_data.decode('utf-8')}") \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..3c7343e --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +from setuptools import setup + +setup( + name='base122', + version='2025.2.12', + description='A short description of the base122 library', + long_description=open('README.md').read(), + long_description_content_type='text/markdown', + url='https://github.com/bartbroere/base122', + author='Bart Broere', + author_email='mail@bartbroere.eu', + license='Apache-2.0', + py_modules=['base122'], + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python :: 3', + 'Topic :: Utilities' + ] +) \ No newline at end of file