use alloc::vec;
use alloc::vec::Vec;
use crate::error::Error;
use super::bits::BitBuf;
#[derive(Debug, Clone)]
pub struct Huffman {
counts: [u16; 17],
symbols: Vec<u16>,
first_code: [u32; 17],
first_idx: [u16; 17],
max_length: u8,
}
impl Huffman {
pub fn from_lengths(code_lengths: &[u8]) -> Result<Self, Error> {
let mut counts = [0u16; 17];
let mut max_length: u8 = 0;
for &len in code_lengths {
if len > 15 {
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: Vec::new(),
first_code: [0u32; 17],
first_idx: [0u16; 17],
max_length: 0,
});
}
let mut kraft: u32 = 0;
for l in 1..=15u32 {
kraft = kraft.wrapping_add((counts[l as usize] as u32) << (15 - l));
}
if kraft > (1 << 15) {
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..=15 {
code <<= 1;
first_code[l] = code;
first_idx[l] = idx;
code = code.wrapping_add(counts[l] as u32);
idx = idx.wrapping_add(counts[l]);
}
let total: usize = counts[1..=15].iter().map(|&c| c as usize).sum();
let mut symbols = vec![0u16; total];
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, br: &mut BitBuf) -> Result<u16, Error> {
if self.max_length == 0 {
return Err(Error::InvalidHuffmanTree);
}
let look = br.peek16() as u32;
for length in 1..=self.max_length as u32 {
let code = look >> (16 - 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 {
if br.bits_remaining() < length {
return Err(Error::UnexpectedEnd);
}
let sym_idx = self.first_idx[length as usize] as u32 + (code - first);
br.skip(length);
return Ok(self.symbols[sym_idx as usize]);
}
}
}
Err(Error::InvalidHuffmanTree)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_and_decode_minimal_tree() {
let huff = Huffman::from_lengths(&[2, 1, 3, 3]).unwrap();
let mut br = BitBuf::new();
br.reset(&[0x5F], 8);
assert_eq!(huff.decode(&mut br).unwrap(), 1);
assert_eq!(huff.decode(&mut br).unwrap(), 0);
assert_eq!(huff.decode(&mut br).unwrap(), 3);
}
#[test]
fn empty_table_rejects_decode() {
let huff = Huffman::from_lengths(&[0, 0]).unwrap();
let mut br = BitBuf::new();
br.reset(&[0], 8);
assert!(huff.decode(&mut br).is_err());
}
#[test]
fn invalid_length_rejected() {
assert!(Huffman::from_lengths(&[16, 0]).is_err());
assert!(Huffman::from_lengths(&[1, 1, 2]).is_err());
}
}