Coverage Report

Created: 2025-12-14 07:56

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
69.2k
    fn default() -> Self {
36
69.2k
        Self(HuffmanTreeInner::Single(0))
37
69.2k
    }
38
}
39
40
impl HuffmanTree {
41
    /// Builds a tree implicitly, just from code lengths
42
21.7k
    pub(crate) fn build_implicit(code_lengths: Vec<u16>) -> Result<Self, DecodingError> {
43
        // Count symbols and build histogram
44
21.7k
        let mut num_symbols = 0;
45
21.7k
        let mut code_length_hist = [0; MAX_ALLOWED_CODE_LENGTH + 1];
46
3.04M
        for &length in code_lengths.iter().filter(|&&x| x != 0) {
47
1.02M
            code_length_hist[usize::from(length)] += 1;
48
1.02M
            num_symbols += 1;
49
1.02M
        }
50
51
        // Handle special cases
52
21.7k
        if num_symbols == 0 {
53
14
            return Err(DecodingError::HuffmanError);
54
21.7k
        } else if num_symbols == 1 {
55
3.42k
            let root_symbol = code_lengths.iter().position(|&x| x != 0).unwrap() as u16;
56
1.48k
            return Ok(Self::build_single_node(root_symbol));
57
20.2k
        };
58
59
        // Assign codes
60
20.2k
        let mut curr_code = 0;
61
20.2k
        let mut next_codes = [0; MAX_ALLOWED_CODE_LENGTH + 1];
62
192k
        let max_code_length = code_length_hist.iter().rposition(|&x| x != 0).unwrap() as u16;
63
130k
        for code_len in 1..usize::from(max_code_length) + 1 {
64
130k
            next_codes[code_len] = curr_code;
65
130k
            curr_code = (curr_code + code_length_hist[code_len]) << 1;
66
130k
        }
67
68
        // Confirm that the huffman tree is valid
69
20.2k
        if curr_code != 2 << max_code_length {
70
155
            return Err(DecodingError::HuffmanError);
71
20.0k
        }
72
73
        // Calculate table/tree parameters
74
20.0k
        let table_bits = max_code_length.min(u16::from(MAX_TABLE_BITS));
75
20.0k
        let table_size = (1 << table_bits) as usize;
76
20.0k
        let table_mask = table_size as u16 - 1;
77
20.0k
        let tree_size = code_length_hist[table_bits as usize + 1..=max_code_length as usize]
78
20.0k
            .iter()
79
20.0k
            .sum::<u16>() as usize;
80
81
        // Populate decoding table
82
20.0k
        let mut tree = Vec::with_capacity(2 * tree_size);
83
20.0k
        let mut table = vec![0; table_size];
84
2.99M
        for (symbol, &length) in code_lengths.iter().enumerate() {
85
2.99M
            if length == 0 {
86
1.97M
                continue;
87
1.01M
            }
88
89
1.01M
            let code = next_codes[length as usize];
90
1.01M
            next_codes[length as usize] += 1;
91
92
1.01M
            if length <= table_bits {
93
641k
                let mut j = (u16::reverse_bits(code) >> (16 - length)) as usize;
94
641k
                let entry = (u32::from(length) << 16) | symbol as u32;
95
6.29M
                while j < table_size {
96
5.65M
                    table[j] = entry;
97
5.65M
                    j += 1 << length as usize;
98
5.65M
                }
99
            } else {
100
377k
                let table_index =
101
377k
                    ((u16::reverse_bits(code) >> (16 - length)) & table_mask) as usize;
102
377k
                let table_value = table[table_index];
103
104
377k
                debug_assert_eq!(table_value >> 16, 0);
105
106
377k
                let mut node_index = if table_value == 0 {
107
77.0k
                    let node_index = tree.len();
108
77.0k
                    table[table_index] = (node_index + 1) as u32;
109
77.0k
                    tree.push(HuffmanTreeNode::Empty);
110
77.0k
                    node_index
111
                } else {
112
300k
                    (table_value - 1) as usize
113
                };
114
115
377k
                let code = usize::from(code);
116
1.07M
                for depth in (0..length - table_bits).rev() {
117
1.07M
                    let node = tree[node_index];
118
119
1.07M
                    let offset = match node {
120
                        HuffmanTreeNode::Empty => {
121
                            // Turns a node from empty into a branch and assigns its children
122
300k
                            let offset = tree.len() - node_index;
123
300k
                            tree[node_index] = HuffmanTreeNode::Branch(offset);
124
300k
                            tree.push(HuffmanTreeNode::Empty);
125
300k
                            tree.push(HuffmanTreeNode::Empty);
126
300k
                            offset
127
                        }
128
0
                        HuffmanTreeNode::Leaf(_) => return Err(DecodingError::HuffmanError),
129
773k
                        HuffmanTreeNode::Branch(offset) => offset,
130
                    };
131
132
1.07M
                    node_index += offset + ((code >> depth) & 1);
133
                }
134
135
377k
                match tree[node_index] {
136
377k
                    HuffmanTreeNode::Empty => {
137
377k
                        tree[node_index] = HuffmanTreeNode::Leaf(symbol as u16);
138
377k
                    }
139
0
                    HuffmanTreeNode::Leaf(_) => return Err(DecodingError::HuffmanError),
140
0
                    HuffmanTreeNode::Branch(_offset) => return Err(DecodingError::HuffmanError),
141
                }
142
            }
143
        }
144
145
20.0k
        Ok(Self(HuffmanTreeInner::Tree {
146
20.0k
            tree,
147
20.0k
            table,
148
20.0k
            table_mask,
149
20.0k
        }))
150
21.7k
    }
151
152
52.4k
    pub(crate) const fn build_single_node(symbol: u16) -> Self {
153
52.4k
        Self(HuffmanTreeInner::Single(symbol))
154
52.4k
    }
155
156
6.44k
    pub(crate) fn build_two_node(zero: u16, one: u16) -> Self {
157
6.44k
        Self(HuffmanTreeInner::Tree {
158
6.44k
            tree: vec![
159
6.44k
                HuffmanTreeNode::Leaf(zero),
160
6.44k
                HuffmanTreeNode::Leaf(one),
161
6.44k
                HuffmanTreeNode::Empty,
162
6.44k
            ],
163
6.44k
            table: vec![(1 << 16) | u32::from(zero), (1 << 16) | u32::from(one)],
164
6.44k
            table_mask: 0x1,
165
6.44k
        })
166
6.44k
    }
167
168
1.83M
    pub(crate) const fn is_single_node(&self) -> bool {
169
1.83M
        matches!(self.0, HuffmanTreeInner::Single(_))
170
1.83M
    }
171
172
    #[inline(never)]
173
1.20M
    fn read_symbol_slowpath<R: BufRead>(
174
1.20M
        tree: &[HuffmanTreeNode],
175
1.20M
        mut v: usize,
176
1.20M
        start_index: usize,
177
1.20M
        bit_reader: &mut BitReader<R>,
178
1.20M
    ) -> Result<u16, DecodingError> {
179
1.20M
        let mut depth = MAX_TABLE_BITS;
180
1.20M
        let mut index = start_index;
181
        loop {
182
3.84M
            match &tree[index] {
183
2.64M
                HuffmanTreeNode::Branch(children_offset) => {
184
2.64M
                    index += children_offset + (v & 1);
185
2.64M
                    depth += 1;
186
2.64M
                    v >>= 1;
187
2.64M
                }
188
1.20M
                HuffmanTreeNode::Leaf(symbol) => {
189
1.20M
                    bit_reader.consume(depth)?;
190
1.20M
                    return Ok(*symbol);
191
                }
192
0
                HuffmanTreeNode::Empty => return Err(DecodingError::HuffmanError),
193
            }
194
        }
195
1.20M
    }
<image_webp::huffman::HuffmanTree>::read_symbol_slowpath::<std::io::Take<&mut std::io::cursor::Cursor<&[u8]>>>
Line
Count
Source
173
1.19M
    fn read_symbol_slowpath<R: BufRead>(
174
1.19M
        tree: &[HuffmanTreeNode],
175
1.19M
        mut v: usize,
176
1.19M
        start_index: usize,
177
1.19M
        bit_reader: &mut BitReader<R>,
178
1.19M
    ) -> Result<u16, DecodingError> {
179
1.19M
        let mut depth = MAX_TABLE_BITS;
180
1.19M
        let mut index = start_index;
181
        loop {
182
3.82M
            match &tree[index] {
183
2.63M
                HuffmanTreeNode::Branch(children_offset) => {
184
2.63M
                    index += children_offset + (v & 1);
185
2.63M
                    depth += 1;
186
2.63M
                    v >>= 1;
187
2.63M
                }
188
1.19M
                HuffmanTreeNode::Leaf(symbol) => {
189
1.19M
                    bit_reader.consume(depth)?;
190
1.19M
                    return Ok(*symbol);
191
                }
192
0
                HuffmanTreeNode::Empty => return Err(DecodingError::HuffmanError),
193
            }
194
        }
195
1.19M
    }
<image_webp::huffman::HuffmanTree>::read_symbol_slowpath::<&mut std::io::Take<&mut std::io::cursor::Cursor<&[u8]>>>
Line
Count
Source
173
7.82k
    fn read_symbol_slowpath<R: BufRead>(
174
7.82k
        tree: &[HuffmanTreeNode],
175
7.82k
        mut v: usize,
176
7.82k
        start_index: usize,
177
7.82k
        bit_reader: &mut BitReader<R>,
178
7.82k
    ) -> Result<u16, DecodingError> {
179
7.82k
        let mut depth = MAX_TABLE_BITS;
180
7.82k
        let mut index = start_index;
181
        loop {
182
18.3k
            match &tree[index] {
183
10.4k
                HuffmanTreeNode::Branch(children_offset) => {
184
10.4k
                    index += children_offset + (v & 1);
185
10.4k
                    depth += 1;
186
10.4k
                    v >>= 1;
187
10.4k
                }
188
7.82k
                HuffmanTreeNode::Leaf(symbol) => {
189
7.82k
                    bit_reader.consume(depth)?;
190
7.82k
                    return Ok(*symbol);
191
                }
192
0
                HuffmanTreeNode::Empty => return Err(DecodingError::HuffmanError),
193
            }
194
        }
195
7.82k
    }
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
107M
    pub(crate) fn read_symbol<R: BufRead>(
202
107M
        &self,
203
107M
        bit_reader: &mut BitReader<R>,
204
107M
    ) -> Result<u16, DecodingError> {
205
107M
        match &self.0 {
206
            HuffmanTreeInner::Tree {
207
98.6M
                tree,
208
98.6M
                table,
209
98.6M
                table_mask,
210
            } => {
211
98.6M
                let v = bit_reader.peek_full() as u16;
212
98.6M
                let entry = table[(v & table_mask) as usize];
213
98.6M
                if entry >> 16 != 0 {
214
97.4M
                    bit_reader.consume((entry >> 16) as u8)?;
215
97.4M
                    return Ok(entry as u16);
216
1.20M
                }
217
218
1.20M
                Self::read_symbol_slowpath(
219
1.20M
                    tree,
220
1.20M
                    (v >> MAX_TABLE_BITS) as usize,
221
1.20M
                    ((entry & 0xffff) - 1) as usize,
222
1.20M
                    bit_reader,
223
                )
224
            }
225
8.85M
            HuffmanTreeInner::Single(symbol) => Ok(*symbol),
226
        }
227
107M
    }
<image_webp::huffman::HuffmanTree>::read_symbol::<std::io::Take<&mut std::io::cursor::Cursor<&[u8]>>>
Line
Count
Source
201
78.6M
    pub(crate) fn read_symbol<R: BufRead>(
202
78.6M
        &self,
203
78.6M
        bit_reader: &mut BitReader<R>,
204
78.6M
    ) -> Result<u16, DecodingError> {
205
78.6M
        match &self.0 {
206
            HuffmanTreeInner::Tree {
207
76.9M
                tree,
208
76.9M
                table,
209
76.9M
                table_mask,
210
            } => {
211
76.9M
                let v = bit_reader.peek_full() as u16;
212
76.9M
                let entry = table[(v & table_mask) as usize];
213
76.9M
                if entry >> 16 != 0 {
214
75.8M
                    bit_reader.consume((entry >> 16) as u8)?;
215
75.8M
                    return Ok(entry as u16);
216
1.19M
                }
217
218
1.19M
                Self::read_symbol_slowpath(
219
1.19M
                    tree,
220
1.19M
                    (v >> MAX_TABLE_BITS) as usize,
221
1.19M
                    ((entry & 0xffff) - 1) as usize,
222
1.19M
                    bit_reader,
223
                )
224
            }
225
1.63M
            HuffmanTreeInner::Single(symbol) => Ok(*symbol),
226
        }
227
78.6M
    }
<image_webp::huffman::HuffmanTree>::read_symbol::<&mut std::io::Take<&mut std::io::cursor::Cursor<&[u8]>>>
Line
Count
Source
201
28.8M
    pub(crate) fn read_symbol<R: BufRead>(
202
28.8M
        &self,
203
28.8M
        bit_reader: &mut BitReader<R>,
204
28.8M
    ) -> Result<u16, DecodingError> {
205
28.8M
        match &self.0 {
206
            HuffmanTreeInner::Tree {
207
21.6M
                tree,
208
21.6M
                table,
209
21.6M
                table_mask,
210
            } => {
211
21.6M
                let v = bit_reader.peek_full() as u16;
212
21.6M
                let entry = table[(v & table_mask) as usize];
213
21.6M
                if entry >> 16 != 0 {
214
21.6M
                    bit_reader.consume((entry >> 16) as u8)?;
215
21.6M
                    return Ok(entry as u16);
216
7.82k
                }
217
218
7.82k
                Self::read_symbol_slowpath(
219
7.82k
                    tree,
220
7.82k
                    (v >> MAX_TABLE_BITS) as usize,
221
7.82k
                    ((entry & 0xffff) - 1) as usize,
222
7.82k
                    bit_reader,
223
                )
224
            }
225
7.21M
            HuffmanTreeInner::Single(symbol) => Ok(*symbol),
226
        }
227
28.8M
    }
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
3.66M
    pub(crate) fn peek_symbol<R: BufRead>(&self, bit_reader: &BitReader<R>) -> Option<(u8, u16)> {
234
3.66M
        match &self.0 {
235
            HuffmanTreeInner::Tree {
236
3.66M
                table, table_mask, ..
237
            } => {
238
3.66M
                let v = bit_reader.peek_full() as u16;
239
3.66M
                let entry = table[(v & table_mask) as usize];
240
3.66M
                if entry >> 16 != 0 {
241
3.66M
                    return Some(((entry >> 16) as u8, entry as u16));
242
332
                }
243
332
                None
244
            }
245
0
            HuffmanTreeInner::Single(symbol) => Some((0, *symbol)),
246
        }
247
3.66M
    }
<image_webp::huffman::HuffmanTree>::peek_symbol::<std::io::Take<&mut std::io::cursor::Cursor<&[u8]>>>
Line
Count
Source
233
1.10M
    pub(crate) fn peek_symbol<R: BufRead>(&self, bit_reader: &BitReader<R>) -> Option<(u8, u16)> {
234
1.10M
        match &self.0 {
235
            HuffmanTreeInner::Tree {
236
1.10M
                table, table_mask, ..
237
            } => {
238
1.10M
                let v = bit_reader.peek_full() as u16;
239
1.10M
                let entry = table[(v & table_mask) as usize];
240
1.10M
                if entry >> 16 != 0 {
241
1.10M
                    return Some(((entry >> 16) as u8, entry as u16));
242
322
                }
243
322
                None
244
            }
245
0
            HuffmanTreeInner::Single(symbol) => Some((0, *symbol)),
246
        }
247
1.10M
    }
<image_webp::huffman::HuffmanTree>::peek_symbol::<&mut std::io::Take<&mut std::io::cursor::Cursor<&[u8]>>>
Line
Count
Source
233
2.55M
    pub(crate) fn peek_symbol<R: BufRead>(&self, bit_reader: &BitReader<R>) -> Option<(u8, u16)> {
234
2.55M
        match &self.0 {
235
            HuffmanTreeInner::Tree {
236
2.55M
                table, table_mask, ..
237
            } => {
238
2.55M
                let v = bit_reader.peek_full() as u16;
239
2.55M
                let entry = table[(v & table_mask) as usize];
240
2.55M
                if entry >> 16 != 0 {
241
2.55M
                    return Some(((entry >> 16) as u8, entry as u16));
242
10
                }
243
10
                None
244
            }
245
0
            HuffmanTreeInner::Single(symbol) => Some((0, *symbol)),
246
        }
247
2.55M
    }
Unexecuted instantiation: <image_webp::huffman::HuffmanTree>::peek_symbol::<_>
248
}