use super::{HuffmanError, TABLE};
#[derive(Clone, Copy)]
enum Node {
Internal(u16, u16),
Leaf(u8),
Eos,
}
const TREE_CAPACITY: usize = 514;
const ROOT: u16 = 1;
const NUM_STATES: usize = 256;
const fn build_decode_tree() -> [Node; TREE_CAPACITY] {
let mut tree = [Node::Internal(0, 0); TREE_CAPACITY];
let mut next_free = ROOT + 1;
let mut sym = 0_u16;
while sym < 257 {
let (code, bit_length) = TABLE[sym as usize];
let mut current = ROOT;
let mut bit = bit_length;
while bit > 0 {
bit -= 1;
let is_one = (code >> bit) & 1 == 1;
let Node::Internal(zero, one) = tree[current as usize] else {
panic!("walked into a leaf while inserting");
};
let child = if is_one { one } else { zero };
if child != 0 {
current = child;
} else {
let new = next_free;
next_free += 1;
#[allow(clippy::cast_possible_truncation)] {
tree[new as usize] = if bit == 0 {
if sym < 256 {
Node::Leaf(sym as u8)
} else {
Node::Eos
}
} else {
Node::Internal(0, 0)
};
}
tree[current as usize] = if is_one {
Node::Internal(zero, new)
} else {
Node::Internal(new, one)
};
current = new;
}
}
sym += 1;
}
tree
}
#[derive(Clone, Copy)]
struct NibbleEntry {
next_state: u8,
symbol: u8,
emit: bool,
eos: bool,
}
struct NibbleDecoder {
table: [[NibbleEntry; 16]; NUM_STATES],
depth: [u8; NUM_STATES],
padding_valid: [bool; NUM_STATES],
}
#[allow(clippy::too_many_lines)] const fn build_nibble_decoder() -> NibbleDecoder {
let tree = build_decode_tree();
let mut node_to_state = [0_u8; TREE_CAPACITY];
let mut state_to_node = [0_u16; NUM_STATES];
let mut num_states = 0_usize;
let mut i = 1_usize;
while i < TREE_CAPACITY {
if let Node::Internal(_, _) = tree[i] {
#[allow(clippy::cast_possible_truncation)]
{
node_to_state[i] = num_states as u8;
state_to_node[num_states] = i as u16;
}
num_states += 1;
}
i += 1;
}
assert!(num_states == NUM_STATES, "expected 256 internal nodes");
let mut node_depth = [0_u8; TREE_CAPACITY];
let mut n = 1_usize;
while n < TREE_CAPACITY {
if let Node::Internal(zero, one) = tree[n] {
let d = node_depth[n] + 1;
if zero != 0 {
node_depth[zero as usize] = d;
}
if one != 0 {
node_depth[one as usize] = d;
}
}
n += 1;
}
let mut depth = [0_u8; NUM_STATES];
let mut s = 0_usize;
while s < NUM_STATES {
depth[s] = node_depth[state_to_node[s] as usize];
s += 1;
}
let mut padding_valid = [false; NUM_STATES];
padding_valid[node_to_state[ROOT as usize] as usize] = true;
let mut current = ROOT as usize;
while let Node::Internal(_, one) = tree[current] {
if one == 0 {
break;
}
match tree[one as usize] {
Node::Internal(_, _) => {
padding_valid[node_to_state[one as usize] as usize] = true;
current = one as usize;
}
_ => break,
}
}
let empty = NibbleEntry {
next_state: 0,
symbol: 0,
emit: false,
eos: false,
};
let mut table = [[empty; 16]; NUM_STATES];
let mut state = 0_usize;
while state < NUM_STATES {
let start_node = state_to_node[state] as usize;
let mut nibble = 0_u8;
while nibble < 16 {
let mut current = start_node;
let mut symbol = 0_u8;
let mut emit = false;
let mut eos = false;
let mut bit = 4_u8;
while bit > 0 && !eos {
bit -= 1;
let is_one = (nibble >> bit) & 1 == 1;
let Node::Internal(zero, one) = tree[current] else {
panic!("expected internal node during nibble walk");
};
let child = if is_one { one } else { zero };
match tree[child as usize] {
Node::Internal(_, _) => {
current = child as usize;
}
Node::Leaf(sym) => {
symbol = sym;
emit = true;
current = ROOT as usize;
}
Node::Eos => {
eos = true;
}
}
}
table[state][nibble as usize] = NibbleEntry {
next_state: node_to_state[current],
symbol,
emit,
eos,
};
nibble += 1;
}
state += 1;
}
NibbleDecoder {
table,
depth,
padding_valid,
}
}
static DECODER: NibbleDecoder = build_nibble_decoder();
#[cfg(test)]
pub(super) fn decode_bitwise(input: &[u8]) -> Result<Vec<u8>, HuffmanError> {
static TREE: [Node; TREE_CAPACITY] = build_decode_tree();
let root_children = |node: Node| match node {
Node::Internal(zero, one) => (zero, one),
_ => unreachable!("root must be an internal node"),
};
let mut output = Vec::with_capacity(input.len());
let mut bits_since_leaf = 0_u8;
let mut padding_all_ones = true;
let (mut zero, mut one) = root_children(TREE[ROOT as usize]);
for &byte in input {
for bit_index in (0..8).rev() {
let is_one = (byte >> bit_index) & 1 == 1;
let child = if is_one { one } else { zero };
bits_since_leaf += 1;
padding_all_ones &= is_one;
match TREE[child as usize] {
Node::Internal(z, o) => {
zero = z;
one = o;
}
Node::Leaf(decoded) => {
output.push(decoded);
(zero, one) = root_children(TREE[ROOT as usize]);
bits_since_leaf = 0;
padding_all_ones = true;
}
Node::Eos => return Err(HuffmanError::EosInStream),
}
}
}
if bits_since_leaf > 7 {
return Err(HuffmanError::PaddingTooLong);
}
if bits_since_leaf > 0 && !padding_all_ones {
return Err(HuffmanError::InvalidPadding);
}
Ok(output)
}
pub(in crate::headers) fn decode(input: &[u8]) -> Result<Vec<u8>, HuffmanError> {
let mut output = Vec::with_capacity(input.len());
let mut state = 0_usize;
for &byte in input {
let entry = DECODER.table[state][(byte >> 4) as usize];
if entry.eos {
return Err(HuffmanError::EosInStream);
}
if entry.emit {
output.push(entry.symbol);
}
state = entry.next_state as usize;
let entry = DECODER.table[state][(byte & 0xF) as usize];
if entry.eos {
return Err(HuffmanError::EosInStream);
}
if entry.emit {
output.push(entry.symbol);
}
state = entry.next_state as usize;
}
let depth = DECODER.depth[state];
if depth > 7 {
return Err(HuffmanError::PaddingTooLong);
}
if depth > 0 && !DECODER.padding_valid[state] {
return Err(HuffmanError::InvalidPadding);
}
Ok(output)
}