pub const LENGTH_TABLE: [(u16, u8); 29] = [
(3, 0), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0), (9, 0), (10, 0), (11, 1), (13, 1), (15, 1), (17, 1), (19, 2), (23, 2), (27, 2), (31, 2), (35, 3), (43, 3), (51, 3), (59, 3), (67, 4), (83, 4), (99, 4), (115, 4), (131, 5), (163, 5), (195, 5), (227, 5), (258, 0), ];
pub const DISTANCE_TABLE: [(u16, u8); 30] = [
(1, 0), (2, 0), (3, 0), (4, 0), (5, 1), (7, 1), (9, 2), (13, 2), (17, 3), (25, 3), (33, 4), (49, 4), (65, 5), (97, 5), (129, 6), (193, 6), (257, 7), (385, 7), (513, 8), (769, 8), (1025, 9), (1537, 9), (2049, 10), (3073, 10), (4097, 11), (6145, 11), (8193, 12), (12289, 12), (16385, 13), (24577, 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 decode_length(code: u16, extra_bits: u32) -> Option<u16> {
if !(257..=285).contains(&code) {
return None;
}
let idx = (code - 257) as usize;
let (base, _) = LENGTH_TABLE[idx];
Some(base + extra_bits as u16)
}
pub fn decode_distance(code: u16, extra_bits: u32) -> Option<u16> {
if code > 29 {
return None;
}
let (base, _) = DISTANCE_TABLE[code as usize];
Some(base + extra_bits as u16)
}
const LENGTH_ENCODE_TABLE: [(u16, u8); 256] = build_length_encode_table();
const fn build_length_encode_table() -> [(u16, u8); 256] {
let mut table = [(0u16, 0u8); 256];
let mut i = 0;
while i < 29 {
let code = i as u16 + 257;
let (base, extra_bits) = LENGTH_TABLE[i];
let range = if extra_bits == 0 { 1u16 } else { 1 << extra_bits };
let mut j = 0u16;
while j < range && (base + j) <= 258 {
let idx = (base + j - 3) as usize;
if idx < 256 {
table[idx] = (code, extra_bits);
}
j += 1;
}
i += 1;
}
table
}
#[inline]
pub fn encode_length(length: u16) -> Option<(u16, u16, u8)> {
if !(3..=258).contains(&length) {
return None;
}
if length == 258 {
return Some((285, 0, 0));
}
let idx = (length - 3) as usize;
let (code, extra_bits) = LENGTH_ENCODE_TABLE[idx];
let base = LENGTH_TABLE[(code - 257) as usize].0;
Some((code, length - base, extra_bits))
}
const DISTANCE_ENCODE_SMALL: [(u8, u8); 256] = build_distance_encode_small();
const fn build_distance_encode_small() -> [(u8, u8); 256] {
let mut table = [(0u8, 0u8); 256];
let mut i = 0;
while i < 30 {
let (base, extra_bits) = DISTANCE_TABLE[i];
let range = if extra_bits == 0 { 1u16 } else { 1 << extra_bits };
let mut j = 0u16;
while j < range {
let dist = base + j;
if dist >= 1 && dist <= 256 {
table[(dist - 1) as usize] = (i as u8, extra_bits);
}
j += 1;
}
i += 1;
}
table
}
#[inline]
pub fn encode_distance(distance: u16) -> Option<(u16, u16, u8)> {
if !(1..=32768).contains(&distance) {
return None;
}
if distance <= 256 {
let (code, extra_bits) = DISTANCE_ENCODE_SMALL[(distance - 1) as usize];
let base = DISTANCE_TABLE[code as usize].0;
return Some((code as u16, distance - base, extra_bits));
}
let mut lo = 0usize;
let mut hi = 29usize;
while lo < hi {
let mid = (lo + hi + 1) / 2;
if DISTANCE_TABLE[mid].0 <= distance {
lo = mid;
} else {
hi = mid - 1;
}
}
let (base, extra_bits) = DISTANCE_TABLE[lo];
Some((lo as u16, distance - base, extra_bits))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decode_length() {
assert_eq!(decode_length(257, 0), Some(3));
assert_eq!(decode_length(258, 0), Some(4));
assert_eq!(decode_length(265, 0), Some(11));
assert_eq!(decode_length(265, 1), Some(12));
assert_eq!(decode_length(285, 0), Some(258));
}
#[test]
fn test_decode_distance() {
assert_eq!(decode_distance(0, 0), Some(1));
assert_eq!(decode_distance(4, 0), Some(5));
assert_eq!(decode_distance(4, 1), Some(6));
assert_eq!(decode_distance(29, 0x1FFF), Some(32768));
}
#[test]
fn test_encode_length() {
assert_eq!(encode_length(3), Some((257, 0, 0)));
assert_eq!(encode_length(4), Some((258, 0, 0)));
assert_eq!(encode_length(11), Some((265, 0, 1)));
assert_eq!(encode_length(12), Some((265, 1, 1)));
assert_eq!(encode_length(258), Some((285, 0, 0)));
}
#[test]
fn test_encode_distance() {
assert_eq!(encode_distance(1), Some((0, 0, 0)));
assert_eq!(encode_distance(5), Some((4, 0, 1)));
assert_eq!(encode_distance(6), Some((4, 1, 1)));
}
#[test]
fn test_length_roundtrip() {
for len in 3..=258 {
let (code, extra, _bits) = encode_length(len).unwrap();
let decoded = decode_length(code, extra as u32).unwrap();
assert_eq!(decoded, len, "Roundtrip failed for length {}", len);
}
}
#[test]
fn test_distance_roundtrip() {
for dist in 1..=32768u16 {
let (code, extra, _bits) = encode_distance(dist).unwrap();
let decoded = decode_distance(code, extra as u32).unwrap();
assert_eq!(decoded, dist, "Roundtrip failed for distance {}", dist);
}
}
}