Coverage Report

Created: 2025-12-03 06:35

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
27.2k
    pub fn fill_buf(&mut self) -> Result<(), Error> {
46
27.2k
        let bit_pos = self.buf_bit_pos();
47
27.2k
        let byte_pos = (bit_pos / 8) as usize;
48
49
27.2k
        let Some(input) = self.input.as_mut() else {
50
12.8k
            return Ok(());
51
        };
52
53
14.3k
        let reader = replace(&mut self.reader, BitReader::new(Cursor::new(Vec::new())));
54
14.3k
        let mut buf = reader.into_reader().into_inner();
55
56
14.3k
        buf.drain(..byte_pos);
57
14.3k
        input.take((buf.capacity() - buf.len()) as u64).read_to_end(&mut buf)?;
58
14.3k
        if self.buf_len - byte_pos == buf.len() {
59
3.62k
            self.input = None;
60
10.7k
        }
61
14.3k
        self.buf_len = buf.len();
62
63
14.3k
        self.reader = BitReader::new(Cursor::new(buf));
64
14.3k
        self.reader.skip((bit_pos % 8) as u32)?;
65
14.3k
        Ok(())
66
27.2k
    }
67
68
56.1M
    pub fn buf_bits(&mut self) -> u64 {
69
56.1M
        self.buf_len as u64 * 8 - self.buf_bit_pos()
70
56.1M
    }
71
72
4.90M
    pub fn buf_read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
73
4.90M
        self.reader
74
4.90M
            .read(bits)
75
4.90M
            .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
898
            .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
373
            .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
260
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
76
4.90M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read::<u8>
Line
Count
Source
72
1.68M
    pub fn buf_read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
73
1.68M
        self.reader
74
1.68M
            .read(bits)
75
1.68M
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
76
1.68M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read::<u32>
Line
Count
Source
72
2.93M
    pub fn buf_read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
73
2.93M
        self.reader
74
2.93M
            .read(bits)
75
2.93M
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
76
2.93M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read::<u16>
Line
Count
Source
72
274k
    pub fn buf_read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
73
274k
        self.reader
74
274k
            .read(bits)
75
274k
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
76
274k
    }
77
78
4.27M
    pub fn buf_read_bit(&mut self) -> Result<bool, Error> {
79
4.27M
        self.reader
80
4.27M
            .read_bit()
81
4.27M
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
82
4.27M
    }
