const FAST_BITS: u8 = 12;
pub struct HuffmanTree {
fast_table: Vec<(u16, u8)>,
nodes: Vec<[i32; 2]>,
min_len: u8,
}
impl HuffmanTree {
pub fn from_lengths(lengths: &[u8]) -> Result<Self, &'static str> {
let mut symbols: Vec<(u16, u8)> = lengths.iter()
.enumerate()
.filter(|(_, len)| **len > 0)
.map(|(sym, len)| (sym as u16, *len))
.collect();
if symbols.is_empty() {
return Err("no symbols");
}
symbols.sort_by(|a, b| a.1.cmp(&b.1).then(a.0.cmp(&b.0)));
let min_len = symbols[0].1;
let mut nodes: Vec<[i32; 2]> = vec![[0, 0]];
let mut code: u32 = 0;
let mut prev_len: u8 = symbols[0].1;
let mut code_entries: Vec<(u32, u8, u16)> = Vec::with_capacity(symbols.len());
for &(sym, len) in &symbols {
code <<= len - prev_len;
prev_len = len;
code_entries.push((code, len, sym));
let mut node_idx: usize = 0;
for bit_pos in (0..len).rev() {
let bit = ((code >> bit_pos) & 1) as usize;
let child = nodes[node_idx][bit];
if child > 0 {
node_idx = child as usize;
} else if bit_pos > 0 {
let new_idx = nodes.len();
nodes.push([0, 0]);
nodes[node_idx][bit] = new_idx as i32;
node_idx = new_idx;
} else {
nodes[node_idx][bit] = -(sym as i32 + 1);
}
}
code += 1;
}
let table_size = 1usize << FAST_BITS;
let mut fast_table = vec![(0u16, 0u8); table_size];
for &(c, len, sym) in &code_entries {
if len <= FAST_BITS {
let pad = FAST_BITS - len;
let base = (c as usize) << pad;
for suffix in 0..(1usize << pad) {
fast_table[base | suffix] = (sym, len);
}
}
}
Ok(Self { fast_table, nodes, min_len })
}
#[inline(always)]
pub fn decode(&self, reader: &mut super::bitreader::BitReader<'_>) -> Option<u16> {
if let Some(bits) = reader.peek(FAST_BITS) {
let entry = unsafe { self.fast_table.get_unchecked(bits as usize) };
if entry.1 > 0 {
reader.consume(entry.1);
return Some(entry.0);
}
}
self.decode_slow(reader)
}
#[cold]
fn decode_slow(&self, reader: &mut super::bitreader::BitReader<'_>) -> Option<u16> {
let mut node_idx: usize = 0;
loop {
let bit = reader.read_bit()? as usize;
let child = self.nodes[node_idx][bit];
if child < 0 {
return Some((-child - 1) as u16);
}
node_idx = child as usize;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bitreader::BitReader;
#[test]
fn simple_tree() {
let tree = HuffmanTree::from_lengths(&[1, 2, 2]).unwrap();
let data = [0b0_10_11_0_00];
let mut r = BitReader::new(&data);
assert_eq!(tree.decode(&mut r), Some(0));
assert_eq!(tree.decode(&mut r), Some(1));
assert_eq!(tree.decode(&mut r), Some(2));
assert_eq!(tree.decode(&mut r), Some(0));
}
#[test]
fn fast_table_coverage() {
let tree = HuffmanTree::from_lengths(&[2, 2, 3, 3]).unwrap();
let data = [0b00_01_100_1, 0b01_000000];
let mut r = BitReader::new(&data);
assert_eq!(tree.decode(&mut r), Some(0));
assert_eq!(tree.decode(&mut r), Some(1));
assert_eq!(tree.decode(&mut r), Some(2));
assert_eq!(tree.decode(&mut r), Some(3));
}
}