From 4f0cfb0fed2bbdf648ea379c8e2e65d42f3930e6 Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Tue, 23 Apr 2024 10:27:11 +0800 Subject: [PATCH] First working version --- .github/workflows/main.yml | 26 ++++++++++++++++++++++++++ .gitignore | 2 ++ .pytest.ini | 3 +++ README.md | 21 ++++++++++++++++++++- huffman.py | 10 ++-------- tests/basic_test.py | 23 +++++++++++++++++++++++ 6 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/main.yml create mode 100644 .gitignore create mode 100644 .pytest.ini create mode 100644 tests/basic_test.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..348332e --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,26 @@ +name: Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.11"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest mypy + - name: Run tests + run: | + pytest tests/ + # - name: Check types with mypy + # run: | + # mypy . diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a60b85 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +*.pyc diff --git a/.pytest.ini b/.pytest.ini new file mode 100644 index 0000000..f5a52af --- /dev/null +++ b/.pytest.ini @@ -0,0 +1,3 @@ +[pytest] +pythonpath = . + diff --git a/README.md b/README.md index 4d5b9ff..3ab013b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,22 @@ # huffman-py -Teeworlds huffman compression ported to python. Work in progress. Not working yet. +Teeworlds huffman compression ported to python. Work in progress. + +This is not a general purpose compression library. It uses the teeworlds frequency table +and is intended to be used for teeworlds networking. + +## example usage + +```python +from huffman import Huffman + +huffman = Huffman() +decompressed = huffman.decompress(bytes([174, 149, 19, 92, 9, 87, 194, 22, 177, 86, 220, 218, 34, 56, 185, 18, 156, 168, 184, 1])) +print(decompressed) # => b'hello world' +``` + +## similar projects + +- teeworlds huffman (ruby) https://github.com/ChillerDragon/huffman-tw +- teeworlds huffman (rust) https://github.com/edg-l/rustyman + diff --git a/huffman.py b/huffman.py index fdc395e..31034fe 100755 --- a/huffman.py +++ b/huffman.py @@ -93,13 +93,13 @@ def construct_tree(self, frequencies: list[int]) -> None: self.start_node = self.nodes[self.num_nodes-1] self.setbits_r(self.start_node, 0, 0) - def __init__(self, frequencies: list[int]) -> None: + def __init__(self) -> None: self.nodes: list[Node] = [Node(0, 0, 0) for _ in range(0, HUFFMAN_MAX_NODES)] self.decoded_lut: list[Node] = [Node(0, 0, 0) for _ in range(0, HUFFMAN_LUTSIZE)] self.num_nodes = 0 self.start_node = Node(0, 0, 0) - self.construct_tree(frequencies) + self.construct_tree(FREQUENCY_TABLE) for i in range(0, HUFFMAN_LUTSIZE): bits = i @@ -162,9 +162,3 @@ def decompress(self, data: bytes) -> bytes: dst.append(node.symbol) return bytes(dst) -huffman = Huffman(FREQUENCY_TABLE) -# a = huffman.decompress(bytes([174, 149, 19, 92, 9, 87, 194, 22, 177, 86, 220, 218, 34, 56, 185, 18, 156, 168, 184, 1])) -a = huffman.decompress(bytearray([188, 21, 55, 0])) - -print(a) - diff --git a/tests/basic_test.py b/tests/basic_test.py new file mode 100644 index 0000000..ea120e3 --- /dev/null +++ b/tests/basic_test.py @@ -0,0 +1,23 @@ +from huffman import Huffman + +def test_huffman(): + huffman = Huffman() + compressed = b'\x4a\x42\x88\x4a\x6e\x16\xba\x31\x46\xa2\x84\x9e\xbf\xe2\x06' + decompressed = huffman.decompress(compressed) + expected = b'\x40\x02\x02\x02\x00\x40\x07\x03\x22\x01\x00\x01\x00\x01\x08\x40\x01\x04\x0b' + assert decompressed == expected + +def test_huffman_hello_world(): + huffman = Huffman() + compressed = bytes([174, 149, 19, 92, 9, 87, 194, 22, 177, 86, 220, 218, 34, 56, 185, 18, 156, 168, 184, 1]) + decompressed = huffman.decompress(compressed) + expected = b'hello world' + assert decompressed == expected + +def test_huffman_A(): + huffman = Huffman() + compressed = bytes([188, 21, 55, 0]) + decompressed = huffman.decompress(compressed) + expected = b'A' + assert decompressed == expected +