Coverage Report

Created: 2025-12-03 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mp4san/webpsan/src/reader.rs
Line
Count
Source
1
use core::slice;
2
use std::io;
3
use std::io::{BufRead, BufReader, Read};
4
use std::num::NonZeroU32;
5
6
use bytes::BytesMut;
7
use mediasan_common::error::{ExtraUnparsedInput, WhileParsingType};
8
use mediasan_common::parse::FourCC;
9
use mediasan_common::util::IoResultExt;
10
use mediasan_common::{bail_attach, ensure_attach, ensure_matches_attach, report_attach, InputSpan, Skip};
11
12
use crate::parse::error::{ExpectedChunk, ParseResultExt, WhileParsingChunk};
13
use crate::parse::{ChunkHeader, ParseChunk, ParseError, WebmPrim};
14
use crate::Error;
15
16
pub struct ChunkReader<R: ?Sized> {
17
    state: State,
18
    inner: BufReader<R>,
19
}
20
21
pub struct ChunkDataReader<'a, R: ?Sized> {
22
    reader: &'a mut ChunkReader<R>,
23
}
24
25
enum State<P = ()> {
26
    Idle { last: FourCC },
27
    PeekingHeader { header: ChunkHeader },
28
    ReadingBody { header: ChunkHeader, remaining: NonZeroU32 },
29
    ReadingPadding { header: ChunkHeader, token: P },
30
}
31
32
enum PaddingReadToken {}
33
34
//
35
// ChunkReader impls
36
//
37
38
impl<R: Read + Skip> ChunkReader<R> {
39
19.9k
    pub fn new(input: R, chunk_name: FourCC) -> Self {
40
19.9k
        let inner = BufReader::with_capacity(ChunkHeader::ENCODED_LEN as usize, input);
41
19.9k
        Self { state: State::Idle { last: chunk_name }, inner }
42
19.9k
    }
<webpsan::reader::ChunkReader<&mut std::io::cursor::Cursor<&[u8]>>>::new
Line
Count
Source
39
6.78k
    pub fn new(input: R, chunk_name: FourCC) -> Self {
40
6.78k
        let inner = BufReader::with_capacity(ChunkHeader::ENCODED_LEN as usize, input);
41
6.78k
        Self { state: State::Idle { last: chunk_name }, inner }
42
6.78k
    }
<webpsan::reader::ChunkReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>>>::new
Line
Count
Source
39
13.1k
    pub fn new(input: R, chunk_name: FourCC) -> Self {
40
13.1k
        let inner = BufReader::with_capacity(ChunkHeader::ENCODED_LEN as usize, input);
41
13.1k
        Self { state: State::Idle { last: chunk_name }, inner }
42
13.1k
    }
43
}
44
45
impl<R: Read + Skip + ?Sized> ChunkReader<R> {
46
46.8k
    pub fn has_remaining(&mut self) -> Result<bool, Error> {
47
46.8k
        match self.read_padding()? {
48
46.6k
            State::Idle { .. } => (),
49
21
            State::PeekingHeader { .. } => return Ok(true),
50
83
            State::ReadingBody { .. } => return Ok(true),
51
            State::ReadingPadding { token, .. } => match token {},
52
        }
53
46.6k
        Ok(!self.inner.fill_buf()?.is_empty())
54
46.8k
    }
55
56
    /// Read a chunk header, also saving it to be returned by [`read_header`](Self::read_header) later.
57
12.1k
    pub fn peek_header(&mut self) -> Result<Option<FourCC>, Error> {
58
12.1k
        let header = match self.read_padding()? {
59
613
            State::PeekingHeader { header } => header,
60
            State::Idle { .. } => {
61
11.4k
                if !self.has_remaining()? {
62
51
                    return Ok(None);
63
11.4k
                }
64
11.4k
                ChunkHeader::read(&mut self.inner).map_eof(|_| {
65
20
                    Error::Parse(report_attach!(
66
20
                        ParseError::TruncatedChunk,
67
20
                        WhileParsingType::new::<ChunkHeader>(),
68
20
                    ))
69
20
                })?
70
            }
71
26
            State::ReadingBody { header, .. } => bail_attach!(
72
26
                ParseError::InvalidInput,
73
26
                ExtraUnparsedInput,
74
26
                WhileParsingChunk(header.name)
75
            ),
76
            State::ReadingPadding { token, .. } => match token {},
77
        };
78
79
12.0k
        self.state = State::PeekingHeader { header };
80
12.0k
        Ok(Some(header.name))
81
12.1k
    }
82
83
    /// Read a specific chunk header.
84
16.6k
    pub fn read_header(&mut self, name: FourCC) -> Result<InputSpan, Error> {
85
16.6k
        match self.read_padding()? {
86
9.87k
            State::Idle { .. } => ensure_attach!(self.has_remaining()?, ParseError::MissingRequiredChunk(name)),
87
6.74k
            State::PeekingHeader { .. } | State::ReadingBody { .. } => (),
88
            State::ReadingPadding { token, .. } => match token {},
89
        }
90
16.5k
        let (read_name, span) = self.read_any_header()?;
91
16.4k
        ensure_attach!(
92
16.4k
            read_name == name,
93
110
            ParseError::InvalidChunkLayout,
94
110
            ExpectedChunk(name),
95
110
            WhileParsingChunk(read_name),
96
        );
97
16.3k
        Ok(span)
98
16.6k
    }
99
100
    /// Read a chunk header.
101
30.1k
    pub fn read_any_header(&mut self) -> Result<(FourCC, InputSpan), Error> {
102
30.1k
        let header = match self.read_padding()? {
103
11.4k
            State::PeekingHeader { header } => header,
104
            State::Idle { .. } => {
105
18.6k
                ensure_attach!(self.has_remaining()?, ParseError::InvalidChunkLayout);
106
18.5k
                ChunkHeader::read(&mut self.inner).map_eof(|_| {
107
131
                    Error::Parse(report_attach!(
108
131
                        ParseError::TruncatedChunk,
109
131
                        WhileParsingType::new::<ChunkHeader>(),
110
131
                    ))
111
131
                })?
112
            }
113
20
            State::ReadingBody { header, .. } => bail_attach!(
114
20
                ParseError::InvalidInput,
115
20
                WhileParsingType::new::<ChunkHeader>(),
116
20
                ExtraUnparsedInput,
117
20
                WhileParsingChunk(header.name),
118
            ),
119
            State::ReadingPadding { token, .. } => match token {},
120
        };
121
122
29.8k
        self.state = match NonZeroU32::new(header.len) {
123
25.7k
            Some(remaining) => State::ReadingBody { header, remaining },
124
4.15k
            None => State::ReadingPadding { header, token: () },
125
        };
126
127
29.8k
        let offset = self.inner.stream_position()? - u64::from(ChunkHeader::ENCODED_LEN);
128
29.8k
        let len = u64::from(header.len) + u64::from(ChunkHeader::ENCODED_LEN);
129
29.8k
        Ok((header.name, InputSpan { offset, len }))
130
30.1k
    }
131
132
    /// Read and parse a chunks's data assuming its header has already been read.
133
23.1k
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
23.1k
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
22.9k
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
22.8k
        Ok(parsed)
137
23.1k
    }
