62 lines
1.8 KiB
Python
62 lines
1.8 KiB
Python
|
|
import hashlib
|
||
|
|
import zlib
|
||
|
|
from typing import Tuple
|
||
|
|
|
||
|
|
|
||
|
|
class GSZ3DecompressionError(Exception):
|
||
|
|
pass
|
||
|
|
|
||
|
|
|
||
|
|
class GSZ3Decompressor:
|
||
|
|
MAGIC = b'GSZ3'
|
||
|
|
VERSION = 1
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def decompress(data: bytes) -> str:
|
||
|
|
if len(data) < 12:
|
||
|
|
raise GSZ3DecompressionError("Data too short for GSZ3 header")
|
||
|
|
|
||
|
|
if data[:4] != GSZ3Decompressor.MAGIC:
|
||
|
|
raise GSZ3DecompressionError("Invalid GSZ3 magic number")
|
||
|
|
|
||
|
|
version = int.from_bytes(data[4:5], 'big')
|
||
|
|
if version != GSZ3Decompressor.VERSION:
|
||
|
|
raise GSZ3DecompressionError(f"Unsupported GSZ3 version: {version}")
|
||
|
|
|
||
|
|
payload_len = int.from_bytes(data[5:9], 'big')
|
||
|
|
stored_checksum = data[9:12]
|
||
|
|
|
||
|
|
if len(data) < 12 + payload_len:
|
||
|
|
raise GSZ3DecompressionError("Incomplete payload")
|
||
|
|
|
||
|
|
payload = data[12:12 + payload_len]
|
||
|
|
computed_checksum = GSZ3Decompressor._compute_checksum(payload)
|
||
|
|
|
||
|
|
if computed_checksum != stored_checksum:
|
||
|
|
raise GSZ3DecompressionError("Checksum mismatch")
|
||
|
|
|
||
|
|
try:
|
||
|
|
decompressed = zlib.decompress(payload)
|
||
|
|
return decompressed.decode('utf-8')
|
||
|
|
except (zlib.error, UnicodeDecodeError) as e:
|
||
|
|
raise GSZ3DecompressionError(f"Decompression failed: {e}")
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def compress(text: str) -> bytes:
|
||
|
|
data = text.encode('utf-8')
|
||
|
|
compressed = zlib.compress(data, level=9)
|
||
|
|
checksum = GSZ3Decompressor._compute_checksum(compressed)
|
||
|
|
payload_len = len(compressed)
|
||
|
|
|
||
|
|
header = GSZ3Decompressor.MAGIC
|
||
|
|
header += bytes([GSZ3Decompressor.VERSION])
|
||
|
|
header += payload_len.to_bytes(4, 'big')
|
||
|
|
header += checksum
|
||
|
|
|
||
|
|
return header + compressed
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def _compute_checksum(data: bytes) -> bytes:
|
||
|
|
h = hashlib.sha256(data).digest()
|
||
|
|
return h[:3]
|