Skip to content

Commit

Permalink
Some pylints
Browse files Browse the repository at this point in the history
  • Loading branch information
ChillerDragon committed Apr 23, 2024
1 parent 2805df2 commit b16ea96
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 23 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10"]
python-version: ["3.11"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
Expand Down
3 changes: 3 additions & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[MESSAGES CONTROL]

disable=too-few-public-methods,
98 changes: 77 additions & 21 deletions huffman.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,100 @@
#!/usr/bin/env python3
"""
Teeworlds huffman compression
Example usage::
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'
"""

import functools

from typing import Optional

FREQUENCY_TABLE = [
1 << 30, 4545, 2657, 431, 1950, 919, 444, 482, 2244, 617, 838, 542, 715, 1814, 304, 240, 754, 212, 647, 186,
283, 131, 146, 166, 543, 164, 167, 136, 179, 859, 363, 113, 157, 154, 204, 108, 137, 180, 202, 176,
872, 404, 168, 134, 151, 111, 113, 109, 120, 126, 129, 100, 41, 20, 16, 22, 18, 18, 17, 19,
16, 37, 13, 21, 362, 166, 99, 78, 95, 88, 81, 70, 83, 284, 91, 187, 77, 68, 52, 68,
59, 66, 61, 638, 71, 157, 50, 46, 69, 43, 11, 24, 13, 19, 10, 12, 12, 20, 14, 9,
20, 20, 10, 10, 15, 15, 12, 12, 7, 19, 15, 14, 13, 18, 35, 19, 17, 14, 8, 5,
15, 17, 9, 15, 14, 18, 8, 10, 2173, 134, 157, 68, 188, 60, 170, 60, 194, 62, 175, 71,
148, 67, 167, 78, 211, 67, 156, 69, 1674, 90, 174, 53, 147, 89, 181, 51, 174, 63, 163, 80,
167, 94, 128, 122, 223, 153, 218, 77, 200, 110, 190, 73, 174, 69, 145, 66, 277, 143, 141, 60,
136, 53, 180, 57, 142, 57, 158, 61, 166, 112, 152, 92, 26, 22, 21, 28, 20, 26, 30, 21,
32, 27, 20, 17, 23, 21, 30, 22, 22, 21, 27, 25, 17, 27, 23, 18, 39, 26, 15, 21,
12, 18, 18, 27, 20, 18, 15, 19, 11, 17, 33, 12, 18, 15, 19, 18, 16, 26, 17, 18,
9, 10, 25, 22, 22, 17, 20, 16, 6, 16, 15, 20, 14, 18, 24, 335, 1517 ]
1 << 30, 4545, 2657, 431, 1950, 919, 444, 482, 2244,
617, 838, 542, 715, 1814, 304, 240, 754, 212, 647, 186,
283, 131, 146, 166, 543, 164, 167, 136, 179, 859, 363, 113, 157, 154, 204, 108, 137, 180, 202, 176,
872, 404, 168, 134, 151, 111, 113, 109, 120, 126, 129, 100, 41, 20, 16, 22, 18, 18, 17, 19,
16, 37, 13, 21, 362, 166, 99, 78, 95, 88, 81, 70, 83, 284, 91, 187, 77, 68, 52, 68,
59, 66, 61, 638, 71, 157, 50, 46, 69, 43, 11, 24, 13, 19, 10, 12, 12, 20, 14, 9,
20, 20, 10, 10, 15, 15, 12, 12, 7, 19, 15, 14, 13, 18, 35, 19, 17, 14, 8, 5,
15, 17, 9, 15, 14, 18, 8, 10, 2173, 134, 157, 68, 188, 60, 170, 60, 194, 62, 175, 71,
148, 67, 167, 78, 211, 67, 156, 69, 1674, 90, 174, 53, 147, 89, 181, 51, 174, 63, 163, 80,
167, 94, 128, 122, 223, 153, 218, 77, 200, 110, 190, 73, 174, 69, 145, 66, 277, 143, 141, 60,
136, 53, 180, 57, 142, 57, 158, 61, 166, 112, 152, 92, 26, 22, 21, 28, 20, 26, 30, 21,
32, 27, 20, 17, 23, 21, 30, 22, 22, 21, 27, 25, 17, 27, 23, 18, 39, 26, 15, 21,
12, 18, 18, 27, 20, 18, 15, 19, 11, 17, 33, 12, 18, 15, 19, 18, 16, 26, 17, 18,
9, 10, 25, 22, 22, 17, 20, 16, 6, 16, 15, 20, 14, 18, 24, 335, 1517]