<webpsan::reader::ChunkReader<dyn webpsan::ReadSkip>>::parse_data::<webpsan::parse::vp8x::Vp8xChunk>
Line
Count
Source
133
3.22k
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
3.22k
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
3.21k
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
3.19k
        Ok(parsed)
137
3.22k
    }
<webpsan::reader::ChunkReader<dyn webpsan::ReadSkip>>::parse_data::<webpsan::parse::header::WebpChunk>
Line
Count
Source
133
6.66k
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
6.66k
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
6.59k
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
6.54k
        Ok(parsed)
137
6.66k
    }
<webpsan::reader::ChunkReader<dyn webpsan::ReadSkip>>::parse_data::<webpsan::parse::alph::AlphChunk>
Line
Count
Source
133
2.36k
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
2.36k
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
2.30k
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
2.29k
        Ok(parsed)
137
2.36k
    }
<webpsan::reader::ChunkReader<dyn webpsan::ReadSkip>>::parse_data::<webpsan::parse::anim::AnimChunk>
Line
Count
Source
133
634
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
634
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
625
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
625
        Ok(parsed)
137
634
    }
<webpsan::reader::ChunkReader<dyn webpsan::ReadSkip>>::parse_data::<webpsan::parse::anmf::AnmfChunk>
Line
Count
Source
133
6.65k
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
6.65k
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
6.61k
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
6.61k
        Ok(parsed)
