use crate::huffman::HuffmanTree;
use oxiarc_core::error::Result;
use std::sync::OnceLock;
pub fn fixed_litlen_lengths() -> [u8; 288] {
let mut lengths = [0u8; 288];
for len in lengths.iter_mut().take(144) {
*len = 8;
}
for len in lengths.iter_mut().take(256).skip(144) {
*len = 9;
}
for len in lengths.iter_mut().take(280).skip(256) {
*len = 7;
}
for len in lengths.iter_mut().take(288).skip(280) {
*len = 8;
}
lengths
}
pub fn fixed_distance_lengths() -> [u8; 30] {
[5u8; 30]
}
pub fn fixed_litlen_tree() -> Result<&'static HuffmanTree> {
static TREE: OnceLock<HuffmanTree> = OnceLock::new();
Ok(TREE.get_or_init(|| {
HuffmanTree::from_code_lengths(&fixed_litlen_lengths())
.expect("Fixed litlen tree construction should never fail")
}))
}
pub fn fixed_distance_tree() -> Result<&'static HuffmanTree> {
static TREE: OnceLock<HuffmanTree> = OnceLock::new();
Ok(TREE.get_or_init(|| {
HuffmanTree::from_code_lengths(&fixed_distance_lengths())
.expect("Fixed distance tree construction should never fail")
}))
}
pub const LENGTH_BASE: [u16; 29] = [
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, ];
pub const LENGTH_EXTRA_BITS: [u8; 29] = [
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, ];
pub const DISTANCE_BASE: [u16; 30] = [
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, ];
pub const DISTANCE_EXTRA_BITS: [u8; 30] = [
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, ];
pub const CODE_LENGTH_ORDER: [usize; 19] = [
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
];
pub fn length_to_code(length: u16) -> (u16, u8, u16) {
debug_assert!(
(3..=258).contains(&length),
"Length out of range: {}",
length
);
let length = length as usize;
let code = match length {
3..=10 => length - 3 + 257,
11..=18 => (length - 11) / 2 + 265,
19..=34 => (length - 19) / 4 + 269,
35..=66 => (length - 35) / 8 + 273,
67..=130 => (length - 67) / 16 + 277,
131..=257 => (length - 131) / 32 + 281,
258 => 285,
_ => unreachable!(),
};
let base = LENGTH_BASE[code - 257] as usize;
let extra_bits = LENGTH_EXTRA_BITS[code - 257];
let extra_value = (length - base) as u16;
(code as u16, extra_bits, extra_value)
}
pub fn distance_to_code(distance: u16) -> (u16, u8, u16) {
debug_assert!(
(1..=32768).contains(&distance),
"Distance out of range: {}",
distance
);
let code = match distance {
1 => 0,
2 => 1,
3 => 2,
4 => 3,
5..=6 => 4,
7..=8 => 5,
9..=12 => 6,
13..=16 => 7,
17..=24 => 8,
25..=32 => 9,
33..=48 => 10,
49..=64 => 11,
65..=96 => 12,
97..=128 => 13,
129..=192 => 14,
193..=256 => 15,
257..=384 => 16,
385..=512 => 17,
513..=768 => 18,
769..=1024 => 19,
1025..=1536 => 20,
1537..=2048 => 21,
2049..=3072 => 22,
3073..=4096 => 23,
4097..=6144 => 24,
6145..=8192 => 25,
8193..=12288 => 26,
12289..=16384 => 27,
16385..=24576 => 28,
_ => 29, };
let base = DISTANCE_BASE[code];
let extra_bits = DISTANCE_EXTRA_BITS[code];
let extra_value = distance - base;
(code as u16, extra_bits, extra_value)
}
pub fn decode_length(code: u16, extra: u16) -> u16 {
debug_assert!((257..=285).contains(&code), "Invalid length code: {}", code);
LENGTH_BASE[(code - 257) as usize] + extra
}
pub fn decode_distance(code: u16, extra: u16) -> u16 {
debug_assert!(code < 30, "Invalid distance code: {}", code);
DISTANCE_BASE[code as usize] + extra
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fixed_litlen_lengths() {
let lengths = fixed_litlen_lengths();
assert_eq!(lengths[0], 8);
assert_eq!(lengths[143], 8);
assert_eq!(lengths[144], 9);
assert_eq!(lengths[255], 9);
assert_eq!(lengths[256], 7); assert_eq!(lengths[279], 7);
assert_eq!(lengths[280], 8);
assert_eq!(lengths[287], 8);
}
#[test]
fn test_fixed_distance_lengths() {
let lengths = fixed_distance_lengths();
assert!(lengths.iter().all(|&l| l == 5));
}
#[test]
fn test_fixed_trees() {
let _ = fixed_litlen_tree().expect("fixed litlen tree should be valid");
let _ = fixed_distance_tree().expect("fixed distance tree should be valid");
}
#[test]
fn test_length_to_code_roundtrip() {
for length in 3..=258 {
let (code, extra_bits, extra_value) = length_to_code(length);
let decoded = decode_length(code, extra_value);
assert_eq!(
decoded, length,
"Roundtrip failed for length {}: code={}, extra_bits={}, extra_value={}",
length, code, extra_bits, extra_value
);
}
}
#[test]
fn test_distance_to_code_roundtrip() {
for distance in 1..=32768u16 {
let (code, extra_bits, extra_value) = distance_to_code(distance);
let decoded = decode_distance(code, extra_value);
assert_eq!(
decoded, distance,
"Roundtrip failed for distance {}: code={}, extra_bits={}, extra_value={}",
distance, code, extra_bits, extra_value
);
}
}
#[test]
fn test_specific_lengths() {
assert_eq!(length_to_code(3), (257, 0, 0));
assert_eq!(length_to_code(10), (264, 0, 0));
assert_eq!(length_to_code(11), (265, 1, 0));
assert_eq!(length_to_code(12), (265, 1, 1));
assert_eq!(length_to_code(258), (285, 0, 0));
}
#[test]
fn test_specific_distances() {
assert_eq!(distance_to_code(1), (0, 0, 0));
assert_eq!(distance_to_code(4), (3, 0, 0));
assert_eq!(distance_to_code(5), (4, 1, 0));
assert_eq!(distance_to_code(6), (4, 1, 1));
assert_eq!(distance_to_code(32768), (29, 13, 8191));
}
}