HUFFMAN_EOF_SYMBOL = 256

HUFFMAN_MAX_SYMBOLS = HUFFMAN_EOF_SYMBOL + 1
HUFFMAN_MAX_NODES = HUFFMAN_MAX_SYMBOLS * 2 - 1

HUFFMAN_LUTBITS = 10
HUFFMAN_LUTSIZE = (1 << HUFFMAN_LUTBITS)
HUFFMAN_LUTMASK = (HUFFMAN_LUTSIZE - 1)
HUFFMAN_LUTSIZE = 1 << HUFFMAN_LUTBITS
HUFFMAN_LUTMASK = HUFFMAN_LUTSIZE - 1

class HuffmanConstructNode:
"""
Internal class to build the huffman tree
"""
def __init__(self, node_id: int, frequency: int) -> None:
self.node_id = node_id
self.frequency = frequency

def compare_nodes_by_frequency_desc(node1: HuffmanConstructNode, node2: HuffmanConstructNode) -> int:
def compare_nodes_by_frequency_desc(
node1: HuffmanConstructNode,
node2: HuffmanConstructNode
) -> int:
"""
Comparison callback for stable sort
"""
if node2.frequency < node1.frequency:
return -1
if node2.frequency > node1.frequency:
return 1
return 0

class Node:
class Node():
"""
Internal class to represent the huffman tree
"""
def __init__(self, bits, num_bits, symbol):
self.bits = bits
self.num_bits = num_bits
self.leafs = [0, 0]
self.symbol = symbol

class Huffman:
"""
Teeworlds huffman compression
Example usage::
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'
"""
def setbits_r(self, node: Node, bits: int, depth: int) -> None:
"""
Internal method to initialize the huffman tree.
"""
if node.leafs[1] != 0xFFFF:
self.setbits_r(self.nodes[node.leafs[1]], bits|(1<<depth), depth+1)
if node.leafs[0] != 0xFFFF:
Expand All @@ -59,7 +105,12 @@ def setbits_r(self, node: Node, bits: int, depth: int) -> None:
node.num_bits = depth

def construct_tree(self, frequencies: list[int]) -> None:
nodes_left: list[HuffmanConstructNode] = [HuffmanConstructNode(0, 0) for _ in range(0, HUFFMAN_MAX_SYMBOLS)]
"""
Internal method to initialize the huffman tree.
"""
nodes_left: list[HuffmanConstructNode] = [
HuffmanConstructNode(0, 0) for _ in range(0, HUFFMAN_MAX_SYMBOLS)
]

num_nodes_left = HUFFMAN_MAX_SYMBOLS

Expand All @@ -69,7 +120,7 @@ def construct_tree(self, frequencies: list[int]) -> None:
self.nodes[i].leafs[0] = 0xFFFF
self.nodes[i].leafs[1] = 0xFFFF

if(i == HUFFMAN_EOF_SYMBOL):
if i == HUFFMAN_EOF_SYMBOL:
nodes_left[i].frequency = 1
else:
nodes_left[i].frequency = frequencies[i]
Expand All @@ -79,7 +130,10 @@ def construct_tree(self, frequencies: list[int]) -> None:
self.num_nodes = HUFFMAN_MAX_SYMBOLS

while num_nodes_left > 1:
nodes_left = sorted(nodes_left, key=functools.cmp_to_key(compare_nodes_by_frequency_desc))
nodes_left = sorted(
nodes_left,
key=functools.cmp_to_key(compare_nodes_by_frequency_desc)
)

self.nodes[self.num_nodes].num_bits = 0
self.nodes[self.num_nodes].leafs[0] = nodes_left[num_nodes_left-1].node_id
Expand Down Expand Up @@ -118,6 +172,9 @@ def __init__(self) -> None:
self.decoded_lut[i] = node

def decompress(self, data: bytes) -> bytes:
"""
Decompresses data using the teeworlds frequency table
"""
src_index = 0
size = len(data)
dst = bytearray()
Expand Down Expand Up @@ -161,4 +218,3 @@ def decompress(self, data: bytes) -> bytes:
break
dst.append(node.symbol)
return bytes(dst)

0 comments on commit b16ea96

Please sign in to comment.