137
6.65k
    }
<webpsan::reader::ChunkReader<dyn webpsan::ReadSkip>>::parse_data::<webpsan::parse::vp8l::Vp8lChunk>
Line
Count
Source
133
3.59k
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
3.59k
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
3.56k
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
3.54k
        Ok(parsed)
137
3.59k
    }
138
139
    /// Read `len` of a chunks's data assuming its header has already been read.
140
23.1k
    pub fn read_data(&mut self, len: u32) -> Result<BytesMut, Error> {
141
23.1k
        let (header, remaining) = match self.read_padding()? {
142
2
            State::Idle { last } => bail_attach!(ParseError::TruncatedChunk, WhileParsingChunk(last)),
143
0
            State::PeekingHeader { header: ChunkHeader { name, .. } } => {
144
0
                panic!("read_header must be read after peek_header for {name}");
145
            }
146
23.1k
            State::ReadingBody { header, remaining } => (header, remaining),
147
            State::ReadingPadding { token, .. } => match token {},
148
        };
149
150
2
        ensure_matches_attach!(
151
23.1k
            remaining.get().checked_sub(len),
152
23.1k
            Some(new_remaining),
153
2
            ParseError::TruncatedChunk,
154
2
            WhileParsingChunk(header.name)
155
        );
156
157
23.1k
        let mut data = BytesMut::zeroed(len as usize);
158
23.1k
        self.inner.read_exact(&mut data).map_eof(|_| {
159
215
            Error::Parse(report_attach!(
160
215
                ParseError::TruncatedChunk,
161
215
                WhileParsingChunk(header.name),
162
215
            ))
163
215
        })?;
164
165
22.9k
        self.state = match NonZeroU32::new(new_remaining) {
166
19.0k
            Some(remaining) => State::ReadingBody { header, remaining },
167
3.83k
            None => State::ReadingPadding { header, token: () },
168
        };
169
170
22.9k
        Ok(data)
171
23.1k
    }
172
173
    /// Skip a chunks's data assuming its header has already been read.
174
6.83k
    pub fn skip_data(&mut self) -> Result<(), Error> {
175
6.83k
        let (header, remaining) = match self.read_padding()? {
176
4.60k
            State::Idle { .. } => return Ok(()),
177
0
            State::PeekingHeader { header: ChunkHeader { name, .. } } => {
178
0
                panic!("read_header must be read after peek_header for {name}");
179
            }
180
2.22k
            State::ReadingBody { header, remaining } => (header, remaining),
181
            State::ReadingPadding { token, .. } => match token {},
182
        };
183
184
2.22k
        self.inner.skip(remaining.get().into()).map_eof(|_| {
185
175
            Error::Parse(report_attach!(
186
175
                ParseError::TruncatedChunk,
187
175
                WhileParsingChunk(header.name),
188
175
            ))
189
175
        })?;
190
191
2.04k
        self.state = State::ReadingPadding { header, token: () };
192
193
2.04k
        Ok(())
194
6.83k
    }
195
196
    /// Return an [`AsyncRead`] + [`AsyncSkip`] type over a chunk's data, assuming its header has already been read.
197
18.8k
    pub fn data_reader(&mut self) -> ChunkDataReader<'_, R> {
198
18.8k
        ChunkDataReader { reader: self }
199
18.8k
    }
200
201
    /// Return a [`ChunkReader`] type over a chunk's data, assuming its header has already been read.
202
13.1k
    pub fn child_reader(&mut self) -> ChunkReader<ChunkDataReader<'_, R>> {
203
13.1k
        let name = self.current_chunk_name();
204
13.1k
        ChunkReader::new(self.data_reader(), name)
205
13.1k
    }
206
207
36.0k
    fn current_chunk_name(&self) -> FourCC {
208
36.0k
        match &self.state {
209
0
            &State::Idle { last } => last,
210
0
            State::PeekingHeader { header, .. } => header.name,
211
32.2k
            State::ReadingBody { header, .. } => header.name,
212
3.84k
            State::ReadingPadding { header, .. } => header.name,
213
        }
214
36.0k
    }
