Coverage Report

Created: 2025-07-12 06:42

/src/mp4san/webpsan/src/reader.rs
Line
Count
Source (jump to first uncovered line)
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
17.9k
    pub fn new(input: R, chunk_name: FourCC) -> Self {
40
17.9k
        let inner = BufReader::with_capacity(ChunkHeader::ENCODED_LEN as usize, input);
41
17.9k
        Self { state: State::Idle { last: chunk_name }, inner }
42
17.9k
    }
<webpsan::reader::ChunkReader<&mut std::io::cursor::Cursor<&[u8]>>>::new
Line
Count
Source
39
6.46k
    pub fn new(input: R, chunk_name: FourCC) -> Self {
40
6.46k
        let inner = BufReader::with_capacity(ChunkHeader::ENCODED_LEN as usize, input);
41
6.46k
        Self { state: State::Idle { last: chunk_name }, inner }
42
6.46k
    }
<webpsan::reader::ChunkReader<webpsan::reader::ChunkDataReader<dyn webpsan::ReadSkip>>>::new
Line
Count
Source
39
11.4k
    pub fn new(input: R, chunk_name: FourCC) -> Self {
40
11.4k
        let inner = BufReader::with_capacity(ChunkHeader::ENCODED_LEN as usize, input);
41
11.4k
        Self { state: State::Idle { last: chunk_name }, inner }
42
11.4k
    }
43
}
44
45
impl<R: Read + Skip + ?Sized> ChunkReader<R> {
46
41.7k
    pub fn has_remaining(&mut self) -> Result<bool, Error> {
47
41.7k
        match self.read_padding()? {
48
41.5k
            State::Idle { .. } => (),
49
30
            State::PeekingHeader { .. } => return Ok(true),
50
50
            State::ReadingBody { .. } => return Ok(true),
51
            State::ReadingPadding { token, .. } => match token {},
52
        }
53
41.5k
        Ok(!self.inner.fill_buf()?.is_empty())
54
41.7k
    }
55
56
    /// Read a chunk header, also saving it to be returned by [`read_header`](Self::read_header) later.
57
10.2k
    pub fn peek_header(&mut self) -> Result<Option<FourCC>, Error> {
58
10.2k
        let header = match self.read_padding()? {
59
416
            State::PeekingHeader { header } => header,
60
            State::Idle { .. } => {
61
9.85k
                if !self.has_remaining()? {
62
33
                    return Ok(None);
63
9.81k
                }
64
9.81k
                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
9.81k
                })?
70
            }
71
22
            State::ReadingBody { header, .. } => bail_attach!(
72
22
                ParseError::InvalidInput,
73
22
                ExtraUnparsedInput,
74
22
                WhileParsingChunk(header.name)
75
22
            ),
76
            State::ReadingPadding { token, .. } => match token {},
77
        };
78
79
10.2k
        self.state = State::PeekingHeader { header };
80
10.2k
        Ok(Some(header.name))
81
10.2k
    }
82
83
    /// Read a specific chunk header.
84
14.8k
    pub fn read_header(&mut self, name: FourCC) -> Result<InputSpan, Error> {
85
14.8k
        match self.read_padding()? {
86
9.52k
            State::Idle { .. } => ensure_attach!(self.has_remaining()?, ParseError::MissingRequiredChunk(name)),
87
5.32k
            State::PeekingHeader { .. } | State::ReadingBody { .. } => (),
88
            State::ReadingPadding { token, .. } => match token {},
89
        }
90
14.8k
        let (read_name, span) = self.read_any_header()?;
91
14.7k
        ensure_attach!(
92
14.7k
            read_name == name,
93
111
            ParseError::InvalidChunkLayout,
94
111
            ExpectedChunk(name),
95
111
            WhileParsingChunk(read_name),
96
        );
97
14.6k
        Ok(span)
98
14.8k
    }
99
100
    /// Read a chunk header.
101
26.6k
    pub fn read_any_header(&mut self) -> Result<(FourCC, InputSpan), Error> {
102
26.6k
        let header = match self.read_padding()? {
103
9.78k
            State::PeekingHeader { header } => header,
104
            State::Idle { .. } => {
105
16.8k
                ensure_attach!(self.has_remaining()?, ParseError::InvalidChunkLayout);
106
16.7k
                ChunkHeader::read(&mut self.inner).map_eof(|_| {
107
95
                    Error::Parse(report_attach!(
108
95
                        ParseError::TruncatedChunk,
109
95
                        WhileParsingType::new::<ChunkHeader>(),
110
95
                    ))
111
16.7k
                })?
112
            }
113
16
            State::ReadingBody { header, .. } => bail_attach!(
114
16
                ParseError::InvalidInput,
115
16
                WhileParsingType::new::<ChunkHeader>(),
116
16
                ExtraUnparsedInput,
117
16
                WhileParsingChunk(header.name),
118
16
            ),
119
            State::ReadingPadding { token, .. } => match token {},
120
        };
121
122
26.4k
        self.state = match NonZeroU32::new(header.len) {
123
24.0k
            Some(remaining) => State::ReadingBody { header, remaining },
124
2.37k
            None => State::ReadingPadding { header, token: () },
125
        };
126
127
26.4k
        let offset = self.inner.stream_position()? - u64::from(ChunkHeader::ENCODED_LEN);
128
26.4k
        let len = u64::from(header.len) + u64::from(ChunkHeader::ENCODED_LEN);
129
26.4k
        Ok((header.name, InputSpan { offset, len }))
130
26.6k
    }