83
84
181M
    pub fn buf_read_huffman<T: Clone>(&mut self, tree: &CanonicalHuffmanTree<E, T>) -> Result<T, Error> {
85
181M
        self.reader
86
181M
            .read_huffman(&tree.read_tree)
87
181M
            .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
355
            .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
582
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
88
181M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read_huffman::<u8>
Line
Count
Source
84
133M
    pub fn buf_read_huffman<T: Clone>(&mut self, tree: &CanonicalHuffmanTree<E, T>) -> Result<T, Error> {
85
133M
        self.reader
86
133M
            .read_huffman(&tree.read_tree)
87
133M
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
88
133M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::buf_read_huffman::<u16>
Line
Count
Source
84
48.5M
    pub fn buf_read_huffman<T: Clone>(&mut self, tree: &CanonicalHuffmanTree<E, T>) -> Result<T, Error> {
85
48.5M
        self.reader
86
48.5M
            .read_huffman(&tree.read_tree)
87
48.5M
            .map_eof(|_| Error::Parse(report_attach!(ParseError::TruncatedChunk)))
88
48.5M
    }
89
90
8.20M
    pub fn buf_read_lz77(&mut self, prefix_code: u16) -> Result<NonZeroU32, Error> {
91
8.20M
        match prefix_code {
92
8.20M
            0..=3 => Ok(NonZeroU32::MIN.saturating_add(prefix_code.into())),
93
2.75M
            4..=LZ77_MAX_SYMBOL => {
94
2.75M
                let extra_bits = (u32::from(prefix_code) - 2) >> 1;
95
2.75M
                let offset = (2 + (u32::from(prefix_code) & 1)) << extra_bits;
96
2.75M
                Ok(NonZeroU32::MIN.saturating_add(offset + self.buf_read::<u32>(extra_bits)?))
97
            }
98
4
            _ => bail_attach!(ParseError::InvalidInput, InvalidLz77PrefixCode(prefix_code)),
99
        }
100
8.20M
    }
101
102
2.14M
    pub fn read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
103
2.14M
        if self.buf_bits() < bits.into() {
104
1.48k
            self.fill_buf()?;
105
2.14M
        }
106
2.14M
        self.buf_read(bits)
107
2.14M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::read::<u8>
Line
Count
Source
102
1.68M
    pub fn read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
103
1.68M
        if self.buf_bits() < bits.into() {
104
1.07k
            self.fill_buf()?;
105
1.68M
        }
106
1.68M
        self.buf_read(bits)
107
1.68M
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::read::<u32>
Line
Count
Source
102
179k
    pub fn read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
103
179k
        if self.buf_bits() < bits.into() {
104
76
            self.fill_buf()?;
105
179k
        }
106
179k
        self.buf_read(bits)
107
179k
    }
<webpsan::parse::bitstream::BitBufReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>, bitstream_io::LittleEndian>>::read::<u16>
Line
Count
Source
102
274k
    pub fn read<T: Numeric>(&mut self, bits: u32) -> Result<T, Error> {
103
274k
        if self.buf_bits() < bits.into() {
104
340
            self.fill_buf()?;
105
273k
        }
106
274k
        self.buf_read(bits)
107
274k
    }
108
109
4.27M
    pub fn read_bit(&mut self) -> Result<bool, Error> {
110
4.27M
        if self.buf_bits() < 1 {
111
6.49k
            self.fill_buf()?;
112
4.27M
        }
113
4.27M
        self.buf_read_bit()
114
4.27M
    }
115
116
1.14M
    pub fn read_huffman<T: Clone>(&mut self, tree: &CanonicalHuffmanTree<E, T>) -> Result<T, Error> {
117
1.14M
        if self.buf_bits() < tree.longest_code_len.into() {
118
565
            self.fill_buf()?;
119
1.14M
        }
120
1.14M
        self.buf_read_huffman(tree)
121
1.14M
    }
122
123
56.1M
    fn buf_bit_pos(&mut self) -> u64 {
124
56.1M
        self.reader.position_in_bits().unwrap_or_else(|_| unreachable!())
125
56.1M
    }
126
}
127
128
//
129
// CanonicalHuffmanTree impls
130
//
131
132
impl<E: Endianness, S: Clone> CanonicalHuffmanTree<E, S> {
133
358k
    pub fn new(code_lengths: &mut [(S, u8)]) -> Result<Self, Error>
134
358k
    where
135
358k
        S: Copy + Debug + Ord + 'static,
136
    {
137
358k
        let symbols = Self::symbols(code_lengths);
138
358k
        log::debug!("symbols: {symbols:?}");
139
358k
        Self::from_symbols(symbols)
140
358k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u8>>::new
Line
Count
Source
133
318k
    pub fn new(code_lengths: &mut [(S, u8)]) -> Result<Self, Error>
134
318k
    where
135
318k
        S: Copy + Debug + Ord + 'static,
136
    {
137
318k
        let symbols = Self::symbols(code_lengths);
138
318k
        log::debug!("symbols: {symbols:?}");
139
318k
        Self::from_symbols(symbols)
140
318k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u16>>::new
Line
Count
Source
133
40.5k
    pub fn new(code_lengths: &mut [(S, u8)]) -> Result<Self, Error>
134
40.5k
    where
135
40.5k
        S: Copy + Debug + Ord + 'static,
136
    {
137
40.5k
        let symbols = Self::symbols(code_lengths);
138
40.5k
        log::debug!("symbols: {symbols:?}");
139
40.5k
        Self::from_symbols(symbols)
140
40.5k
    }
141
142
1.35M
    pub fn from_symbols(symbols: Vec<(S, Vec<u8>)>) -> Result<Self, Error> {
143
1.35M
        let longest_code_len = match &symbols[..] {
144
604k
            [_symbol] => 0,
145
2.78M
            _ => 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
2.23M
            _ => 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
542k
            _ => symbols.iter().map(|(_, code)| code.len()).max().unwrap_or_default() as u32,
146
        };
147
1.35M
        let read_tree =
148
1.35M
            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
895
            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
425
            compile_read_tree(symbols).map_err(|err| report_attach!(ParseError::InvalidVp8lPrefixCode, err))?;
149
1.35M
        Ok(Self { read_tree, longest_code_len })
150
1.35M
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u8>>::from_symbols
Line
Count
Source
142
1.11M
    pub fn from_symbols(symbols: Vec<(S, Vec<u8>)>) -> Result<Self, Error> {
143
1.11M
        let longest_code_len = match &symbols[..] {
144
488k
            [_symbol] => 0,
145
627k
            _ => symbols.iter().map(|(_, code)| code.len()).max().unwrap_or_default() as u32,
146
        };
147
1.11M
        let read_tree =
148
1.11M
            compile_read_tree(symbols).map_err(|err| report_attach!(ParseError::InvalidVp8lPrefixCode, err))?;
149
1.11M
        Ok(Self { read_tree, longest_code_len })
150
1.11M
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u16>>::from_symbols
Line
Count
Source
142
236k
    pub fn from_symbols(symbols: Vec<(S, Vec<u8>)>) -> Result<Self, Error> {
143
236k
        let longest_code_len = match &symbols[..] {
144
116k
            [_symbol] => 0,
145
119k
            _ => symbols.iter().map(|(_, code)| code.len()).max().unwrap_or_default() as u32,
146
        };
147
235k
        let read_tree =
148
236k
            compile_read_tree(symbols).map_err(|err| report_attach!(ParseError::InvalidVp8lPrefixCode, err))?;
149
235k
        Ok(Self { read_tree, longest_code_len })
150
236k
    }
151
152
    pub fn read_tree(&self) -> &[ReadHuffmanTree<E, S>] {
153
        &self.read_tree
154
    }
155
156
24.1k
    pub fn longest_code_len(&self) -> u32 {
157
24.1k
        self.longest_code_len
158
24.1k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u8>>::longest_code_len
Line
Count
Source
156
16.0k
    pub fn longest_code_len(&self) -> u32 {
157
16.0k
        self.longest_code_len
158
16.0k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u16>>::longest_code_len
Line
Count
Source
156
8.03k
    pub fn longest_code_len(&self) -> u32 {
157
8.03k
        self.longest_code_len
158
8.03k
    }
159
160
358k
    fn symbols(code_lengths: &mut [(S, u8)]) -> Vec<(S, Vec<u8>)>
161
358k
    where
162
358k
        S: Copy + Ord + 'static,
163
    {
164
81.5M
        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
49.5M
        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
32.0M
        code_lengths.sort_unstable_by_key(|&(symbol, code_length)| (code_length, symbol));
165
1.88M
        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
1.68M
        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
201k
        let zero_code_length_count = code_lengths.partition_point(|&(_, code_length)| code_length == 0);
166
167
358k
        match (&code_lengths[zero_code_length_count..], &*code_lengths) {
168
26.0k
            (&[(first_symbol, 1)], _) => vec![(first_symbol, vec![])],
169
170
332k
            (&[(first_symbol, first_code_length), ref rest_code_lengths @ ..], &[.., (_, last_code_length)]) => {
171
332k
                let mut code = Vec::with_capacity(last_code_length.into());
172
332k
                code.resize(first_code_length.into(), 0);
173
174
332k
                let mut symbols = Vec::with_capacity(code_lengths.len());
175
332k
                symbols.push((first_symbol, code.clone()));
176
1.95M
                for &(symbol, code_length) in rest_code_lengths {
177
2.21M
                    for code_bit in code.iter_mut().rev() {
178
2.21M
                        *code_bit ^= 1;
179
2.21M
                        if *code_bit == 1 {
180
1.59M
                            break;
181
617k
                        }
182
                    }
183
1.61M
                    code.resize(code_length.into(), 0);
184
1.61M
                    symbols.push((symbol, code.clone()));
185
                }
186
332k
                symbols
187
            }
188
66
            _ => vec![],
189
        }
190
358k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u8>>::symbols
Line
Count
Source
160
318k
    fn symbols(code_lengths: &mut [(S, u8)]) -> Vec<(S, Vec<u8>)>
161
318k
    where
162
318k
        S: Copy + Ord + 'static,
163
    {
164
318k
        code_lengths.sort_unstable_by_key(|&(symbol, code_length)| (code_length, symbol));
165
318k
        let zero_code_length_count = code_lengths.partition_point(|&(_, code_length)| code_length == 0);
166
167
318k
        match (&code_lengths[zero_code_length_count..], &*code_lengths) {
168
21.2k
            (&[(first_symbol, 1)], _) => vec![(first_symbol, vec![])],
169
170
296k
            (&[(first_symbol, first_code_length), ref rest_code_lengths @ ..], &[.., (_, last_code_length)]) => {
171
296k
                let mut code = Vec::with_capacity(last_code_length.into());
172
296k
                code.resize(first_code_length.into(), 0);
173
174
296k
                let mut symbols = Vec::with_capacity(code_lengths.len());
175
296k
                symbols.push((first_symbol, code.clone()));
176
1.57M
                for &(symbol, code_length) in rest_code_lengths {
177
1.65M
                    for code_bit in code.iter_mut().rev() {
178
1.65M
                        *code_bit ^= 1;
179
1.65M
                        if *code_bit == 1 {
180
1.27M
                            break;
181
383k
                        }
182
                    }
183
1.28M
                    code.resize(code_length.into(), 0);
184
1.28M
                    symbols.push((symbol, code.clone()));
185
                }
186
296k
                symbols
187
            }
188
52
            _ => vec![],
189
        }
190
318k
    }
<webpsan::parse::bitstream::CanonicalHuffmanTree<bitstream_io::LittleEndian, u16>>::symbols
Line
Count
Source
160
40.5k
    fn symbols(code_lengths: &mut [(S, u8)]) -> Vec<(S, Vec<u8>)>
161
40.5k
    where
162
40.5k
        S: Copy + Ord + 'static,
163
    {
164
40.5k
        code_lengths.sort_unstable_by_key(|&(symbol, code_length)| (code_length, symbol));
165
40.5k
        let zero_code_length_count = code_lengths.partition_point(|&(_, code_length)| code_length == 0);
166
167
40.5k
        match (&code_lengths[zero_code_length_count..], &*code_lengths) {
168
4.84k
            (&[(first_symbol, 1)], _) => vec![(first_symbol, vec![])],
169
170
35.7k
            (&[(first_symbol, first_code_length), ref rest_code_lengths @ ..], &[.., (_, last_code_length)]) => {
171
35.7k
                let mut code = Vec::with_capacity(last_code_length.into());
172
35.7k
                code.resize(first_code_length.into(), 0);
173
174
35.7k
                let mut symbols = Vec::with_capacity(code_lengths.len());
175
35.7k
                symbols.push((first_symbol, code.clone()));
176
374k
                for &(symbol, code_length) in rest_code_lengths {
177
555k
                    for code_bit in code.iter_mut().rev() {
178
555k
                        *code_bit ^= 1;
179
555k
                        if *code_bit == 1 {
180
321k
                            break;
181
234k
                        }
182
                    }
183
338k
                    code.resize(code_length.into(), 0);
184
338k
                    symbols.push((symbol, code.clone()));
185
                }
186
35.7k
                symbols
187
            }
188
14
            _ => vec![],
189
        }
190
40.5k
    }
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
}