Coverage Report

Created: 2026-07-25 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mp4san/webpsan/src/parse/bitstream.rs
Line
Count
Source
1
#![allow(missing_docs)]
2
3
use std::fmt::Debug;
4
use std::io::{Cursor, Read};
5
use std::mem::replace;
6
use std::num::NonZeroU32;
7
8
use bitstream_io::huffman::{compile_read_tree, ReadHuffmanTree};
9
use bitstream_io::{BitRead, BitReader, Endianness, HuffmanRead, Numeric};
10
use derive_more::Display;
11
use mediasan_common::util::IoResultExt;
12
use mediasan_common::{bail_attach, report_attach};
13
14
use crate::parse::ParseError;
15
use crate::Error;
16
17
pub struct BitBufReader<R, E: Endianness> {
18
    input: Option<R>,
19
    reader: BitReader<Cursor<Vec<u8>>, E>,
20
    buf_len: usize,
21
}
22
23
pub struct CanonicalHuffmanTree<E: Endianness, S: Clone> {
24
    read_tree: Box<[ReadHuffmanTree<E, S>]>,
25
    longest_code_len: u32,
26
}
27
28
#[derive(Display)]
29
#[display("invalid lz77 prefix code `{_0}`")]
30
struct InvalidLz77PrefixCode(u16);
31
32
pub const LZ77_MAX_LEN: u16 = (LZ77_MAX_SYMBOL - 2) >> 1;
33
34
const LZ77_MAX_SYMBOL: u16 = 39;
35
36
//
37
// BitBufReader impls
38
//
39
40
impl<R: Read, E: Endianness> BitBufReader<R, E> {
41
5.67k
    pub fn with_capacity(input: R, capacity: usize) -> Self {
42
5.67k
        Self { input: Some(input), reader: BitReader::new(Cursor::new(Vec::with_capacity(capacity))), buf_len: 0 }
43
5.67k
    }
44
45
30.7k
    pub fn fill_buf(&mut self) -> Result<(), Error> {
46
30.7k
        let bit_pos = self.buf_bit_pos();
47
30.7k
        let byte_pos = (bit_pos / 8) as usize;
48
49
30.7k
        let Some(input) = self.input.as_mut() else {
50
14.7k
            return Ok(());
51
        };
52
53
16.0k
        let reader = replace(&mut self.reader, BitReader::new(Cursor::new(Vec::new())));
54
16.0k
        let mut buf = reader.into_reader().into_inner();
55
56
16.0k
        buf.drain(..byte_pos);
57
16.0k
        input.take((buf.capacity() - buf.len()) as u64).read_to_end(&mut buf)?;
58
16.0k
        if self.buf_len - byte_pos == buf.len() {
59
3.47k
            self.input = None;
60
12.6k
        }
61
16.0k
        self.buf_len = buf.len();
62
63
16.0k
        self.reader = BitReader::new(Cursor::new(buf));
64
16.0k
        self.reader.skip((bit_pos % 8) as u32)?;
65
16.0k
        Ok(())
66
30.7k
    }
67
68
69.9M
    pub fn buf_bits(&mut self) -> u64 {
69
69.9M
        self.buf_len as u64 * 8 - self.buf_bit_pos()
70
69.9M
    }
71
72
6.99M
    pub fn buf_read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
73
6.99M
        self.reader
74
6.99M
            .read(bits)
75
6.99M
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read::<u8>::{closure#0}
Line
Count
Source
75
959
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read::<u32>::{closure#0}
Line
Count
Source
75
426
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read::<u16>::{closure#0}
Line
Count
Source
75
247
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
76
6.99M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read::<u8>
Line
Count
Source
72
1.20M
    pub fn buf_read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
73
1.20M
        self.reader
74
1.20M
            .read(bits)
75
1.20M
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
76
1.20M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read::<u32>
Line
Count
Source
72
5.60M
    pub fn buf_read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
73
5.60M
        self.reader
74
5.60M
            .read(bits)
75
5.60M
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
76
5.60M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read::<u16>
Line
Count
Source
72
194k
    pub fn buf_read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
73
194k
        self.reader
74
194k
            .read(bits)
75
194k
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
76
194k
    }
77
78
3.38M
    pub fn buf_read_bit(&mut self) -> Result<bool, Error> {
79
3.38M
        self.reader
80
3.38M
            .read_bit()
81
3.38M
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
82
3.38M
    }
83
84
233M
    pub fn buf_read_huffman<T: Clone>(&mut self, tree: &CanonicalHuffmanTree<E, T>) -> Result<T, Error> {
85
233M
        self.reader
86
233M
            .read_huffman(&tree.read_tree)
87
233M
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read_huffman::<u8>::{closure#0}
Line
Count
Source
87
380
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read_huffman::<u16>::{closure#0}
Line
Count
Source
87
517
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
88
233M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read_huffman::<u8>
Line
Count
Source
84
169M
    pub fn buf_read_huffman<T: Clone>(&mut self, tree: &CanonicalHuffmanTree<E, T>) -> Result<T, Error> {
85
169M
        self.reader
86
169M
            .read_huffman(&tree.read_tree)
87
169M
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
88
169M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read_huffman::<u16>
Line
Count
Source
84
64.5M
    pub fn buf_read_huffman<T: Clone>(&mut self, tree: &CanonicalHuffmanTree<E, T>) -> Result<T, Error> {
85
64.5M
        self.reader
86
64.5M
            .read_huffman(&tree.read_tree)
87
64.5M
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
88
64.5M
    }
89
90
14.8M
    pub fn buf_read_lz77(&mut self, prefix_code: u16) -> Result<NonZeroU32, Error> {
91
14.8M
        match prefix_code {
92
14.8M
            0..=3 => Ok(NonZeroU32::MIN.saturating_add(prefix_code.into())),
93
5.51M
            4..=LZ77_MAX_SYMBOL => {
94
5.51M
                let extra_bits = (u32::from(prefix_code) - 2) >> 1;
95
5.51M
                let offset = (2 + (u32::from(prefix_code) & 1)) << extra_bits;
96
5.51M
                Ok(NonZeroU32::MIN.saturating_add(offset + self.buf_read::<u32>(extra_bits)?))
97
            }
98
11
            _ => bail_attach!(ParseError::InvalidInput, InvalidLz77PrefixCode(prefix_code)),
99
        }
100
14.8M
    }
101
102
1.48M
    pub fn read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
103
1.48M
        if self.buf_bits() < bits.into() {
104
1.44k
            self.fill_buf()?;
105
1.48M
        }
106
1.48M
        self.buf_read(bits)
107
1.48M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::read::<u8>
Line
Count
Source
102
1.20M
    pub fn read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
103
1.20M
        if self.buf_bits() < bits.into() {
104
1.06k
            self.fill_buf()?;
105
1.19M
        }
106
1.20M
        self.buf_read(bits)
107
1.20M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::read::<u32>
Line
Count
Source
102
88.9k
    pub fn read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
103
88.9k
        if self.buf_bits() < bits.into() {
104
81
            self.fill_buf()?;
105
88.8k
        }
106
88.9k
        self.buf_read(bits)
107
88.9k
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::read::<u16>
Line
Count
Source
102
194k
    pub fn read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
103
194k
        if self.buf_bits() < bits.into() {
104
304
            self.fill_buf()?;
105
194k
        }
106
194k
        self.buf_read(bits)
107
194k
    }
108
109
3.38M
    pub fn read_bit(&mut self) -> Result<bool, Error> {
110
3.38M
        if self.buf_bits() < 1 {
111
6.30k
            self.fill_buf()?;
112
3.38M
        }
113
3.38M
        self.buf_read_bit()
114
3.38M
    }
115
116
508k
    pub fn read_huffman<T: Clone>(&mut self, tree: &CanonicalHuffmanTree<E, T>) -> Result<T, Error> {
117
508k
        if self.buf_bits() < tree.longest_code_len.into() {
118
428
            self.fill_buf()?;
119
507k
        }
120
508k
        self.buf_read_huffman(tree)
121
508k
    }
122
123
69.9M
    fn buf_bit_pos(&mut self) -> u64 {
124
69.9M
        self.reader.position_in_bits().unwrap_or_else(|_| unreachable!())
125
69.9M
    }
126
}
127
128
//
129
// CanonicalHuffmanTree impls
130
//
131
132
impl<E: Endianness, S: Clone> CanonicalHuffmanTree<E, S> {
133
174k
    pub fn new(code_lengths: &mut [(S, u8)]) -> Result<Self, Error>
134
174k
    where
135
174k
        S: Copy + Debug + Ord + 'static,
136
    {
137
174k
        let symbols = Self::symbols(code_lengths);
138
174k
        log::debug!("symbols: {symbols:?}");
139
174k
        Self::from_symbols(symbols)
140
174k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u8>>::new
Line
Count
Source
133
156k
    pub fn new(code_lengths: &mut [(S, u8)]) -> Result<Self, Error>
134
156k
    where
135
156k
        S: Copy + Debug + Ord + 'static,
136
    {
137
156k
        let symbols = Self::symbols(code_lengths);
138
156k
        log::debug!("symbols: {symbols:?}");
139
156k
        Self::from_symbols(symbols)
140
156k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u16>>::new
Line
Count
Source
133
17.8k
    pub fn new(code_lengths: &mut [(S, u8)]) -> Result<Self, Error>
134
17.8k
    where
135
17.8k
        S: Copy + Debug + Ord + 'static,
136
    {
137
17.8k
        let symbols = Self::symbols(code_lengths);
138
17.8k
        log::debug!("symbols: {symbols:?}");
139
17.8k
        Self::from_symbols(symbols)
140
17.8k
    }
141
142
996k
    pub fn from_symbols(symbols: Vec<(S, Vec<u8>)>) -> Result<Self, Error> {
143
996k
        let longest_code_len = match &symbols[..] {
144
533k
            [_symbol] => 0,
145
1.51M
            _ => symbols.iter().map(|(_, code)| code.len()).max().unwrap_or_default() as u32,
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u8>>::from_symbols::{closure#0}
Line
Count
Source
145
1.11M
            _ => symbols.iter().map(|(_, code)| code.len()).max().unwrap_or_default() as u32,
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u16>>::from_symbols::{closure#0}
Line
Count
Source
145
400k
            _ => symbols.iter().map(|(_, code)| code.len()).max().unwrap_or_default() as u32,
146
        };
147
994k
        let read_tree =
148
996k
            compile_read_tree(symbols).map_err(|err| report_attach!(ParseError::InvalidVp8lPrefixCode, err))?;
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u8>>::from_symbols::{closure#1}
Line
Count
Source
148
918
            compile_read_tree(symbols).map_err(|err| report_attach!(ParseError::InvalidVp8lPrefixCode, err))?;
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u16>>::from_symbols::{closure#1}
Line
Count
Source
148
395
            compile_read_tree(symbols).map_err(|err| report_attach!(ParseError::InvalidVp8lPrefixCode, err))?;
149
994k
        Ok(Self { read_tree, longest_code_len })
150
996k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u8>>::from_symbols
Line
Count
Source
142
812k
    pub fn from_symbols(symbols: Vec<(S, Vec<u8>)>) -> Result<Self, Error> {
143
812k
        let longest_code_len = match &symbols[..] {
144
442k
            [_symbol] => 0,
145
370k
            _ => symbols.iter().map(|(_, code)| code.len()).max().unwrap_or_default() as u32,
146
        };
147
812k
        let read_tree =
148
812k
            compile_read_tree(symbols).map_err(|err| report_attach!(ParseError::InvalidVp8lPrefixCode, err))?;
149
812k
        Ok(Self { read_tree, longest_code_len })
150
812k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u16>>::from_symbols
Line
Count
Source
142
183k
    pub fn from_symbols(symbols: Vec<(S, Vec<u8>)>) -> Result<Self, Error> {
143
183k
        let longest_code_len = match &symbols[..] {
144
91.0k
            [_symbol] => 0,
145
92.0k
            _ => symbols.iter().map(|(_, code)| code.len()).max().unwrap_or_default() as u32,
146
        };
147
182k
        let read_tree =
148
183k
            compile_read_tree(symbols).map_err(|err| report_attach!(ParseError::InvalidVp8lPrefixCode, err))?;
149
182k
        Ok(Self { read_tree, longest_code_len })
150
183k
    }
151
152
    pub fn read_tree(&self) -> &[ReadHuffmanTree<E, S>] {
153
        &self.read_tree
154
    }
155
156
25.1k
    pub fn longest_code_len(&self) -> u32 {
157
25.1k
        self.longest_code_len
158
25.1k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u8>>::longest_code_len
Line
Count
Source
156
16.7k
    pub fn longest_code_len(&self) -> u32 {
157
16.7k
        self.longest_code_len
158
16.7k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u16>>::longest_code_len
Line
Count
Source
156
8.37k
    pub fn longest_code_len(&self) -> u32 {
157
8.37k
        self.longest_code_len
158
8.37k
    }
159
160
174k
    fn symbols(code_lengths: &mut [(S, u8)]) -> Vec<(S, Vec<u8>)>
161
174k
    where
162
174k
        S: Copy + Ord + 'static,
163
    {
164
76.8M
        code_lengths.sort_unstable_by_key(|&(symbol, code_length)| (code_length, symbol));
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u8>>::symbols::{closure#0}
Line
Count
Source
164
46.2M
        code_lengths.sort_unstable_by_key(|&(symbol, code_length)| (code_length, symbol));
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u16>>::symbols::{closure#0}
Line
Count
Source
164
30.5M
        code_lengths.sort_unstable_by_key(|&(symbol, code_length)| (code_length, symbol));
165
1.03M
        let zero_code_length_count = code_lengths.partition_point(|&(_, code_length)| code_length == 0);
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u8>>::symbols::{closure#1}
Line
Count
Source
165
921k
        let zero_code_length_count = code_lengths.partition_point(|&(_, code_length)| code_length == 0);
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u16>>::symbols::{closure#1}
Line
Count
Source
165
109k
        let zero_code_length_count = code_lengths.partition_point(|&(_, code_length)| code_length == 0);
166
167
174k
        match (&code_lengths[zero_code_length_count..], &*code_lengths) {
168
45.9k
            (&[(first_symbol, 1)], _) => vec![(first_symbol, vec![])],
169
170
128k
            (&[(first_symbol, first_code_length), ref rest_code_lengths @ ..], &[.., (_, last_code_length)]) => {
171
128k
                let mut code = Vec::with_capacity(last_code_length.into());
172
128k
                code.resize(first_code_length.into(), 0);
173
174
128k
                let mut symbols = Vec::with_capacity(code_lengths.len());
175
128k
                symbols.push((first_symbol, code.clone()));
176
850k
                for &(symbol, code_length) in rest_code_lengths {
177
1.11M
                    for code_bit in code.iter_mut().rev() {
178
1.11M
                        *code_bit ^= 1;
179
1.11M
                        if *code_bit == 1 {
180
711k
                            break;
181
406k
                        }
182
                    }
183
722k
                    code.resize(code_length.into(), 0);
184
722k
                    symbols.push((symbol, code.clone()));
185
                }
186
128k
                symbols
187
            }
188
66
            _ => vec![],
189
        }
190
174k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u8>>::symbols
Line
Count
Source
160
156k
    fn symbols(code_lengths: &mut [(S, u8)]) -> Vec<(S, Vec<u8>)>
161
156k
    where
162
156k
        S: Copy + Ord + 'static,
163
    {
164
156k
        code_lengths.sort_unstable_by_key(|&(symbol, code_length)| (code_length, symbol));
165
156k
        let zero_code_length_count = code_lengths.partition_point(|&(_, code_length)| code_length == 0);
166
167
156k
        match (&code_lengths[zero_code_length_count..], &*code_lengths) {
168
40.3k
            (&[(first_symbol, 1)], _) => vec![(first_symbol, vec![])],
169
170
115k
            (&[(first_symbol, first_code_length), ref rest_code_lengths @ ..], &[.., (_, last_code_length)]) => {
171
115k
                let mut code = Vec::with_capacity(last_code_length.into());
172
115k
                code.resize(first_code_length.into(), 0);
173
174
115k
                let mut symbols = Vec::with_capacity(code_lengths.len());
175
115k
                symbols.push((first_symbol, code.clone()));
176
610k
                for &(symbol, code_length) in rest_code_lengths {
177
707k
                    for code_bit in code.iter_mut().rev() {
178
707k
                        *code_bit ^= 1;
179
707k
                        if *code_bit == 1 {
180
491k
                            break;
181
215k
                        }
182
                    }
183
494k
                    code.resize(code_length.into(), 0);
184
494k
                    symbols.push((symbol, code.clone()));
185
                }
186
115k
                symbols
187
            }
188
53
            _ => vec![],
189
        }
190
156k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u16>>::symbols
Line
Count
Source
160
17.8k
    fn symbols(code_lengths: &mut [(S, u8)]) -> Vec<(S, Vec<u8>)>
161
17.8k
    where
162
17.8k
        S: Copy + Ord + 'static,
163
    {
164
17.8k
        code_lengths.sort_unstable_by_key(|&(symbol, code_length)| (code_length, symbol));
165
17.8k
        let zero_code_length_count = code_lengths.partition_point(|&(_, code_length)| code_length == 0);
166
167
17.8k
        match (&code_lengths[zero_code_length_count..], &*code_lengths) {
168
5.59k
            (&[(first_symbol, 1)], _) => vec![(first_symbol, vec![])],
169
170
12.2k
            (&[(first_symbol, first_code_length), ref rest_code_lengths @ ..], &[.., (_, last_code_length)]) => {
171
12.2k
                let mut code = Vec::with_capacity(last_code_length.into());
172
12.2k
                code.resize(first_code_length.into(), 0);
173
174
12.2k
                let mut symbols = Vec::with_capacity(code_lengths.len());
175
12.2k
                symbols.push((first_symbol, code.clone()));
176
240k
                for &(symbol, code_length) in rest_code_lengths {
177
410k
                    for code_bit in code.iter_mut().rev() {
178
410k
                        *code_bit ^= 1;
179
410k
                        if *code_bit == 1 {
180
219k
                            break;
181
191k
                        }
182
                    }
183
228k
                    code.resize(code_length.into(), 0);
184
228k
                    symbols.push((symbol, code.clone()));
185
                }
186
12.2k
                symbols
187
            }
188
13
            _ => vec![],
189
        }
190
17.8k
    }
191
}
192
193
impl<E: Endianness, S: Clone + Default> Default for CanonicalHuffmanTree<E, S> {
194
    fn default() -> Self {
195
        Self::from_symbols(vec![(S::default(), vec![])]).unwrap_or_else(|_| unreachable!())
196
    }
197
}