use crate::error::Error;
use super::bitreader::BitReader;
#[derive(Debug, Clone)]
pub struct LzxHuffman<const N: usize> {
counts: [u16; 17],
symbols: [u16; N],
first_code: [u32; 17],
first_idx: [u16; 17],
max_length: u8,
}
impl<const N: usize> LzxHuffman<N> {
pub fn from_lengths(code_lengths: &[u8]) -> Result<Self, Error> {
assert!(code_lengths.len() <= N);
let mut counts = [0u16; 17];
let mut max_length: u8 = 0;
for &len in code_lengths {
if len > 16 {
return Err(Error::InvalidHuffmanTree);
}
if len > 0 {
counts[len as usize] += 1;
if len > max_length {
max_length = len;
}
}
}
if max_length == 0 {
return Ok(Self {
counts,
symbols: [0u16; N],
first_code: [0u32; 17],
first_idx: [0u16; 17],
max_length: 0,
});
}
let mut kraft: u32 = 0;
for l in 1..=16u32 {
kraft += (counts[l as usize] as u32) << (16 - l);
}
if kraft > (1 << 16) {
return Err(Error::InvalidHuffmanTree);
}
let mut first_code = [0u32; 17];
let mut first_idx = [0u16; 17];
let mut code: u32 = 0;
let mut idx: u16 = 0;
for l in 1..=16 {
code <<= 1;
first_code[l] = code;
first_idx[l] = idx;
code += counts[l] as u32;
idx += counts[l];
}
let mut symbols = [0u16; N];
let mut next = first_idx;
for (sym, &len) in code_lengths.iter().enumerate() {
if len > 0 {
symbols[next[len as usize] as usize] = sym as u16;
next[len as usize] += 1;
}
}
Ok(Self {
counts,
symbols,
first_code,
first_idx,
max_length,
})
}
pub const fn is_empty(&self) -> bool {
self.max_length == 0
}
pub fn decode(&self, reader: &mut BitReader) -> Result<Option<u16>, Error> {
if self.max_length == 0 {
return Err(Error::InvalidHuffmanTree);
}
let max = self.max_length as u32;
if reader.bits_available() < max {
return Ok(None);
}
let lookahead = reader.peek(max);
for length in 1..=max {
let code = lookahead >> (max - length);
let count = self.counts[length as usize] as u32;
if count > 0 {
let first = self.first_code[length as usize];
if code >= first && code < first + count {
let sym_idx = self.first_idx[length as usize] as u32 + (code - first);
reader.drop_bits(length);
return Ok(Some(self.symbols[sym_idx as usize]));
}
}
}
Err(Error::InvalidHuffmanTree)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn canonical_decoder_msb_walk() {
let dec = LzxHuffman::<4>::from_lengths(&[2, 1, 3, 3]).unwrap();
let mut r = BitReader::new();
r.feed(0x00);
r.feed(0x5C);
assert_eq!(dec.decode(&mut r).unwrap(), Some(1)); assert_eq!(dec.decode(&mut r).unwrap(), Some(0)); assert_eq!(dec.decode(&mut r).unwrap(), Some(3)); }
#[test]
fn empty_tree_rejects_decode() {
let dec = LzxHuffman::<4>::from_lengths(&[0, 0, 0, 0]).unwrap();
assert!(dec.is_empty());
let mut r = BitReader::new();
r.feed(0xFF);
r.feed(0xFF);
assert!(matches!(dec.decode(&mut r), Err(Error::InvalidHuffmanTree)));
}
#[test]
fn invalid_lengths_rejected() {
assert!(LzxHuffman::<4>::from_lengths(&[17, 0, 0, 0]).is_err());
assert!(LzxHuffman::<3>::from_lengths(&[1, 1, 2]).is_err());
}
}