use crate::{Error, Result};
const HUFF_ITEM_COUNT: usize = 0x203; const LINK_ITEM_COUNT: usize = 0x80; const HUFF_DECOMPRESS_ERROR: u32 = 0x1FF;
const BYTE_TO_WEIGHT_00: [u8; 258] = [
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00,
];
const BYTE_TO_WEIGHT_01: [u8; 258] = [
0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05,
0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02,
0x0D, 0x07, 0x09, 0x06, 0x06, 0x04, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
0x09, 0x06, 0x04, 0x04, 0x04, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04,
0x08, 0x03, 0x04, 0x07, 0x09, 0x05, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02,
0x06, 0x0A, 0x08, 0x08, 0x06, 0x07, 0x04, 0x03, 0x04, 0x04, 0x02, 0x02, 0x04, 0x02, 0x03, 0x03,
0x04, 0x03, 0x07, 0x07, 0x09, 0x06, 0x04, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02,
0x0A, 0x02, 0x02, 0x03, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x03, 0x05, 0x02, 0x03,
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x01, 0x01,
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x04, 0x04, 0x04, 0x07, 0x09, 0x08, 0x0C, 0x02,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03,
0x04, 0x01, 0x02, 0x04, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B,
0x00, 0x00,
];
const BYTE_TO_WEIGHT_02: [u8; 258] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04,
0x06, 0x08, 0x05, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x01, 0x01, 0x02, 0x01, 0x01,
0x01, 0x04, 0x02, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x04, 0x01, 0x01, 0x02, 0x03, 0x03, 0x02,
0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01,
0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A,
0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
const BYTE_TO_WEIGHT_03: [u8; 258] = [
0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03,
0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
0x0A, 0x04, 0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01,
0x05, 0x02, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03,
0x01, 0x03, 0x01, 0x01, 0x02, 0x05, 0x01, 0x01, 0x04, 0x03, 0x05, 0x01, 0x03, 0x01, 0x03, 0x03,
0x02, 0x01, 0x04, 0x03, 0x0A, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x02, 0x01, 0x0A, 0x02, 0x05, 0x01, 0x01, 0x02, 0x07, 0x02, 0x17, 0x01, 0x05, 0x01, 0x01,
0x0E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x06, 0x02, 0x01, 0x04, 0x05, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11,
0x00, 0x00,
];
const BYTE_TO_WEIGHT_04: [u8; 258] = [
0xFF, 0xFB, 0x98, 0x9A, 0x84, 0x85, 0x63, 0x64, 0x3E, 0x3E, 0x22, 0x22, 0x13, 0x13, 0x18, 0x17,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
const BYTE_TO_WEIGHT_05: [u8; 258] = [
0xFF, 0xF1, 0x9D, 0x9E, 0x9A, 0x9B, 0x9A, 0x97, 0x93, 0x93, 0x8C, 0x8E, 0x86, 0x88, 0x80, 0x82,
0x7C, 0x7C, 0x72, 0x73, 0x69, 0x6B, 0x5F, 0x60, 0x55, 0x56, 0x4A, 0x4B, 0x40, 0x41, 0x37, 0x37,
0x2F, 0x2F, 0x27, 0x27, 0x21, 0x21, 0x1B, 0x1C, 0x17, 0x17, 0x13, 0x13, 0x10, 0x10, 0x0D, 0x0D,
0x0B, 0x0B, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x19, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
const BYTE_TO_WEIGHT_06: [u8; 258] = [
0xC3, 0xCB, 0xF5, 0x41, 0xFF, 0x7B, 0xF7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xBF, 0xCC, 0xF2, 0x40, 0xFD, 0x7C, 0xF7, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7A, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
const BYTE_TO_WEIGHT_07: [u8; 258] = [
0xC3, 0xD9, 0xEF, 0x3D, 0xF9, 0x7C, 0xE9, 0x1E, 0xFD, 0xAB, 0xF1, 0x2C, 0xFC, 0x5B, 0xFE, 0x17,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xBD, 0xD9, 0xEC, 0x3D, 0xF5, 0x7D, 0xE8, 0x1D, 0xFB, 0xAE, 0xF0, 0x2C, 0xFB, 0x5C, 0xFF, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x70, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
const BYTE_TO_WEIGHT_08: [u8; 258] = [
0xBA, 0xC5, 0xDA, 0x33, 0xE3, 0x6D, 0xD8, 0x18, 0xE5, 0x94, 0xDA, 0x23, 0xDF, 0x4A, 0xD1, 0x10,
0xEE, 0xAF, 0xE4, 0x2C, 0xEA, 0x5A, 0xDE, 0x15, 0xF4, 0x87, 0xE9, 0x21, 0xF6, 0x43, 0xFC, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xB0, 0xC7, 0xD8, 0x33, 0xE3, 0x6B, 0xD6, 0x18, 0xE7, 0x95, 0xD8, 0x23, 0xDB, 0x49, 0xD0, 0x11,
0xE9, 0xB2, 0xE2, 0x2B, 0xE8, 0x5C, 0xDD, 0x15, 0xF1, 0x87, 0xE7, 0x20, 0xF7, 0x44, 0xFF, 0x13,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5F, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
const WEIGHT_TABLES: [&[u8; 258]; 9] = [
&BYTE_TO_WEIGHT_00,
&BYTE_TO_WEIGHT_01,
&BYTE_TO_WEIGHT_02,
&BYTE_TO_WEIGHT_03,
&BYTE_TO_WEIGHT_04,
&BYTE_TO_WEIGHT_05,
&BYTE_TO_WEIGHT_06,
&BYTE_TO_WEIGHT_07,
&BYTE_TO_WEIGHT_08,
];
struct BitReader<'a> {
data: &'a [u8],
position: usize,
bit_buffer: u32,
bit_count: u32,
}
impl<'a> BitReader<'a> {
fn new(data: &'a [u8]) -> Self {
Self {
data,
position: 0,
bit_buffer: 0,
bit_count: 0,
}
}
fn get_bit(&mut self) -> Result<u32> {
if self.bit_count == 0 {
if self.position >= self.data.len() {
return Err(Error::compression("Unexpected end of Huffman data"));
}
self.bit_buffer = self.data[self.position] as u32;
self.position += 1;
self.bit_count = 8;
}
let bit = self.bit_buffer & 1;
self.bit_buffer >>= 1;
self.bit_count -= 1;
Ok(bit)
}
fn get_8_bits(&mut self) -> Result<u32> {
if self.position >= self.data.len() {
return Err(Error::compression("Unexpected end of Huffman data"));
}
let byte = self.data[self.position] as u32;
self.position += 1;
Ok(byte)
}
fn peek_7_bits(&mut self) -> Result<u32> {
while self.bit_count < 7 {
if self.position >= self.data.len() {
return Err(Error::compression("Unexpected end of Huffman data"));
}
self.bit_buffer |= (self.data[self.position] as u32) << self.bit_count;
self.position += 1;
self.bit_count += 8;
}
Ok(self.bit_buffer & 0x7F) }
fn skip_bits(&mut self, count: u32) {
if count <= self.bit_count {
self.bit_buffer >>= count;
self.bit_count -= count;
} else {
let remaining = count - self.bit_count;
self.bit_buffer = 0;
self.bit_count = 0;
self.position += (remaining / 8) as usize;
if !remaining.is_multiple_of(8) && self.peek_7_bits().is_ok() {
self.skip_bits(remaining % 8);
}
}
}
}
#[derive(Debug, Clone)]
struct HuffmanItem {
next: Option<usize>, prev: Option<usize>,
decompressed_value: u32, weight: u32, parent: Option<usize>, child_lo: Option<usize>,
}
impl HuffmanItem {
fn new(value: u32, weight: u32) -> Self {
Self {
next: None,
prev: None,
decompressed_value: value,
weight,
parent: None,
child_lo: None,
}
}
}
#[derive(Debug, Clone)]
struct QuickLink {
valid_value: u32,
valid_bits: u32,
decompressed_value: u32,
item_index: Option<usize>,
}
impl QuickLink {
fn new() -> Self {
Self {
valid_value: 0,
valid_bits: 0,
decompressed_value: 0,
item_index: None,
}
}
}
struct HuffmanTree {
items: Vec<HuffmanItem>,
items_by_byte: [Option<usize>; 0x102],
quick_links: [QuickLink; LINK_ITEM_COUNT],
first: Option<usize>, last: Option<usize>, min_valid_value: u32,
}
const LIST_HEAD: Option<usize> = None;
impl HuffmanTree {
fn new() -> Self {
Self {
items: Vec::with_capacity(HUFF_ITEM_COUNT),
items_by_byte: [None; 0x102],
quick_links: std::array::from_fn(|_| QuickLink::new()),
first: LIST_HEAD,
last: LIST_HEAD,
min_valid_value: 1,
}
}
fn link_two_items(&mut self, item1_idx: usize, item2_idx: usize) {
self.items[item2_idx].next = self.items[item1_idx].next;
if let Some(next_idx) = self.items[item1_idx].next {
self.items[item2_idx].prev = self.items[next_idx].prev;
self.items[next_idx].prev = Some(item2_idx);
} else {
self.items[item2_idx].prev = Some(item1_idx);
}
self.items[item1_idx].next = Some(item2_idx);
}
fn insert_after(&mut self, new_item_idx: usize, after_idx: Option<usize>) {
if let Some(idx) = after_idx {
self.link_two_items(idx, new_item_idx);
} else {
self.items[new_item_idx].prev = None;
self.items[new_item_idx].next = self.first;
if let Some(first_idx) = self.first {
self.items[first_idx].prev = Some(new_item_idx);
}
self.first = Some(new_item_idx);
if self.last.is_none() {
self.last = Some(new_item_idx);
}
}
}
fn insert_before(&mut self, new_item_idx: usize, before_idx: Option<usize>) {
if let Some(idx) = before_idx {
let prev_idx = self.items[idx].prev;
self.items[new_item_idx].next = Some(idx);
self.items[new_item_idx].prev = prev_idx;
self.items[idx].prev = Some(new_item_idx);
if let Some(prev) = prev_idx {
self.items[prev].next = Some(new_item_idx);
} else {
self.first = Some(new_item_idx);
}
} else {
self.items[new_item_idx].next = None;
self.items[new_item_idx].prev = self.last;
if let Some(last_idx) = self.last {
self.items[last_idx].next = Some(new_item_idx);
}
self.last = Some(new_item_idx);
if self.first.is_none() {
self.first = Some(new_item_idx);
}
}
}
fn create_new_item(
&mut self,
decompressed_value: u32,
weight: u32,
insert_after: bool,
) -> usize {
let new_idx = self.items.len();
self.items
.push(HuffmanItem::new(decompressed_value, weight));
if self.first.is_none() {
self.first = Some(new_idx);
self.last = Some(new_idx);
} else if insert_after {
self.insert_after(new_idx, self.last);
} else {
self.insert_before(new_idx, self.first);
}
new_idx
}
fn find_higher_or_equal_item(&self, start_idx: Option<usize>, weight: u32) -> Option<usize> {
let mut current = start_idx;
while let Some(idx) = current {
if self.items[idx].weight >= weight {
return Some(idx);
}
current = self.items[idx].prev;
}
None
}
fn fixup_item_pos_by_weight(&mut self, item_idx: usize, mut max_weight: u32) -> u32 {
let item_weight = self.items[item_idx].weight;
if item_weight < max_weight {
if let Some(higher_idx) = self.find_higher_or_equal_item(self.last, item_weight) {
self.remove_item_from_list(item_idx);
self.insert_after(item_idx, Some(higher_idx));
}
} else {
max_weight = item_weight;
}
max_weight
}
fn remove_item_from_list(&mut self, item_idx: usize) {
let prev = self.items[item_idx].prev;
let next = self.items[item_idx].next;
if let Some(prev_idx) = prev {
self.items[prev_idx].next = next;
} else {
self.first = next;
}
if let Some(next_idx) = next {
self.items[next_idx].prev = prev;
} else {
self.last = prev;
}
self.items[item_idx].prev = None;
self.items[item_idx].next = None;
}
fn build_tree(&mut self, compression_type: u32) -> Result<()> {
self.items.clear();
self.items_by_byte = [None; 0x102];
self.first = LIST_HEAD;
self.last = LIST_HEAD;
let comp_type = (compression_type & 0x0F) as usize;
if comp_type >= WEIGHT_TABLES.len() {
return Err(Error::compression("Invalid Huffman compression type"));
}
let weight_table = WEIGHT_TABLES[comp_type];
for (i, &weight) in weight_table.iter().enumerate().take(0x100) {
if weight != 0 {
let item_idx = self.create_new_item(i as u32, weight as u32, true);
self.items_by_byte[i] = Some(item_idx);
}
}
let end_idx = self.create_new_item(0x100, 1, true);
self.items_by_byte[0x100] = Some(end_idx);
let eof_idx = self.create_new_item(0x101, 1, true);
self.items_by_byte[0x101] = Some(eof_idx);
let mut sorted_items: Vec<(usize, u32)> = Vec::new();
let mut curr = self.first;
while let Some(idx) = curr {
sorted_items.push((idx, self.items[idx].weight));
curr = self.items[idx].next;
}
sorted_items.sort_by_key(|&(_, weight)| std::cmp::Reverse(weight));
self.first = None;
self.last = None;
for &(idx, _) in &sorted_items {
self.items[idx].prev = None;
self.items[idx].next = None;
if self.first.is_none() {
self.first = Some(idx);
self.last = Some(idx);
} else {
self.items[idx].prev = self.last;
if let Some(last_idx) = self.last {
self.items[last_idx].next = Some(idx);
}
self.last = Some(idx);
}
}
log::debug!("Built initial list with {} leaf nodes", self.items.len());
if log::log_enabled!(log::Level::Trace) {
log::trace!("Initial linked list (first -> last):");
let mut curr = self.first;
let mut count = 0;
while let Some(idx) = curr {
if count < 5 || count >= self.items.len() - 5 {
log::trace!(
" [{}] idx={}, value=0x{:02X}, weight={}",
count,
idx,
self.items[idx].decompressed_value,
self.items[idx].weight
);
} else if count == 5 {
log::trace!(" ...");
}
curr = self.items[idx].next;
count += 1;
}
}
let mut child_lo = self.last;
let mut internal_nodes = 0;
let mut max_weight = if let Some(first_idx) = self.first {
self.items[first_idx].weight
} else {
0
};
while let Some(lo_idx) = child_lo {
let child_hi = self.items[lo_idx].prev;
if child_hi.is_none() {
log::debug!(
"Tree building stopped: no more pairs (created {internal_nodes} internal nodes)"
);
break; }
let hi_idx = child_hi.unwrap();
log::trace!(
"Building parent for children: lo={} (w={}), hi={} (w={})",
lo_idx,
self.items[lo_idx].weight,
hi_idx,
self.items[hi_idx].weight
);
let parent_weight = self.items[hi_idx].weight + self.items[lo_idx].weight;
let parent_idx = self.create_new_item(0xFFFFFFFFu32, parent_weight, true);
internal_nodes += 1;
self.items[lo_idx].parent = Some(parent_idx);
self.items[hi_idx].parent = Some(parent_idx);
self.items[parent_idx].child_lo = Some(lo_idx);
let next_child = self.items[hi_idx].prev;
max_weight = self.fixup_item_pos_by_weight(parent_idx, max_weight);
child_lo = next_child;
log::trace!("Next iteration will start from: {child_lo:?}");
}
log::debug!(
"Tree building complete: {} total items ({} leaf + {} internal)",
self.items.len(),
self.items.len() - internal_nodes,
internal_nodes
);
self.min_valid_value = 1;
Ok(())
}
fn decode_one_byte(&mut self, reader: &mut BitReader<'_>) -> Result<u32> {
let item_link_index = reader.peek_7_bits().unwrap_or(0) as usize;
let has_item_link = item_link_index < LINK_ITEM_COUNT;
let mut item_idx = if has_item_link
&& self.quick_links[item_link_index].valid_value >= self.min_valid_value
{
if self.quick_links[item_link_index].valid_bits <= 7 {
reader.skip_bits(self.quick_links[item_link_index].valid_bits);
return Ok(self.quick_links[item_link_index].decompressed_value);
}
reader.skip_bits(7);
self.quick_links[item_link_index].item_index
} else {
self.first
};
if item_idx.is_none() {
return Err(Error::compression("Empty Huffman tree"));
}
let mut item_link: Option<usize> = None;
let mut bit_count = 0;
while let Some(idx) = item_idx {
let item = &self.items[idx];
if item.child_lo.is_none() {
break;
}
let bit_value = reader.get_bit()?;
bit_count += 1;
let child_lo_idx = item.child_lo.unwrap();
item_idx = if bit_value != 0 {
self.items[child_lo_idx].prev
} else {
Some(child_lo_idx)
};
if bit_count == 7 {
item_link = item_idx;
}
}
let final_idx =
item_idx.ok_or_else(|| Error::compression("Invalid Huffman tree traversal"))?;
let decompressed_value = self.items[final_idx].decompressed_value;
if has_item_link && self.quick_links[item_link_index].valid_value < self.min_valid_value {
if bit_count > 7 {
self.quick_links[item_link_index].valid_value = self.min_valid_value;
self.quick_links[item_link_index].valid_bits = bit_count;
self.quick_links[item_link_index].item_index = item_link;
} else if bit_count > 0 {
let mut index = item_link_index;
if bit_count < 7 {
index &= (0xFFFFFFFFu32 >> (32 - bit_count)) as usize;
}
while index < LINK_ITEM_COUNT {
self.quick_links[index].valid_value = self.min_valid_value;
self.quick_links[index].valid_bits = bit_count;
self.quick_links[index].decompressed_value = decompressed_value;
index += 1 << bit_count;
}
}
}
Ok(decompressed_value)
}
}
pub(crate) fn decompress(data: &[u8], expected_size: usize) -> Result<Vec<u8>> {
if data.is_empty() {
return Ok(Vec::new());
}
log::debug!(
"Huffman decompress input: {} bytes, expected: {}, first 16 bytes: {:02X?}",
data.len(),
expected_size,
&data[..std::cmp::min(16, data.len())]
);
let mut reader = BitReader::new(data);
let mut output = Vec::with_capacity(expected_size);
let compression_type = reader.get_8_bits()?;
log::debug!("Huffman compression type: 0x{compression_type:02X}");
let mut tree = HuffmanTree::new();
tree.build_tree(compression_type)?;
log::debug!("Huffman tree built with {} items", tree.items.len());
let mut decoded_count = 0;
loop {
let decoded_value = tree.decode_one_byte(&mut reader)?;
decoded_count += 1;
log::trace!("Huffman decoded byte {decoded_count}: value=0x{decoded_value:03X}");
if decoded_value == 0x100 {
log::debug!("Huffman hit end marker after {decoded_count} symbols");
break;
}
if decoded_value == HUFF_DECOMPRESS_ERROR {
return Err(Error::compression("Huffman decompression error"));
}
if decoded_value == 0x101 {
let next_byte = reader.get_8_bits()?;
output.push(next_byte as u8);
continue;
}
if decoded_value > 255 {
return Err(Error::compression("Invalid Huffman decoded value"));
}
output.push(decoded_value as u8);
if output.len() >= expected_size {
break;
}
}
log::debug!(
"Huffman decompress output: {} bytes, first 16 bytes: {:02X?}",
output.len(),
&output[..std::cmp::min(16, output.len())]
);
Ok(output)
}
pub(crate) fn compress(_data: &[u8]) -> Result<Vec<u8>> {
Err(Error::compression(
"Huffman compression not implemented - only decompression is available",
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_huffman_empty_data() {
assert!(decompress(&[], 0).is_ok());
}
#[test]
fn test_bit_reader() {
let data = vec![0b10101100, 0b11110000];
let mut reader = BitReader::new(&data);
assert_eq!(reader.get_bit().unwrap(), 0); assert_eq!(reader.get_bit().unwrap(), 0); assert_eq!(reader.get_bit().unwrap(), 1); assert_eq!(reader.get_bit().unwrap(), 1); assert_eq!(reader.get_bit().unwrap(), 0); assert_eq!(reader.get_bit().unwrap(), 1); assert_eq!(reader.get_bit().unwrap(), 0); assert_eq!(reader.get_bit().unwrap(), 1); }
}