use super::bits::{BitReader, reverse};
use super::{INCOMPLETE_CODE, INVALID_CODE, NO_DISTANCE_CODES, OVERSUBSCRIBED_CODE, TRUNCATED};
use crate::{Error, Result};
pub(super) const MAX_BITS: u32 = 15;
const MAX_SYMBOLS: usize = 288;
const FAST_BITS: u32 = 9;
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) enum Kind {
Complete,
Distance,
}
pub(super) struct Huffman {
counts: [u16; MAX_BITS as usize + 1],
symbols: [u16; MAX_SYMBOLS],
used: u16,
max_len: u32,
fast: [u16; 1 << FAST_BITS],
}
impl Huffman {
pub(super) fn new(lengths: &[u8], kind: Kind) -> Result<Self> {
if lengths.len() > MAX_SYMBOLS {
return Err(Error::InvalidFormat(INVALID_CODE));
}
let mut counts = [0u16; MAX_BITS as usize + 1];
for &len in lengths {
let slot = counts
.get_mut(usize::from(len))
.ok_or(Error::InvalidFormat(INVALID_CODE))?;
*slot += 1;
}
counts[0] = 0;
let mut left: i32 = 1;
for &count in &counts[1..] {
left = (left << 1) - i32::from(count);
if left < 0 {
return Err(Error::InvalidFormat(OVERSUBSCRIBED_CODE));
}
}
let used: u16 = counts[1..].iter().sum();
if left > 0 {
let allowed = kind == Kind::Distance && (used == 0 || (used == 1 && counts[1] == 1));
if !allowed {
return Err(Error::InvalidFormat(INCOMPLETE_CODE));
}
}
let mut offsets = [0usize; MAX_BITS as usize + 2];
for len in 1..=MAX_BITS as usize {
offsets[len + 1] = offsets[len] + usize::from(counts[len]);
}
let mut symbols = [0u16; MAX_SYMBOLS];
let mut next_code = [0u32; MAX_BITS as usize + 1];
let mut code = 0u32;
for len in 1..=MAX_BITS as usize {
code = (code + u32::from(counts[len - 1])) << 1;
next_code[len] = code;
}
let mut fast = [0u16; 1 << FAST_BITS];
for (symbol, &len) in (0u16..).zip(lengths) {
if len == 0 {
continue;
}
let len_index = usize::from(len);
symbols[offsets[len_index]] = symbol;
offsets[len_index] += 1;
let code = next_code[len_index];
next_code[len_index] += 1;
let len = u32::from(len);
if len <= FAST_BITS {
let entry = symbol | ((len as u16) << FAST_BITS);
let step = 1usize << len;
let mut index = reverse(code, len) as usize;
while index < fast.len() {
fast[index] = entry;
index += step;
}
}
}
let max_len = (1..=MAX_BITS)
.rev()
.find(|&len| counts[len as usize] != 0)
.unwrap_or(0);
Ok(Self {
counts,
symbols,
used,
max_len,
fast,
})
}
pub(super) fn decode(&self, reader: &mut BitReader<'_>) -> Result<u16> {
if self.used == 0 {
return Err(Error::InvalidFormat(NO_DISTANCE_CODES));
}
let (bits, available) = reader.peek(MAX_BITS);
let entry = self.fast[(bits & ((1 << FAST_BITS) - 1)) as usize];
if entry != 0 {
let len = u32::from(entry >> FAST_BITS);
if len > available {
return Err(Error::InvalidFormat(TRUNCATED));
}
reader.consume(len);
return Ok(entry & ((1 << FAST_BITS) - 1));
}
let mut code: i32 = 0;
let mut first: i32 = 0;
let mut index: i32 = 0;
for len in 1..=self.max_len {
if len > available {
return Err(Error::InvalidFormat(TRUNCATED));
}
code |= ((bits >> (len - 1)) & 1) as i32;
let count = i32::from(self.counts[len as usize]);
if code >= first && code - first < count {
let symbol = self
.symbols
.get((index + code - first) as usize)
.copied()
.ok_or(Error::InvalidFormat(INVALID_CODE))?;
reader.consume(len);
return Ok(symbol);
}
index += count;
first = (first + count) << 1;
code <<= 1;
}
Err(Error::InvalidFormat(INVALID_CODE))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::deflate::bits::BitWriter;
#[test]
fn rfc1951_section_3_2_2_example() {
let lengths = [3, 3, 3, 3, 3, 2, 4, 4];
let table = Huffman::new(&lengths, Kind::Complete).unwrap();
let codes: [(u32, u32); 8] = [
(0b010, 3),
(0b011, 3),
(0b100, 3),
(0b101, 3),
(0b110, 3),
(0b00, 2),
(0b1110, 4),
(0b1111, 4),
];
let mut out = Vec::new();
let mut w = BitWriter::new(&mut out);
for &(code, len) in codes.iter().rev() {
w.code(code, len);
}
w.finish();
let mut r = BitReader::new(&out);
for symbol in (0..8u16).rev() {
assert_eq!(table.decode(&mut r).unwrap(), symbol);
}
}
#[test]
fn decodes_fifteen_bit_codes() {
let mut lengths: Vec<u8> = (1..=15).collect();
lengths.push(15);
let table = Huffman::new(&lengths, Kind::Complete).unwrap();
let mut out = Vec::new();
let mut w = BitWriter::new(&mut out);
w.code(0x7FFF, 15);
w.code(0x7FFE, 15);
w.code(0b10, 2);
w.finish();
let mut r = BitReader::new(&out);
assert_eq!(table.decode(&mut r).unwrap(), 15);
assert_eq!(table.decode(&mut r).unwrap(), 14);
assert_eq!(table.decode(&mut r).unwrap(), 1);
}
#[test]
fn rejects_over_subscribed_code() {
let r = Huffman::new(&[1, 1, 1], Kind::Complete);
assert!(matches!(r, Err(Error::InvalidFormat(OVERSUBSCRIBED_CODE))));
let r = Huffman::new(&[1, 1, 1], Kind::Distance);
assert!(matches!(r, Err(Error::InvalidFormat(OVERSUBSCRIBED_CODE))));
}
#[test]
fn rejects_incomplete_code() {
let r = Huffman::new(&[1, 2], Kind::Complete);
assert!(matches!(r, Err(Error::InvalidFormat(INCOMPLETE_CODE))));
let r = Huffman::new(&[0; 19], Kind::Complete);
assert!(matches!(r, Err(Error::InvalidFormat(INCOMPLETE_CODE))));
let r = Huffman::new(&[0, 2], Kind::Distance);
assert!(matches!(r, Err(Error::InvalidFormat(INCOMPLETE_CODE))));
let r = Huffman::new(&[1, 2], Kind::Distance);
assert!(matches!(r, Err(Error::InvalidFormat(INCOMPLETE_CODE))));
let r = Huffman::new(&[1], Kind::Complete);
assert!(matches!(r, Err(Error::InvalidFormat(INCOMPLETE_CODE))));
}
#[test]
fn single_distance_code() {
let table = Huffman::new(&[0, 0, 1], Kind::Distance).unwrap();
let mut r = BitReader::new(&[0b0000_0010]);
assert_eq!(table.decode(&mut r).unwrap(), 2);
assert_eq!(
table.decode(&mut r),
Err(Error::InvalidFormat(INVALID_CODE))
);
}
#[test]
fn no_distance_codes() {
let table = Huffman::new(&[0], Kind::Distance).unwrap();
let mut r = BitReader::new(&[0]);
assert_eq!(
table.decode(&mut r),
Err(Error::InvalidFormat(NO_DISTANCE_CODES))
);
}
#[test]
fn truncated_code_is_an_error() {
let table = Huffman::new(&[8u8; 256], Kind::Complete).unwrap();
let mut r = BitReader::new(&[]);
assert_eq!(table.decode(&mut r), Err(Error::InvalidFormat(TRUNCATED)));
let mut lengths: Vec<u8> = (1..=15).collect();
lengths.push(15);
let table = Huffman::new(&lengths, Kind::Complete).unwrap();
let mut r = BitReader::new(&[0xFF]);
assert_eq!(table.decode(&mut r), Err(Error::InvalidFormat(TRUNCATED)));
}
#[test]
fn rejects_lengths_above_fifteen_and_oversized_alphabets() {
assert!(Huffman::new(&[16, 1], Kind::Complete).is_err());
assert!(Huffman::new(&[9u8; 289], Kind::Complete).is_err());
}
}