Coverage Report

Created: 2026-09-28 07:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/image-webp-0.2.4/src/huffman.rs
Line
Count
Source
1
//! Rudimentary utility for reading Canonical Huffman Codes.
2
//! Based off <https://github.com/webmproject/libwebp/blob/7f8472a610b61ec780ef0a8873cd954ac512a505/src/utils/huffman.c>
3
4
use std::io::BufRead;
5
6
use crate::decoder::DecodingError;
7
8
use super::lossless::BitReader;
9
10
const MAX_ALLOWED_CODE_LENGTH: usize = 15;
11
const MAX_TABLE_BITS: u8 = 10;
12
13
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14
enum HuffmanTreeNode {
15
    Branch(usize), //offset in vector to children
16
    Leaf(u16),     //symbol stored in leaf
17
    Empty,
18
}
19
20
#[derive(Clone, Debug)]
21
enum HuffmanTreeInner {
22
    Single(u16),
23
    Tree {
24
        tree: Vec<HuffmanTreeNode>,
25
        table: Vec<u32>,
26
        table_mask: u16,
27
    },
28
}
29
30
/// Huffman tree
31
#[derive(Clone, Debug)]
32
pub(crate) struct HuffmanTree(HuffmanTreeInner);
33
34
impl Default for HuffmanTree {
35
197k
    fn default() -> Self {
36
197k
        Self(HuffmanTreeInner::Single(0))
37
197k
    }
38
}
39
40
impl HuffmanTree {
41
    /// Builds a tree implicitly, just from code lengths
42
30.0k
    pub(crate) fn build_implicit(code_lengths: Vec<u16>) -> Result<Self, DecodingError> {
43
        // Count symbols and build histogram
44
30.0k
        let mut num_symbols = 0;
45
30.0k
        let mut code_length_hist = [0; MAX_ALLOWED_CODE_LENGTH + 1];
46
4.33M
        for &length in code_lengths.iter().filter(|&&x| x != 0) {
47
990k
            code_length_hist[usize::from(length)] += 1;
48
990k
            num_symbols += 1;
49
990k
        }
50
51
        // Handle special cases
52
30.0k
        if num_symbols == 0 {
53
53
            return Err(DecodingError::HuffmanError);
54
29.9k
        } else if num_symbols == 1 {
55
25.4k
            let root_symbol = code_lengths.iter().position(|&x| x != 0).unwrap() as u16;
56
2.09k
            return Ok(Self::build_single_node(root_symbol));
57
27.8k
        };
58
59
        // Assign codes
60
27.8k
        let mut curr_code = 0;
61
27.8k
        let mut next_codes = [0; MAX_ALLOWED_CODE_LENGTH + 1];
62
296k
        let max_code_length = code_length_hist.iter().rposition(|&x| x != 0).unwrap() as u16;
63
149k
        for code_len in 1..usize::from(max_code_length) + 1 {
64
149k
            next_codes[code_len] = curr_code;
65
149k
            curr_code = (curr_code + code_length_hist[code_len]) << 1;
66
149k
        }
67
68
        // Confirm that the huffman tree is valid
69
27.8k
        if curr_code != 2 << max_code_length {
70
407
            return Err(DecodingError::HuffmanError);
71
27.4k
        }
72
73
        // Calculate table/tree parameters
74
27.4k
        let table_bits = max_code_length.min(u16::from(MAX_TABLE_BITS));
75
27.4k
        let table_size = (1 << table_bits) as usize;
76
27.4k
        let table_mask = table_size as u16 - 1;
77
27.4k
        let tree_size = code_length_hist[table_bits as usize + 1..=max_code_length as usize]
78
27.4k
            .iter()
79
27.4k
            .sum::<u16>() as usize;
80
81
        // Populate decoding table
82
27.4k
        let mut tree = Vec::with_capacity(2 * tree_size);
83
27.4k
        let mut table = vec![0; table_size];
84
4.14M
        for (symbol, &length) in code_lengths.iter().enumerate() {
85
4.14M
            if length == 0 {
86
3.17M
                continue;
87
961k
            }
88
89
961k
            let code = next_codes[length as usize];
90
961k
            next_codes[length as usize] += 1;
91
92
961k
            if length <= table_bits {
93
667k
                let mut j = (u16::reverse_bits(code) >> (16 - length)) as usize;
94
667k
                let entry = (u32::from(length) << 16) | symbol as u32;
95
6.37M
                while j < table_size {
96
5.71M
                    table[j] = entry;
97
5.71M
                    j += 1 << length as usize;
98
5.71M
                }
99
            } else {
100
293k
                let table_index =
101
293k
                    ((u16::reverse_bits(code) >> (16 - length)) & table_mask) as usize;
102
293k
                let table_value = table[table_index];
103
104
293k
                debug_assert_eq!(table_value >> 16, 0);
105
106
293k
                let mut node_index = if table_value == 0 {
107
58.3k
                    let node_index = tree.len();
108
58.3k
                    table[table_index] = (node_index + 1) as u32;
109
58.3k
                    tree.push(HuffmanTreeNode::Empty);
110
58.3k
                    node_index
111
                } else {
112
235k
                    (table_value - 1) as usize
113
                };
114
115
293k
                let code = usize::from(code);
116
862k
                for depth in (0..length - table_bits).rev() {
117
862k
                    let node = tree[node_index];
118
119
862k
                    let offset = match node {
120
                        HuffmanTreeNode::Empty => {
121
                            // Turns a node from empty into a branch and assigns its children
122
235k
                            let offset = tree.len() - node_index;
123
235k
                            tree[node_index] = HuffmanTreeNode::Branch(offset);
124
235k
                            tree.push(HuffmanTreeNode::Empty);
125
235k
                            tree.push(HuffmanTreeNode::Empty);
126
235k
                            offset
127
                        }
128
0
                        HuffmanTreeNode::Leaf(_) => return Err(DecodingError::HuffmanError),
129
626k
                        HuffmanTreeNode::Branch(offset) => offset,
130
                    };
131
132
862k
                    node_index += offset + ((code >> depth) & 1);
133
                }
134
135
293k
                match tree[node_index] {
136
293k
                    HuffmanTreeNode::Empty => {
137
293k
                        tree[node_index] = HuffmanTreeNode::Leaf(symbol as u16);
138
293k
                    }
139
0
                    HuffmanTreeNode::Leaf(_) => return Err(DecodingError::HuffmanError),
140
0
                    HuffmanTreeNode::Branch(_offset) => return Err(DecodingError::HuffmanError),
141
                }
142
            }
143
        }
144
145
27.4k
        Ok(Self(HuffmanTreeInner::Tree {
146
27.4k
            tree,
147
27.4k
            table,
148
27.4k
            table_mask,
149
27.4k
        }))
150
30.0k
    }
151
152
170k
    pub(crate) const fn build_single_node(symbol: u16) -> Self {
153
170k
        Self(HuffmanTreeInner::Single(symbol))
154
170k
    }
155
156
10.0k
    pub(crate) fn build_two_node(zero: u16, one: u16) -> Self {
157
10.0k
        Self(HuffmanTreeInner::Tree {
158
10.0k
            tree: vec![
159
10.0k
                HuffmanTreeNode::Leaf(zero),
160
10.0k
                HuffmanTreeNode::Leaf(one),
161
10.0k
                HuffmanTreeNode::Empty,
162
10.0k
            ],
163
10.0k
            table: vec![(1 << 16) | u32::from(zero), (1 << 16) | u32::from(one)],
164
10.0k
            table_mask: 0x1,
165
10.0k
        })
166
10.0k
    }
167
168
244M
    pub(crate) const fn is_single_node(&self) -> bool {
169
244M
        matches!(self.0, HuffmanTreeInner::Single(_))
170
244M
    }
171
172
    #[inline(never)]
173
782k
    fn read_symbol_slowpath<R: BufRead>(
174
782k
        tree: &[HuffmanTreeNode],
175
782k
        mut v: usize,
176
782k
        start_index: usize,
177
782k
        bit_reader: &mut BitReader<R>,
178
782k
    ) -> Result<u16, DecodingError> {
179
782k
        let mut depth = MAX_TABLE_BITS;
180
782k
        let mut index = start_index;
181
        loop {
182
2.56M
            match &tree[index] {
183
1.78M
                HuffmanTreeNode::Branch(children_offset) => {
184
1.78M
                    index += children_offset + (v & 1);
185
1.78M
                    depth += 1;
186
1.78M
                    v >>= 1;
187
1.78M
                }
188
782k
                HuffmanTreeNode::Leaf(symbol) => {
189
782k
                    bit_reader.consume(depth)?;
190
782k
                    return Ok(*symbol);
191
                }
192
0
                HuffmanTreeNode::Empty => return Err(DecodingError::HuffmanError),
193
            }
194
        }
195
782k
    }
<image_webp::huffman::HuffmanTree>::read_symbol_slowpath::<std::io::Take<&mut std::io::cursor::Cursor<&[u8]>>>
Line
Count
Source
173
775k
    fn read_symbol_slowpath<R: BufRead>(
174
775k
        tree: &[HuffmanTreeNode],
175
775k
        mut v: usize,
176
775k
        start_index: usize,
177
775k
        bit_reader: &mut BitReader<R>,
178
775k
    ) -> Result<u16, DecodingError> {
179
775k
        let mut depth = MAX_TABLE_BITS;
180
775k
        let mut index = start_index;
181
        loop {
182
2.54M
            match &tree[index] {
183
1.77M
                HuffmanTreeNode::Branch(children_offset) => {
184
1.77M
                    index += children_offset + (v & 1);
185
1.77M
                    depth += 1;
186
1.77M
                    v >>= 1;
187
1.77M
                }
188
775k
                HuffmanTreeNode::Leaf(symbol) => {
189
775k
                    bit_reader.consume(depth)?;
190
775k
                    return Ok(*symbol);
191
                }
192
0
                HuffmanTreeNode::Empty => return Err(DecodingError::HuffmanError),
193
            }
194
        }
195
775k
    }
<image_webp::huffman::HuffmanTree>::read_symbol_slowpath::<&mut std::io::Take<&mut std::io::cursor::Cursor<&[u8]>>>
Line
Count
Source
173
7.01k
    fn read_symbol_slowpath<R: BufRead>(
174
7.01k
        tree: &[HuffmanTreeNode],
175
7.01k
        mut v: usize,
176
7.01k
        start_index: usize,
177
7.01k
        bit_reader: &mut BitReader<R>,
178
7.01k
    ) -> Result<u16, DecodingError> {
179
7.01k
        let mut depth = MAX_TABLE_BITS;
180
7.01k
        let mut index = start_index;
181
        loop {
182
15.6k
            match &tree[index] {
183
8.59k
                HuffmanTreeNode::Branch(children_offset) => {
184
8.59k
                    index += children_offset + (v & 1);
185
8.59k
                    depth += 1;
186
8.59k
                    v >>= 1;
187
8.59k
                }
188
7.01k
                HuffmanTreeNode::Leaf(symbol) => {
189
7.01k
                    bit_reader.consume(depth)?;
190
7.00k
                    return Ok(*symbol);
191
                }
192
0
                HuffmanTreeNode::Empty => return Err(DecodingError::HuffmanError),
193
            }
194
        }
195
7.01k
    }
Unexecuted instantiation: <image_webp::huffman::HuffmanTree>::read_symbol_slowpath::<_>
196
197
    /// Reads a symbol using the bit reader.
198
    ///
199
    /// You must call call `bit_reader.fill()` before calling this function or it may erroroneosly
200
    /// detect the end of the stream and return a bitstream error.
201
401M
    pub(crate) fn read_symbol<R: BufRead>(
202
401M
        &self,
203
401M
        bit_reader: &mut BitReader<R>,
204
401M
    ) -> Result<u16, DecodingError> {
205
401M
        match &self.0 {
206
            HuffmanTreeInner::Tree {
207
71.3M
                tree,
208
71.3M
                table,
209
71.3M
                table_mask,
210
            } => {
211
71.3M
                let v = bit_reader.peek_full() as u16;
212
71.3M
                let entry = table[(v & table_mask) as usize];
213
71.3M
                if entry >> 16 != 0 {
214
70.5M
                    bit_reader.consume((entry >> 16) as u8)?;
215
70.5M
                    return Ok(entry as u16);
216
782k
                }
217
218
782k
                Self::read_symbol_slowpath(
219
782k
                    tree,
220
782k
                    (v >> MAX_TABLE_BITS) as usize,
221
782k
                    ((entry & 0xffff) - 1) as usize,
222
782k
                    bit_reader,
223
                )
224
            }
225
329M
            HuffmanTreeInner::Single(symbol) => Ok(*symbol),
226
        }
227
401M
    }
<image_webp::huffman::HuffmanTree>::read_symbol::<std::io::Take<&mut std::io::cursor::Cursor<&[u8]>>>
Line
Count
Source
201
368M
    pub(crate) fn read_symbol<R: BufRead>(
202
368M
        &self,
203
368M
        bit_reader: &mut BitReader<R>,
204
368M
    ) -> Result<u16, DecodingError> {
205
368M
        match &self.0 {
206
            HuffmanTreeInner::Tree {
207
60.7M
                tree,
208
60.7M
                table,
209
60.7M
                table_mask,
210
            } => {
211
60.7M
                let v = bit_reader.peek_full() as u16;
212
60.7M
                let entry = table[(v & table_mask) as usize];
213
60.7M
                if entry >> 16 != 0 {
214
59.9M
                    bit_reader.consume((entry >> 16) as u8)?;
215
59.9M
                    return Ok(entry as u16);
216
775k
                }
217
218
775k
                Self::read_symbol_slowpath(
219
775k
                    tree,
220
775k
                    (v >> MAX_TABLE_BITS) as usize,
221
775k
                    ((entry & 0xffff) - 1) as usize,
222
775k
                    bit_reader,
223
                )
224
            }
225
308M
            HuffmanTreeInner::Single(symbol) => Ok(*symbol),
226
        }
227
368M
    }
<image_webp::huffman::HuffmanTree>::read_symbol::<&mut std::io::Take<&mut std::io::cursor::Cursor<&[u8]>>>
Line
Count
Source
201
32.2M
    pub(crate) fn read_symbol<R: BufRead>(
202
32.2M
        &self,
203
32.2M
        bit_reader: &mut BitReader<R>,
204
32.2M
    ) -> Result<u16, DecodingError> {
205
32.2M
        match &self.0 {
206
            HuffmanTreeInner::Tree {
207
10.6M
                tree,
208
10.6M
                table,
209
10.6M
                table_mask,
210
            } => {
211
10.6M
                let v = bit_reader.peek_full() as u16;
212
10.6M
                let entry = table[(v & table_mask) as usize];
213
10.6M
                if entry >> 16 != 0 {
214
10.6M
                    bit_reader.consume((entry >> 16) as u8)?;
215
10.6M
                    return Ok(entry as u16);
216
7.01k
                }
217
218
7.01k
                Self::read_symbol_slowpath(
219
7.01k
                    tree,
220
7.01k
                    (v >> MAX_TABLE_BITS) as usize,
221
7.01k
                    ((entry & 0xffff) - 1) as usize,
222
7.01k
                    bit_reader,
223
                )
224
            }
225
21.5M
            HuffmanTreeInner::Single(symbol) => Ok(*symbol),
226
        }
227
32.2M
    }
Unexecuted instantiation: <image_webp::huffman::HuffmanTree>::read_symbol::<_>
228
229
    /// Peek at the next symbol in the bitstream if it can be read with only a primary table lookup.
230
    ///
231
    /// Returns a tuple of the codelength and symbol value. This function may return wrong
232
    /// information if there aren't enough bits in the bit reader to read the next symbol.
233
81.3M
    pub(crate) fn peek_symbol<R: BufRead>(&self, bit_reader: &BitReader<R>) -> Option<(u8, u16)> {
234
81.3M
        match &self.0 {
235
            HuffmanTreeInner::Tree {
236
1.97M
                table, table_mask, ..
237
            } => {
238
1.97M
                let v = bit_reader.peek_full() as u16;
239
1.97M
                let entry = table[(v & table_mask) as usize];
240
1.97M
                if entry >> 16 != 0 {
241
1.97M
                    return Some(((entry >> 16) as u8, entry as u16));
242
542
                }
243
542
                None
244
            }
245
79.3M
            HuffmanTreeInner::Single(symbol) => Some((0, *symbol)),
246
        }
247
81.3M
    }
<image_webp::huffman::HuffmanTree>::peek_symbol::<std::io::Take<&mut std::io::cursor::Cursor<&[u8]>>>
Line
Count
Source
233
79.5M
    pub(crate) fn peek_symbol<R: BufRead>(&self, bit_reader: &BitReader<R>) -> Option<(u8, u16)> {
234
79.5M
        match &self.0 {
235
            HuffmanTreeInner::Tree {
236
862k
                table, table_mask, ..
237
            } => {
238
862k
                let v = bit_reader.peek_full() as u16;
239
862k
                let entry = table[(v & table_mask) as usize];
240
862k
                if entry >> 16 != 0 {
241
861k
                    return Some(((entry >> 16) as u8, entry as u16));
242
511
                }
243
511
                None
244
            }
245
78.6M
            HuffmanTreeInner::Single(symbol) => Some((0, *symbol)),
246
        }
247
79.5M
    }
<image_webp::huffman::HuffmanTree>::peek_symbol::<&mut std::io::Take<&mut std::io::cursor::Cursor<&[u8]>>>
Line
Count
Source
233
1.80M
    pub(crate) fn peek_symbol<R: BufRead>(&self, bit_reader: &BitReader<R>) -> Option<(u8, u16)> {
234
1.80M
        match &self.0 {
235
            HuffmanTreeInner::Tree {
236
1.11M
                table, table_mask, ..
237
            } => {
238
1.11M
                let v = bit_reader.peek_full() as u16;
239
1.11M
                let entry = table[(v & table_mask) as usize];
240
1.11M
                if entry >> 16 != 0 {
241
1.11M
                    return Some(((entry >> 16) as u8, entry as u16));
242
31
                }
243
31
                None
244
            }
245
686k
            HuffmanTreeInner::Single(symbol) => Some((0, *symbol)),
246
        }
247
1.80M
    }
Unexecuted instantiation: <image_webp::huffman::HuffmanTree>::peek_symbol::<_>
248
}