Skip to content

Commit fcbbb20

Browse files
authored
Faster decoding of long huffman codes (#102)
1 parent bdef041 commit fcbbb20

File tree

1 file changed

+39
-22
lines changed

1 file changed

+39
-22
lines changed

src/huffman.rs

+39-22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use super::lossless::BitReader;
99
///
1010
1111
const MAX_ALLOWED_CODE_LENGTH: usize = 15;
12+
const MAX_TABLE_BITS: u8 = 10;
1213

1314
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1415
enum HuffmanTreeNode {
@@ -97,7 +98,7 @@ impl HuffmanTree {
9798
}
9899

99100
// Populate decoding table
100-
let table_bits = max_code_length.min(10);
101+
let table_bits = max_code_length.min(MAX_TABLE_BITS as u16);
101102
let table_size = (1 << table_bits) as usize;
102103
let table_mask = table_size as u16 - 1;
103104
let mut table = vec![0; table_size];
@@ -115,38 +116,48 @@ impl HuffmanTree {
115116
// If the longest code is larger than the table size, build a tree as a fallback.
116117
let mut tree = Vec::new();
117118
if max_code_length > table_bits {
118-
tree = vec![HuffmanTreeNode::Empty; 2 * num_symbols - 1];
119+
for (symbol, (&code, &length)) in huff_codes.iter().zip(code_lengths.iter()).enumerate()
120+
{
121+
if length > table_bits {
122+
let table_index =
123+
((u16::reverse_bits(code) >> (16 - length)) & table_mask) as usize;
124+
let table_value = table[table_index];
125+
126+
debug_assert_eq!(table_value >> 16, 0);
127+
128+
let mut node_index = if table_value == 0 {
129+
let node_index = tree.len();
130+
table[table_index] = (node_index + 1) as u32;
131+
tree.push(HuffmanTreeNode::Empty);
132+
node_index
133+
} else {
134+
(table_value - 1) as usize
135+
};
119136

120-
let mut num_nodes = 1;
121-
for (symbol, &length) in code_lengths.iter().enumerate() {
122-
let code = huff_codes[symbol];
123-
let code_length = length;
124-
let symbol = symbol.try_into().unwrap();
125-
126-
if length > 0 {
127-
let mut node_index = 0;
128137
let code = usize::from(code);
129-
130-
for length in (0..code_length).rev() {
138+
for depth in (0..length - table_bits).rev() {
131139
let node = tree[node_index];
132140

133141
let offset = match node {
134142
HuffmanTreeNode::Empty => {
135143
// Turns a node from empty into a branch and assigns its children
136-
let offset_index = num_nodes - node_index;
137-
tree[node_index] = HuffmanTreeNode::Branch(offset_index);
138-
num_nodes += 2;
139-
offset_index
144+
let offset = tree.len() - node_index;
145+
tree[node_index] = HuffmanTreeNode::Branch(offset);
146+
tree.push(HuffmanTreeNode::Empty);
147+
tree.push(HuffmanTreeNode::Empty);
148+
offset
140149
}
141150
HuffmanTreeNode::Leaf(_) => return Err(DecodingError::HuffmanError),
142151
HuffmanTreeNode::Branch(offset) => offset,
143152
};
144153

145-
node_index += offset + ((code >> length) & 1);
154+
node_index += offset + ((code >> depth) & 1);
146155
}
147156

148157
match tree[node_index] {
149-
HuffmanTreeNode::Empty => tree[node_index] = HuffmanTreeNode::Leaf(symbol),
158+
HuffmanTreeNode::Empty => {
159+
tree[node_index] = HuffmanTreeNode::Leaf(symbol as u16)
160+
}
150161
HuffmanTreeNode::Leaf(_) => return Err(DecodingError::HuffmanError),
151162
HuffmanTreeNode::Branch(_offset) => {
152163
return Err(DecodingError::HuffmanError)
@@ -187,10 +198,11 @@ impl HuffmanTree {
187198
fn read_symbol_slowpath<R: BufRead>(
188199
tree: &[HuffmanTreeNode],
189200
mut v: usize,
201+
start_index: usize,
190202
bit_reader: &mut BitReader<R>,
191203
) -> Result<u16, DecodingError> {
192-
let mut depth = 0;
193-
let mut index = 0;
204+
let mut depth = MAX_TABLE_BITS;
205+
let mut index = start_index;
194206
loop {
195207
match &tree[index] {
196208
HuffmanTreeNode::Branch(children_offset) => {
@@ -223,12 +235,17 @@ impl HuffmanTree {
223235
} => {
224236
let v = bit_reader.peek_full() as u16;
225237
let entry = table[(v & table_mask) as usize];
226-
if entry != 0 {
238+
if entry >> 16 != 0 {
227239
bit_reader.consume((entry >> 16) as u8)?;
228240
return Ok(entry as u16);
229241
}
230242

231-
Self::read_symbol_slowpath(tree, v as usize, bit_reader)
243+
Self::read_symbol_slowpath(
244+
tree,
245+
(v >> MAX_TABLE_BITS) as usize,
246+
((entry & 0xffff) - 1) as usize,
247+
bit_reader,
248+
)
232249
}
233250
HuffmanTreeInner::Single(symbol) => Ok(*symbol),
234251
}

0 commit comments

Comments
 (0)