131
132
    /// Read and parse a chunks's data assuming its header has already been read.
133
20.9k
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
20.9k
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
20.7k
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
20.6k
        Ok(parsed)
137
20.9k
    }
<webpsan::reader::ChunkReader<dyn webpsan::ReadSkip>>::parse_data::<webpsan::parse::vp8x::Vp8xChunk>
Line
Count
Source
133
3.20k
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
3.20k
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
3.19k
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
3.17k
        Ok(parsed)
137
3.20k
    }
<webpsan::reader::ChunkReader<dyn webpsan::ReadSkip>>::parse_data::<webpsan::parse::header::WebpChunk>
Line
Count
Source
133
6.36k
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
6.36k
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
6.29k
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
6.24k
        Ok(parsed)
137
6.36k
    }
<webpsan::reader::ChunkReader<dyn webpsan::ReadSkip>>::parse_data::<webpsan::parse::alph::AlphChunk>
Line
Count
Source
133
2.53k
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
2.53k
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
2.50k
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
2.49k
        Ok(parsed)
137
2.53k
    }
<webpsan::reader::ChunkReader<dyn webpsan::ReadSkip>>::parse_data::<webpsan::parse::anim::AnimChunk>
Line
Count
Source
133
439
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
439
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
435
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
435
        Ok(parsed)
137
439
    }
<webpsan::reader::ChunkReader<dyn webpsan::ReadSkip>>::parse_data::<webpsan::parse::anmf::AnmfChunk>
Line
Count
Source
133
5.28k
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
5.28k
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
5.25k
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
5.24k
        Ok(parsed)
137
5.28k
    }
<webpsan::reader::ChunkReader<dyn webpsan::ReadSkip>>::parse_data::<webpsan::parse::vp8l::Vp8lChunk>
Line
Count
Source
133
3.12k
    pub fn parse_data<T: ParseChunk>(&mut self) -> Result<T, Error> {
134
3.12k
        let mut data = self.read_data(T::ENCODED_LEN)?;
135
3.09k
        let parsed = T::parse(&mut data).while_parsing_chunk(self.current_chunk_name())?;
136
3.08k
        Ok(parsed)
137
3.12k
    }
138
139
    /// Read `len` of a chunks's data assuming its header has already been read.
140
20.9k
    pub fn read_data(&mut self, len: u32) -> Result<BytesMut, Error> {
141
20.9k
        let (header, remaining) = match self.read_padding()? {
142
4
            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
20.9k
            State::ReadingBody { header, remaining } => (header, remaining),
147
            State::ReadingPadding { token, .. } => match token {},
148
        };
149
150
2
        ensure_matches_attach!(
151
20.9k
            remaining.get().checked_sub(len),
152
20.9k
            Some(new_remaining),
153
2
            ParseError::TruncatedChunk,
154
2
            WhileParsingChunk(header.name)
155
        );
156
157
20.9k
        let mut data = BytesMut::zeroed(len as usize);
158
20.9k
        self.inner.read_exact(&mut data).map_eof(|_| {
159
155
            Error::Parse(report_attach!(
160
155
                ParseError::TruncatedChunk,
161
155
                WhileParsingChunk(header.name),
162
155
            ))
163
20.9k
        })?;
164
165
20.7k
        self.state = match NonZeroU32::new(new_remaining) {
166
17.1k
            Some(remaining) => State::ReadingBody { header, remaining },
167
3.61k
            None => State::ReadingPadding { header, token: () },
168
        };
169
170
20.7k
        Ok(data)
171
20.9k
    }
172
173
    /// Skip a chunks's data assuming its header has already been read.
174
5.42k
    pub fn skip_data(&mut self) -> Result<(), Error> {
175
5.42k
        let (header, remaining) = match self.read_padding()? {
176
2.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.81k
            State::ReadingBody { header, remaining } => (header, remaining),
181
2.81k
            State::ReadingPadding { token, .. } => match token {},
182
2.81k
        };
183
2.81k
184
2.81k
        self.inner.skip(remaining.get().into()).map_eof(|_| {
185
102
            Error::Parse(report_attach!(
186
102
                ParseError::TruncatedChunk,
187
102
                WhileParsingChunk(header.name),
188
102
            ))
189
2.81k
        })?;
190
191
2.71k
        self.state = State::ReadingPadding { header, token: () };
192
2.71k
193
2.71k
        Ok(())
194
5.42k
    }