215
216
135k
    fn read_padding(&mut self) -> Result<State<PaddingReadToken>, Error> {
217
135k
        let header = match self.state {
218
74.8k
            State::Idle { last } => return Ok(State::Idle { last }),
219
18.7k
            State::PeekingHeader { header } => return Ok(State::PeekingHeader { header }),
220
25.5k
            State::ReadingBody { header, remaining } => return Ok(State::ReadingBody { header, remaining }),
221
16.5k
            State::ReadingPadding { header, token: () } => header,
222
        };
223
224
16.5k
        if header.padded() {
225
2.39k
            let mut pad = 0;
226
2.39k
            self.inner.read_exact(slice::from_mut(&mut pad)).map_eof(|_| {
227
94
                Error::Parse(report_attach!(
228
94
                    ParseError::TruncatedChunk,
229
94
                    WhileParsingChunk(header.name),
230
94
                ))
231
94
            })?;
232
2.30k
            ensure_matches_attach!(pad, 0, ParseError::InvalidInput, WhileParsingChunk(header.name));
233
14.1k
        }
234
235
16.4k
        self.state = State::Idle { last: header.name };
236
237
16.4k
        Ok(State::Idle { last: header.name })
238
135k
    }
239
}
240
241
//
242
// ChunkDataReader impls
243
//
244
245
impl<R: Read + ?Sized> Read for ChunkDataReader<'_, R> {
246
106k
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
247
106k
        let (header, remaining) = match &self.reader.state {
248
6.64k
            State::Idle { .. } | State::ReadingPadding { .. } => return Ok(0),
249
0
            State::PeekingHeader { header: ChunkHeader { name, .. } } => {
250
0
                panic!("read_header must be read after peek_header for {name}");
251
            }
252
99.5k
            &State::ReadingBody { header, remaining } => (header, remaining),
253
        };
254
255
99.5k
        let read_len = buf.len().min(remaining.get() as usize);
256
99.5k
        let amount_read = self.reader.inner.read(&mut buf[..read_len])?;
257
99.5k
        self.reader.state = match NonZeroU32::new(remaining.get() - amount_read as u32) {
258
92.8k
            Some(remaining) => State::ReadingBody { header, remaining },
259
6.65k
            None => State::ReadingPadding { header, token: () },
260
        };
261
99.5k
        Ok(amount_read)
262
106k
    }
263
}
264
265
impl<R: Read + Skip + ?Sized> Skip for ChunkDataReader<'_, R> {
266
4.02k
    fn skip(&mut self, skip_amount: u64) -> io::Result<()> {
267
3.98k
        let (header, remaining) = match &self.reader.state {
268
0
            State::Idle { .. } | State::ReadingPadding { .. } if skip_amount == 0 => return Ok(()),
269
43
            State::Idle { .. } | State::ReadingPadding { .. } => return Err(io::ErrorKind::UnexpectedEof.into()),
270
0
            State::PeekingHeader { header: ChunkHeader { name, .. } } => {
271
0
                panic!("read_header must be read after peek_header for {name}");
272
            }
273
3.98k
            &State::ReadingBody { header, remaining } => (header, remaining),
274
        };
275
276
3.98k
        if skip_amount > remaining.get().into() {
277
132
            return Err(io::ErrorKind::UnexpectedEof.into());
278
3.85k
        }
279
280
3.85k
        self.reader.inner.skip(skip_amount)?;
281
282
3.83k
        self.reader.state = match NonZeroU32::new(remaining.get() - skip_amount as u32) {
283
3.82k
            Some(remaining) => State::ReadingBody { header, remaining },
284
15
            None => State::ReadingPadding { header, token: () },
285
        };
286
3.83k
        Ok(())
287
4.02k
    }
288
289
29.7k
    fn stream_position(&mut self) -> io::Result<u64> {
290
29.7k
        self.reader.inner.stream_position()
291
29.7k
    }
292
293
0
    fn stream_len(&mut self) -> io::Result<u64> {
294
0
        self.reader.inner.stream_len()
295
0
    }
296
}