599 lines
21 KiB
Markdown
599 lines
21 KiB
Markdown
|
|
# Huffman Декомпрессия
|
|||
|
|
|
|||
|
|
## Обзор
|
|||
|
|
|
|||
|
|
Это реализация **DEFLATE-подобного** алгоритма декомпрессии, используемого в [NRes](overview.md). Алгоритм поддерживает три режима блоков и использует два Huffman дерева для кодирования литералов/длин и расстояний.
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
int __thiscall sub_1001AF10(
|
|||
|
|
unsigned int *this, // Контекст декодера (HuffmanContext)
|
|||
|
|
int *a2 // Выходной параметр (результат операции)
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Структура контекста (HuffmanContext)
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
struct HuffmanContext {
|
|||
|
|
uint32_t bitBuffer[0x4000]; // 0x00000-0x0FFFF: Битовый буфер (32KB)
|
|||
|
|
uint32_t compressedSize; // 0x10000: Размер сжатых данных
|
|||
|
|
uint32_t unknown1; // 0x10004: Не используется
|
|||
|
|
uint32_t outputPosition; // 0x10008: Позиция в выходном буфере
|
|||
|
|
uint32_t currentByte; // 0x1000C: Текущий байт
|
|||
|
|
uint8_t* sourceData; // 0x10010: Указатель на сжатые данные
|
|||
|
|
uint8_t* destData; // 0x10014: Указатель на выходной буфер
|
|||
|
|
uint32_t bitPosition; // 0x10018: Позиция бита
|
|||
|
|
uint32_t inputPosition; // 0x1001C: Позиция чтения (this[16389])
|
|||
|
|
uint32_t decodedBytes; // 0x10020: Декодированные байты (this[16386])
|
|||
|
|
uint32_t bitBufferValue; // 0x10024: Значение бит буфера (this[16391])
|
|||
|
|
uint32_t bitsAvailable; // 0x10028: Доступные биты (this[16392])
|
|||
|
|
// ...
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Смещения в структуре:
|
|||
|
|
#define CTX_OUTPUT_POS 16385 // this[16385]
|
|||
|
|
#define CTX_DECODED_BYTES 16386 // this[16386]
|
|||
|
|
#define CTX_SOURCE_PTR 16387 // this[16387]
|
|||
|
|
#define CTX_DEST_PTR 16388 // this[16388]
|
|||
|
|
#define CTX_INPUT_POS 16389 // this[16389]
|
|||
|
|
#define CTX_BIT_BUFFER 16391 // this[16391]
|
|||
|
|
#define CTX_BITS_COUNT 16392 // this[16392]
|
|||
|
|
#define CTX_MAX_SYMBOL 16393 // this[16393]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Три режима блоков
|
|||
|
|
|
|||
|
|
Алгоритм определяет тип блока по первым 3 битам:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Биты: [TYPE:2] [FINAL:1]
|
|||
|
|
|
|||
|
|
FINAL = 1: Это последний блок
|
|||
|
|
TYPE:
|
|||
|
|
00 = Несжатый блок (сырые данные)
|
|||
|
|
01 = Сжатый с фиксированными Huffman кодами
|
|||
|
|
10 = Сжатый с динамическими Huffman кодами
|
|||
|
|
11 = Зарезервировано (ошибка)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Основной цикл декодирования
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
int decode_block(HuffmanContext* ctx) {
|
|||
|
|
// Читаем первый бит (FINAL)
|
|||
|
|
int final_bit = read_bit(ctx);
|
|||
|
|
|
|||
|
|
// Читаем 2 бита (TYPE)
|
|||
|
|
int type = read_bits(ctx, 2);
|
|||
|
|
|
|||
|
|
switch (type) {
|
|||
|
|
case 0: // 00 - Несжатый блок
|
|||
|
|
return decode_uncompressed_block(ctx);
|
|||
|
|
|
|||
|
|
case 1: // 01 - Фиксированные Huffman коды
|
|||
|
|
return decode_fixed_huffman_block(ctx);
|
|||
|
|
|
|||
|
|
case 2: // 10 - Динамические Huffman коды
|
|||
|
|
return decode_dynamic_huffman_block(ctx);
|
|||
|
|
|
|||
|
|
case 3: // 11 - Ошибка
|
|||
|
|
return 2; // Неподдерживаемый тип
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return final_bit ? 0 : 1; // 0 = конец, 1 = есть еще блоки
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Режим 0: Несжатый блок
|
|||
|
|
|
|||
|
|
Простое копирование байтов без сжатия.
|
|||
|
|
|
|||
|
|
### Алгоритм
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def decode_uncompressed_block(ctx):
|
|||
|
|
"""
|
|||
|
|
Формат несжатого блока:
|
|||
|
|
[LEN:16][NLEN:16][DATA:LEN]
|
|||
|
|
|
|||
|
|
Где:
|
|||
|
|
LEN - длина данных (little-endian)
|
|||
|
|
NLEN - инверсия LEN (~LEN)
|
|||
|
|
DATA - сырые данные
|
|||
|
|
"""
|
|||
|
|
# Выравнивание к границе байта
|
|||
|
|
bits_to_skip = ctx.bits_available & 7
|
|||
|
|
ctx.bit_buffer >>= bits_to_skip
|
|||
|
|
ctx.bits_available -= bits_to_skip
|
|||
|
|
|
|||
|
|
# Читаем длину (16 бит)
|
|||
|
|
length = read_bits(ctx, 16)
|
|||
|
|
|
|||
|
|
# Читаем инверсию длины (16 бит)
|
|||
|
|
nlength = read_bits(ctx, 16)
|
|||
|
|
|
|||
|
|
# Проверка целостности
|
|||
|
|
if length != (~nlength & 0xFFFF):
|
|||
|
|
return 1 # Ошибка
|
|||
|
|
|
|||
|
|
# Копируем данные
|
|||
|
|
for i in range(length):
|
|||
|
|
byte = read_byte(ctx)
|
|||
|
|
write_output_byte(ctx, byte)
|
|||
|
|
|
|||
|
|
# Проверка переполнения выходного буфера
|
|||
|
|
if ctx.output_position >= 0x8000:
|
|||
|
|
flush_output_buffer(ctx)
|
|||
|
|
|
|||
|
|
return 0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Детали
|
|||
|
|
|
|||
|
|
- Данные копируются "как есть"
|
|||
|
|
- Используется для несжимаемых данных
|
|||
|
|
- Требует выравнивания по байтам перед чтением длины
|
|||
|
|
|
|||
|
|
## Режим 1: Фиксированные Huffman коды
|
|||
|
|
|
|||
|
|
Использует предопределенные Huffman таблицы.
|
|||
|
|
|
|||
|
|
### Фиксированные таблицы длин кодов
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Таблица для литералов/длин (288 символов)
|
|||
|
|
FIXED_LITERAL_LENGTHS = [
|
|||
|
|
8, 8, 8, 8, ..., 8, # 0-143: коды длины 8 (144 символа)
|
|||
|
|
9, 9, 9, 9, ..., 9, # 144-255: коды длины 9 (112 символов)
|
|||
|
|
7, 7, 7, 7, ..., 7, # 256-279: коды длины 7 (24 символа)
|
|||
|
|
8, 8, 8, 8, ..., 8 # 280-287: коды длины 8 (8 символов)
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# Таблица для расстояний (30 символов)
|
|||
|
|
FIXED_DISTANCE_LENGTHS = [
|
|||
|
|
5, 5, 5, 5, ..., 5 # 0-29: все коды длины 5
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Алгоритм декодирования
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def decode_fixed_huffman_block(ctx):
|
|||
|
|
"""Декодирование блока с фиксированными Huffman кодами"""
|
|||
|
|
|
|||
|
|
# Инициализация фиксированных таблиц
|
|||
|
|
lit_tree = build_huffman_tree(FIXED_LITERAL_LENGTHS)
|
|||
|
|
dist_tree = build_huffman_tree(FIXED_DISTANCE_LENGTHS)
|
|||
|
|
|
|||
|
|
while True:
|
|||
|
|
# Декодировать символ литерала/длины
|
|||
|
|
symbol = decode_huffman_symbol(ctx, lit_tree)
|
|||
|
|
|
|||
|
|
if symbol < 256:
|
|||
|
|
# Литерал - просто вывести байт
|
|||
|
|
write_output_byte(ctx, symbol)
|
|||
|
|
|
|||
|
|
elif symbol == 256:
|
|||
|
|
# Конец блока
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
else:
|
|||
|
|
# Символ длины (257-285)
|
|||
|
|
length = decode_length(ctx, symbol)
|
|||
|
|
|
|||
|
|
# Декодировать расстояние
|
|||
|
|
dist_symbol = decode_huffman_symbol(ctx, dist_tree)
|
|||
|
|
distance = decode_distance(ctx, dist_symbol)
|
|||
|
|
|
|||
|
|
# Скопировать из истории
|
|||
|
|
copy_from_history(ctx, distance, length)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Таблицы экстра-бит
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Дополнительные биты для длины
|
|||
|
|
LENGTH_EXTRA_BITS = {
|
|||
|
|
257: 0, 258: 0, 259: 0, 260: 0, 261: 0, 262: 0, 263: 0, 264: 0, # 3-10
|
|||
|
|
265: 1, 266: 1, 267: 1, 268: 1, # 11-18
|
|||
|
|
269: 2, 270: 2, 271: 2, 272: 2, # 19-34
|
|||
|
|
273: 3, 274: 3, 275: 3, 276: 3, # 35-66
|
|||
|
|
277: 4, 278: 4, 279: 4, 280: 4, # 67-130
|
|||
|
|
281: 5, 282: 5, 283: 5, 284: 5, # 131-257
|
|||
|
|
285: 0 # 258
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
LENGTH_BASE = {
|
|||
|
|
257: 3, 258: 4, 259: 5, ..., 285: 258
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Дополнительные биты для расстояния
|
|||
|
|
DISTANCE_EXTRA_BITS = {
|
|||
|
|
0: 0, 1: 0, 2: 0, 3: 0, # 1-4
|
|||
|
|
4: 1, 5: 1, 6: 2, 7: 2, # 5-12
|
|||
|
|
8: 3, 9: 3, 10: 4, 11: 4, # 13-48
|
|||
|
|
12: 5, 13: 5, 14: 6, 15: 6, # 49-192
|
|||
|
|
16: 7, 17: 7, 18: 8, 19: 8, # 193-768
|
|||
|
|
20: 9, 21: 9, 22: 10, 23: 10, # 769-3072
|
|||
|
|
24: 11, 25: 11, 26: 12, 27: 12, # 3073-12288
|
|||
|
|
28: 13, 29: 13 # 12289-24576
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
DISTANCE_BASE = {
|
|||
|
|
0: 1, 1: 2, 2: 3, 3: 4, ..., 29: 24577
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Декодирование длины и расстояния
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def decode_length(ctx, symbol):
|
|||
|
|
"""Декодировать длину из символа"""
|
|||
|
|
base = LENGTH_BASE[symbol]
|
|||
|
|
extra_bits = LENGTH_EXTRA_BITS[symbol]
|
|||
|
|
|
|||
|
|
if extra_bits > 0:
|
|||
|
|
extra = read_bits(ctx, extra_bits)
|
|||
|
|
return base + extra
|
|||
|
|
|
|||
|
|
return base
|
|||
|
|
|
|||
|
|
|
|||
|
|
def decode_distance(ctx, symbol):
|
|||
|
|
"""Декодировать расстояние из символа"""
|
|||
|
|
base = DISTANCE_BASE[symbol]
|
|||
|
|
extra_bits = DISTANCE_EXTRA_BITS[symbol]
|
|||
|
|
|
|||
|
|
if extra_bits > 0:
|
|||
|
|
extra = read_bits(ctx, extra_bits)
|
|||
|
|
return base + extra
|
|||
|
|
|
|||
|
|
return base
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Режим 2: Динамические Huffman коды
|
|||
|
|
|
|||
|
|
Самый сложный режим. Huffman таблицы передаются в начале блока.
|
|||
|
|
|
|||
|
|
### Формат заголовка динамического блока
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Биты заголовка:
|
|||
|
|
[HLIT:5] - Количество литерал/длина кодов - 257 (значение: 257-286)
|
|||
|
|
[HDIST:5] - Количество расстояние кодов - 1 (значение: 1-30)
|
|||
|
|
[HCLEN:4] - Количество длин кодов для code length алфавита - 4 (значение: 4-19)
|
|||
|
|
|
|||
|
|
Далее идут длины кодов для code length алфавита:
|
|||
|
|
[CL0:3] [CL1:3] ... [CL(HCLEN-1):3]
|
|||
|
|
|
|||
|
|
Порядок code length кодов:
|
|||
|
|
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Алгоритм декодирования
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def decode_dynamic_huffman_block(ctx):
|
|||
|
|
"""Декодирование блока с динамическими Huffman кодами"""
|
|||
|
|
|
|||
|
|
# 1. Читаем заголовок
|
|||
|
|
hlit = read_bits(ctx, 5) + 257 # Количество литерал/длина кодов
|
|||
|
|
hdist = read_bits(ctx, 5) + 1 # Количество расстояние кодов
|
|||
|
|
hclen = read_bits(ctx, 4) + 4 # Количество code length кодов
|
|||
|
|
|
|||
|
|
if hlit > 286 or hdist > 30:
|
|||
|
|
return 1 # Ошибка
|
|||
|
|
|
|||
|
|
# 2. Читаем длины для code length алфавита
|
|||
|
|
CODE_LENGTH_ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5,
|
|||
|
|
11, 4, 12, 3, 13, 2, 14, 1, 15]
|
|||
|
|
|
|||
|
|
code_length_lengths = [0] * 19
|
|||
|
|
for i in range(hclen):
|
|||
|
|
code_length_lengths[CODE_LENGTH_ORDER[i]] = read_bits(ctx, 3)
|
|||
|
|
|
|||
|
|
# 3. Строим дерево для code length
|
|||
|
|
cl_tree = build_huffman_tree(code_length_lengths)
|
|||
|
|
|
|||
|
|
# 4. Декодируем длины литерал/длина и расстояние кодов
|
|||
|
|
lengths = decode_code_lengths(ctx, cl_tree, hlit + hdist)
|
|||
|
|
|
|||
|
|
# 5. Разделяем на два алфавита
|
|||
|
|
literal_lengths = lengths[:hlit]
|
|||
|
|
distance_lengths = lengths[hlit:]
|
|||
|
|
|
|||
|
|
# 6. Строим деревья для декодирования
|
|||
|
|
lit_tree = build_huffman_tree(literal_lengths)
|
|||
|
|
dist_tree = build_huffman_tree(distance_lengths)
|
|||
|
|
|
|||
|
|
# 7. Декодируем данные (аналогично фиксированному режиму)
|
|||
|
|
return decode_huffman_data(ctx, lit_tree, dist_tree)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Декодирование длин кодов
|
|||
|
|
|
|||
|
|
Используется специальный алфавит с тремя специальными символами:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def decode_code_lengths(ctx, cl_tree, total_count):
|
|||
|
|
"""
|
|||
|
|
Декодирование последовательности длин кодов
|
|||
|
|
|
|||
|
|
Специальные символы:
|
|||
|
|
16 - Повторить предыдущую длину 3-6 раз (2 доп. бита)
|
|||
|
|
17 - Повторить 0 длину 3-10 раз (3 доп. бита)
|
|||
|
|
18 - Повторить 0 длину 11-138 раз (7 доп. бит)
|
|||
|
|
"""
|
|||
|
|
lengths = []
|
|||
|
|
last_length = 0
|
|||
|
|
|
|||
|
|
while len(lengths) < total_count:
|
|||
|
|
symbol = decode_huffman_symbol(ctx, cl_tree)
|
|||
|
|
|
|||
|
|
if symbol < 16:
|
|||
|
|
# Обычная длина (0-15)
|
|||
|
|
lengths.append(symbol)
|
|||
|
|
last_length = symbol
|
|||
|
|
|
|||
|
|
elif symbol == 16:
|
|||
|
|
# Повторить предыдущую длину
|
|||
|
|
repeat = read_bits(ctx, 2) + 3
|
|||
|
|
lengths.extend([last_length] * repeat)
|
|||
|
|
|
|||
|
|
elif symbol == 17:
|
|||
|
|
# Повторить ноль (короткий)
|
|||
|
|
repeat = read_bits(ctx, 3) + 3
|
|||
|
|
lengths.extend([0] * repeat)
|
|||
|
|
last_length = 0
|
|||
|
|
|
|||
|
|
elif symbol == 18:
|
|||
|
|
# Повторить ноль (длинный)
|
|||
|
|
repeat = read_bits(ctx, 7) + 11
|
|||
|
|
lengths.extend([0] * repeat)
|
|||
|
|
last_length = 0
|
|||
|
|
|
|||
|
|
return lengths[:total_count]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Построение Huffman дерева
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def build_huffman_tree(code_lengths):
|
|||
|
|
"""
|
|||
|
|
Построить Huffman дерево из длин кодов
|
|||
|
|
|
|||
|
|
Использует алгоритм "canonical Huffman codes"
|
|||
|
|
"""
|
|||
|
|
max_length = max(code_lengths) if code_lengths else 0
|
|||
|
|
|
|||
|
|
# 1. Подсчитать количество кодов каждой длины
|
|||
|
|
bl_count = [0] * (max_length + 1)
|
|||
|
|
for length in code_lengths:
|
|||
|
|
if length > 0:
|
|||
|
|
bl_count[length] += 1
|
|||
|
|
|
|||
|
|
# 2. Вычислить первый код для каждой длины
|
|||
|
|
code = 0
|
|||
|
|
next_code = [0] * (max_length + 1)
|
|||
|
|
|
|||
|
|
for bits in range(1, max_length + 1):
|
|||
|
|
code = (code + bl_count[bits - 1]) << 1
|
|||
|
|
next_code[bits] = code
|
|||
|
|
|
|||
|
|
# 3. Присвоить числовые коды символам
|
|||
|
|
tree = {}
|
|||
|
|
for symbol, length in enumerate(code_lengths):
|
|||
|
|
if length > 0:
|
|||
|
|
tree[symbol] = {
|
|||
|
|
'code': next_code[length],
|
|||
|
|
'length': length
|
|||
|
|
}
|
|||
|
|
next_code[length] += 1
|
|||
|
|
|
|||
|
|
# 4. Создать структуру быстрого поиска
|
|||
|
|
lookup_table = create_lookup_table(tree)
|
|||
|
|
|
|||
|
|
return lookup_table
|
|||
|
|
|
|||
|
|
|
|||
|
|
def decode_huffman_symbol(ctx, tree):
|
|||
|
|
"""Декодировать один символ из Huffman дерева"""
|
|||
|
|
code = 0
|
|||
|
|
length = 0
|
|||
|
|
|
|||
|
|
for length in range(1, 16):
|
|||
|
|
bit = read_bit(ctx)
|
|||
|
|
code = (code << 1) | bit
|
|||
|
|
|
|||
|
|
# Проверить в таблице быстрого поиска
|
|||
|
|
if (code, length) in tree:
|
|||
|
|
return tree[(code, length)]
|
|||
|
|
|
|||
|
|
return -1 # Ошибка декодирования
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Управление выходным буфером
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def write_output_byte(ctx, byte):
|
|||
|
|
"""Записать байт в выходной буфер"""
|
|||
|
|
# Записываем в bitBuffer (используется как циклический буфер)
|
|||
|
|
ctx.bitBuffer[ctx.decodedBytes] = byte
|
|||
|
|
ctx.decodedBytes += 1
|
|||
|
|
|
|||
|
|
# Если буфер заполнен (32KB)
|
|||
|
|
if ctx.decodedBytes >= 0x8000:
|
|||
|
|
flush_output_buffer(ctx)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def flush_output_buffer(ctx):
|
|||
|
|
"""Сбросить выходной буфер в финальный выход"""
|
|||
|
|
# Копируем данные в финальный выходной буфер
|
|||
|
|
dest_offset = ctx.outputPosition + ctx.destData
|
|||
|
|
memcpy(dest_offset, ctx.bitBuffer, ctx.decodedBytes)
|
|||
|
|
|
|||
|
|
# Обновляем счетчики
|
|||
|
|
ctx.outputPosition += ctx.decodedBytes
|
|||
|
|
ctx.decodedBytes = 0
|
|||
|
|
|
|||
|
|
|
|||
|
|
def copy_from_history(ctx, distance, length):
|
|||
|
|
"""Скопировать данные из истории (LZ77)"""
|
|||
|
|
# Позиция источника в циклическом буфере
|
|||
|
|
src_pos = (ctx.decodedBytes - distance) & 0x7FFF
|
|||
|
|
|
|||
|
|
for i in range(length):
|
|||
|
|
byte = ctx.bitBuffer[src_pos]
|
|||
|
|
write_output_byte(ctx, byte)
|
|||
|
|
src_pos = (src_pos + 1) & 0x7FFF
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Полная реализация на Python
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class HuffmanDecoder:
|
|||
|
|
"""Полный DEFLATE-подобный декодер"""
|
|||
|
|
|
|||
|
|
def __init__(self, input_data, output_size):
|
|||
|
|
self.input_data = input_data
|
|||
|
|
self.output_size = output_size
|
|||
|
|
self.input_pos = 0
|
|||
|
|
self.bit_buffer = 0
|
|||
|
|
self.bits_available = 0
|
|||
|
|
self.output = bytearray()
|
|||
|
|
self.history = bytearray(32768) # 32KB циклический буфер
|
|||
|
|
self.history_pos = 0
|
|||
|
|
|
|||
|
|
def read_bit(self):
|
|||
|
|
"""Прочитать один бит"""
|
|||
|
|
if self.bits_available == 0:
|
|||
|
|
if self.input_pos >= len(self.input_data):
|
|||
|
|
return 0
|
|||
|
|
self.bit_buffer = self.input_data[self.input_pos]
|
|||
|
|
self.input_pos += 1
|
|||
|
|
self.bits_available = 8
|
|||
|
|
|
|||
|
|
bit = self.bit_buffer & 1
|
|||
|
|
self.bit_buffer >>= 1
|
|||
|
|
self.bits_available -= 1
|
|||
|
|
return bit
|
|||
|
|
|
|||
|
|
def read_bits(self, count):
|
|||
|
|
"""Прочитать несколько бит (LSB first)"""
|
|||
|
|
result = 0
|
|||
|
|
for i in range(count):
|
|||
|
|
result |= self.read_bit() << i
|
|||
|
|
return result
|
|||
|
|
|
|||
|
|
def write_byte(self, byte):
|
|||
|
|
"""Записать байт в выход и историю"""
|
|||
|
|
self.output.append(byte)
|
|||
|
|
self.history[self.history_pos] = byte
|
|||
|
|
self.history_pos = (self.history_pos + 1) & 0x7FFF
|
|||
|
|
|
|||
|
|
def copy_from_history(self, distance, length):
|
|||
|
|
"""Скопировать из истории"""
|
|||
|
|
src_pos = (self.history_pos - distance) & 0x7FFF
|
|||
|
|
|
|||
|
|
for _ in range(length):
|
|||
|
|
byte = self.history[src_pos]
|
|||
|
|
self.write_byte(byte)
|
|||
|
|
src_pos = (src_pos + 1) & 0x7FFF
|
|||
|
|
|
|||
|
|
def decompress(self):
|
|||
|
|
"""Основной цикл декомпрессии"""
|
|||
|
|
while len(self.output) < self.output_size:
|
|||
|
|
# Читаем заголовок блока
|
|||
|
|
final = self.read_bit()
|
|||
|
|
block_type = self.read_bits(2)
|
|||
|
|
|
|||
|
|
if block_type == 0:
|
|||
|
|
# Несжатый блок
|
|||
|
|
if not self.decode_uncompressed_block():
|
|||
|
|
break
|
|||
|
|
elif block_type == 1:
|
|||
|
|
# Фиксированные Huffman коды
|
|||
|
|
if not self.decode_fixed_huffman_block():
|
|||
|
|
break
|
|||
|
|
elif block_type == 2:
|
|||
|
|
# Динамические Huffman коды
|
|||
|
|
if not self.decode_dynamic_huffman_block():
|
|||
|
|
break
|
|||
|
|
else:
|
|||
|
|
# Ошибка
|
|||
|
|
raise ValueError("Invalid block type")
|
|||
|
|
|
|||
|
|
if final:
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
return bytes(self.output[:self.output_size])
|
|||
|
|
|
|||
|
|
# ... реализации decode_*_block методов ...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Оптимизации
|
|||
|
|
|
|||
|
|
### 1. Таблица быстрого поиска
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Предвычисленная таблица для 9 бит (первый уровень)
|
|||
|
|
FAST_LOOKUP_BITS = 9
|
|||
|
|
fast_table = [None] * (1 << FAST_LOOKUP_BITS)
|
|||
|
|
|
|||
|
|
# Заполнение таблицы при построении дерева
|
|||
|
|
for symbol, info in tree.items():
|
|||
|
|
if info['length'] <= FAST_LOOKUP_BITS:
|
|||
|
|
# Все возможные префиксы для этого кода
|
|||
|
|
code = info['code']
|
|||
|
|
for i in range(1 << (FAST_LOOKUP_BITS - info['length'])):
|
|||
|
|
lookup_code = code | (i << info['length'])
|
|||
|
|
fast_table[lookup_code] = symbol
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. Буферизация битов
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Читать по 32 бита за раз вместо побитового чтения
|
|||
|
|
def refill_bits(self):
|
|||
|
|
"""Пополнить битовый буфер"""
|
|||
|
|
while self.bits_available < 24 and self.input_pos < len(self.input_data):
|
|||
|
|
byte = self.input_data[self.input_pos]
|
|||
|
|
self.input_pos += 1
|
|||
|
|
self.bit_buffer |= byte << self.bits_available
|
|||
|
|
self.bits_available += 8
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Отладка и тестирование
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def debug_huffman_decode(data):
|
|||
|
|
"""Декодирование с отладочной информацией"""
|
|||
|
|
decoder = HuffmanDecoder(data, len(data) * 10)
|
|||
|
|
|
|||
|
|
original_read_bits = decoder.read_bits
|
|||
|
|
def debug_read_bits(count):
|
|||
|
|
result = original_read_bits(count)
|
|||
|
|
print(f"Read {count} bits: 0x{result:0{count//4}X} ({result})")
|
|||
|
|
return result
|
|||
|
|
|
|||
|
|
decoder.read_bits = debug_read_bits
|
|||
|
|
return decoder.decompress()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Заключение
|
|||
|
|
|
|||
|
|
Этот Huffman декодер реализует **DEFLATE**-совместимый алгоритм с тремя режимами блоков:
|
|||
|
|
|
|||
|
|
1. **Несжатый** - для несжимаемых данных
|
|||
|
|
2. **Фиксированный Huffman** - быстрое декодирование с предопределенными таблицами
|
|||
|
|
3. **Динамический Huffman** - максимальное сжатие с пользовательскими таблицами
|
|||
|
|
|
|||
|
|
**Ключевые особенности:**
|
|||
|
|
|
|||
|
|
- Поддержка LZ77 для повторяющихся последовательностей
|
|||
|
|
- Канонические Huffman коды для эффективного декодирования
|
|||
|
|
- Циклический буфер 32KB для истории
|
|||
|
|
- Оптимизации через таблицы быстрого поиска
|
|||
|
|
|
|||
|
|
**Сложность:** O(n) где n - размер выходных данных
|