195
196
    /// Return an [`AsyncRead`] + [`AsyncSkip`] type over a chunk's data, assuming its header has already been read.
197
16.9k
    pub fn data_reader(&mut self) -> ChunkDataReader<'_, R> {
198
16.9k
        ChunkDataReader { reader: self }
199
16.9k
    }
200
201
    /// Return a [`ChunkReader`] type over a chunk's data, assuming its header has already been read.
202
11.4k
    pub fn child_reader(&mut self) -> ChunkReader<ChunkDataReader<'_, R>> {
203
11.4k
        let name = self.current_chunk_name();
204
11.4k
        ChunkReader::new(self.data_reader(), name)
205
11.4k
    }
206
207
32.2k
    fn current_chunk_name(&self) -> FourCC {
208
32.2k
        match &self.state {
209
0
            &State::Idle { last } => last,
210
0
            State::PeekingHeader { header, .. } => header.name,
211
28.6k
            State::ReadingBody { header, .. } => header.name,
212
3.61k
            State::ReadingPadding { header, .. } => header.name,
213
        }
214
32.2k
    }
215
216
119k
    fn read_padding(&mut self) -> Result<State<PaddingReadToken>, Error> {
217
119k
        let header = match self.state {
218
66.6k
            State::Idle { last } => return Ok(State::Idle { last }),
219
15.5k
            State::PeekingHeader { header } => return Ok(State::PeekingHeader { header }),
220
23.8k
            State::ReadingBody { header, remaining } => return Ok(State::ReadingBody { header, remaining }),
221
13.8k
            State::ReadingPadding { header, token: () } => header,
222
13.8k
        };
223
13.8k
224
13.8k
        if header.padded() {
225
2.89k
            let mut pad = 0;
226
2.89k
            self.inner.read_exact(slice::from_mut(&mut pad)).map_eof(|_| {
227
78
                Error::Parse(report_attach!(
228
78
                    ParseError::TruncatedChunk,
229
78
                    WhileParsingChunk(header.name),
230
78
                ))
231
2.89k
            })?;
232
2.81k
            ensure_matches_attach!(pad, 0, ParseError::InvalidInput, WhileParsingChunk(header.name));
233
10.9k
        }
234
235
13.7k
        self.state = State::Idle { last: header.name };
236
13.7k
237
13.7k
        Ok(State::Idle { last: header.name })
238
119k
    }
239
}
240
241
//
242
// ChunkDataReader impls
243
//
244
245
impl<R: Read + ?Sized> Read for ChunkDataReader<'_, R> {
246
93.9k
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
247
93.9k
        let (header, remaining) = match &self.reader.state {
248
5.24k
            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
88.6k
            &State::ReadingBody { header, remaining } => (header, remaining),
253
88.6k
        };
254
88.6k
255
88.6k
        let read_len = buf.len().min(remaining.get() as usize);
256
88.6k
        let amount_read = self.reader.inner.read(&mut buf[..read_len])?;
257
88.6k
        self.reader.state = match NonZeroU32::new(remaining.get() - amount_read as u32) {
258
83.3k
            Some(remaining) => State::ReadingBody { header, remaining },
259
5.30k
            None => State::ReadingPadding { header, token: () },
260
        };
261
88.6k
        Ok(amount_read)
262
93.9k
    }
263
}
264
265
impl<R: Read + Skip + ?Sized> Skip for ChunkDataReader<'_, R> {
266
5.34k
    fn skip(&mut self, skip_amount: u64) -> io::Result<()> {
267
5.29k
        let (header, remaining) = match &self.reader.state {
268
0
            State::Idle { .. } | State::ReadingPadding { .. } if skip_amount == 0 => return Ok(()),
269
53
            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
5.29k
            &State::ReadingBody { header, remaining } => (header, remaining),
274
5.29k
        };
275
5.29k
276
5.29k
        if skip_amount > remaining.get().into() {
277
49
            return Err(io::ErrorKind::UnexpectedEof.into());
278
5.24k
        }
279
5.24k
280
5.24k
        self.reader.inner.skip(skip_amount)?;
281
282
5.24k
        self.reader.state = match NonZeroU32::new(remaining.get() - skip_amount as u32) {
283
5.24k
            Some(remaining) => State::ReadingBody { header, remaining },
284
1
            None => State::ReadingPadding { header, token: () },
285
        };
286
5.24k
        Ok(())
287
5.34k
    }
288
289
25.2k
    fn stream_position(&mut self) -> io::Result<u64> {
290
25.2k
        self.reader.inner.stream_position()
291
25.2k
    }
292
293
0
    fn stream_len(&mut self) -> io::Result<u64> {
294
0
        self.reader.inner.stream_len()
295
0
    }
296
}