Skip to content

Commit 3afe6d2

Browse files
committed
MP4: Document AtomReader
1 parent ef53255 commit 3afe6d2

File tree

2 files changed

+166
-137
lines changed

2 files changed

+166
-137
lines changed

lofty/src/mp4/read/atom_reader.rs

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
use crate::config::ParsingMode;
2+
use crate::error::Result;
3+
use crate::macros::err;
4+
use crate::mp4::atom_info::AtomInfo;
5+
use crate::util::io::SeekStreamLen;
6+
7+
use std::io::{Read, Seek, SeekFrom};
8+
9+
use byteorder::{BigEndian, ReadBytesExt};
10+
11+
/// A reader for an MP4 file
12+
///
13+
/// This is a special wrapper around a reader that provides:
14+
///
15+
/// * [`Self::next`] to read atoms.
16+
/// * `read_u*` methods to read integers without needing to specify the endianness.
17+
/// * Bounds checking on reads and seeks to prevent going outside the file.
18+
pub(crate) struct AtomReader<R>
19+
where
20+
R: Read + Seek,
21+
{
22+
reader: R,
23+
start: u64,
24+
remaining_size: u64,
25+
len: u64,
26+
parse_mode: ParsingMode,
27+
}
28+
29+
impl<R> AtomReader<R>
30+
where
31+
R: Read + Seek,
32+
{
33+
/// Create a new `AtomReader`
34+
pub(crate) fn new(mut reader: R, parse_mode: ParsingMode) -> Result<Self> {
35+
let len = reader.stream_len_hack()?;
36+
Ok(Self {
37+
reader,
38+
start: 0,
39+
remaining_size: len,
40+
len,
41+
parse_mode,
42+
})
43+
}
44+
45+
/// Set new bounds for the reader
46+
///
47+
/// This is useful when reading an atom such as `moov`, where we only want to read it and its
48+
/// children. We can read the atom, set the bounds to the atom's length, and then read the children
49+
/// without worrying about reading past the atom's end.
50+
pub(crate) fn reset_bounds(&mut self, start_position: u64, len: u64) {
51+
self.start = start_position;
52+
self.remaining_size = len;
53+
self.len = len;
54+
}
55+
56+
pub(crate) fn read_u8(&mut self) -> std::io::Result<u8> {
57+
self.remaining_size = self.remaining_size.saturating_sub(1);
58+
self.reader.read_u8()
59+
}
60+
61+
pub(crate) fn read_u16(&mut self) -> std::io::Result<u16> {
62+
self.remaining_size = self.remaining_size.saturating_sub(2);
63+
self.reader.read_u16::<BigEndian>()
64+
}
65+
66+
pub(crate) fn read_u24(&mut self) -> std::io::Result<u32> {
67+
self.remaining_size = self.remaining_size.saturating_sub(3);
68+
self.reader.read_u24::<BigEndian>()
69+
}
70+
71+
pub(crate) fn read_u32(&mut self) -> std::io::Result<u32> {
72+
self.remaining_size = self.remaining_size.saturating_sub(4);
73+
self.reader.read_u32::<BigEndian>()
74+
}
75+
76+
pub(crate) fn read_u64(&mut self) -> std::io::Result<u64> {
77+
self.remaining_size = self.remaining_size.saturating_sub(8);
78+
self.reader.read_u64::<BigEndian>()
79+
}
80+
81+
pub(crate) fn read_uint(&mut self, size: usize) -> std::io::Result<u64> {
82+
self.remaining_size = self.remaining_size.saturating_sub(size as u64);
83+
self.reader.read_uint::<BigEndian>(size)
84+
}
85+
86+
/// Read the next atom in the file
87+
///
88+
/// This will leave the reader at the beginning of the atom content.
89+
pub(crate) fn next(&mut self) -> Result<Option<AtomInfo>> {
90+
if self.remaining_size == 0 {
91+
return Ok(None);
92+
}
93+
94+
if self.remaining_size < 8 {
95+
err!(SizeMismatch);
96+
}
97+
98+
AtomInfo::read(self, self.remaining_size, self.parse_mode)
99+
}
100+
101+
pub(crate) fn into_inner(self) -> R {
102+
self.reader
103+
}
104+
}
105+
106+
impl<R> Seek for AtomReader<R>
107+
where
108+
R: Read + Seek,
109+
{
110+
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
111+
match pos {
112+
SeekFrom::Start(s) => {
113+
if s > self.len {
114+
self.remaining_size = 0;
115+
116+
let bound_end = self.start + self.len;
117+
return self.reader.seek(SeekFrom::Start(bound_end));
118+
}
119+
120+
let ret = self.reader.seek(SeekFrom::Start(self.start + s))?;
121+
self.remaining_size = self.len.saturating_sub(ret);
122+
Ok(ret)
123+
},
124+
SeekFrom::End(s) => {
125+
if s >= 0 {
126+
self.remaining_size = 0;
127+
return self.reader.seek(SeekFrom::Start(self.start + self.len));
128+
}
129+
130+
let bound_end = self.start + self.len;
131+
let relative_seek_count = core::cmp::min(self.len, s.unsigned_abs());
132+
self.reader.seek(SeekFrom::Start(
133+
bound_end.saturating_sub(relative_seek_count),
134+
))
135+
},
136+
SeekFrom::Current(s) => {
137+
if s.is_negative() {
138+
self.remaining_size = self.remaining_size.saturating_add(s.unsigned_abs());
139+
} else {
140+
self.remaining_size = self.remaining_size.saturating_sub(s as u64);
141+
}
142+
143+
self.reader.seek(pos)
144+
},
145+
}
146+
}
147+
}
148+
149+
impl<R> Read for AtomReader<R>
150+
where
151+
R: Read + Seek,
152+
{
153+
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
154+
if self.remaining_size == 0 {
155+
return Ok(0);
156+
}
157+
158+
let r = self.reader.read(buf)?;
159+
self.remaining_size = self.remaining_size.saturating_sub(r as u64);
160+
161+
Ok(r)
162+
}
163+
}

lofty/src/mp4/read.rs renamed to lofty/src/mp4/read/mod.rs

Lines changed: 3 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mod atom_reader;
2+
13
use super::atom_info::{AtomIdent, AtomInfo};
24
use super::moov::Moov;
35
use super::properties::Mp4Properties;
@@ -12,143 +14,7 @@ use std::io::{Read, Seek, SeekFrom};
1214

1315
use byteorder::{BigEndian, ReadBytesExt};
1416

15-
pub(super) struct AtomReader<R>
16-
where
17-
R: Read + Seek,
18-
{
19-
reader: R,
20-
start: u64,
21-
remaining_size: u64,
22-
len: u64,
23-
parse_mode: ParsingMode,
24-
}
25-
26-
impl<R> AtomReader<R>
27-
where
28-
R: Read + Seek,
29-
{
30-
pub(super) fn new(mut reader: R, parse_mode: ParsingMode) -> Result<Self> {
31-
let len = reader.stream_len_hack()?;
32-
Ok(Self {
33-
reader,
34-
start: 0,
35-
remaining_size: len,
36-
len,
37-
parse_mode,
38-
})
39-
}
40-
41-
pub(super) fn reset_bounds(&mut self, start_position: u64, len: u64) {
42-
self.start = start_position;
43-
self.remaining_size = len;
44-
self.len = len;
45-
}
46-
47-
pub(super) fn read_u8(&mut self) -> std::io::Result<u8> {
48-
self.remaining_size = self.remaining_size.saturating_sub(1);
49-
self.reader.read_u8()
50-
}
51-
52-
pub(super) fn read_u16(&mut self) -> std::io::Result<u16> {
53-
self.remaining_size = self.remaining_size.saturating_sub(2);
54-
self.reader.read_u16::<BigEndian>()
55-
}
56-
57-
pub(super) fn read_u24(&mut self) -> std::io::Result<u32> {
58-
self.remaining_size = self.remaining_size.saturating_sub(3);
59-
self.reader.read_u24::<BigEndian>()
60-
}
61-
62-
pub(super) fn read_u32(&mut self) -> std::io::Result<u32> {
63-
self.remaining_size = self.remaining_size.saturating_sub(4);
64-
self.reader.read_u32::<BigEndian>()
65-
}
66-
67-
pub(super) fn read_u64(&mut self) -> std::io::Result<u64> {
68-
self.remaining_size = self.remaining_size.saturating_sub(8);
69-
self.reader.read_u64::<BigEndian>()
70-
}
71-
72-
pub(super) fn read_uint(&mut self, size: usize) -> std::io::Result<u64> {
73-
self.remaining_size = self.remaining_size.saturating_sub(size as u64);
74-
self.reader.read_uint::<BigEndian>(size)
75-
}
76-
77-
pub(super) fn next(&mut self) -> Result<Option<AtomInfo>> {
78-
if self.remaining_size == 0 {
79-
return Ok(None);
80-
}
81-
82-
if self.remaining_size < 8 {
83-
err!(SizeMismatch);
84-
}
85-
86-
AtomInfo::read(self, self.remaining_size, self.parse_mode)
87-
}
88-
89-
pub(super) fn into_inner(self) -> R {
90-
self.reader
91-
}
92-
}
93-
94-
impl<R> Seek for AtomReader<R>
95-
where
96-
R: Read + Seek,
97-
{
98-
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
99-
match pos {
100-
SeekFrom::Start(s) => {
101-
if s > self.len {
102-
self.remaining_size = 0;
103-
104-
let bound_end = self.start + self.len;
105-
return self.reader.seek(SeekFrom::Start(bound_end));
106-
}
107-
108-
let ret = self.reader.seek(SeekFrom::Start(self.start + s))?;
109-
self.remaining_size = self.len.saturating_sub(ret);
110-
Ok(ret)
111-
},
112-
SeekFrom::End(s) => {
113-
if s >= 0 {
114-
self.remaining_size = 0;
115-
return self.reader.seek(SeekFrom::Start(self.start + self.len));
116-
}
117-
118-
let bound_end = self.start + self.len;
119-
let relative_seek_count = core::cmp::min(self.len, s.unsigned_abs());
120-
self.reader.seek(SeekFrom::Start(
121-
bound_end.saturating_sub(relative_seek_count),
122-
))
123-
},
124-
SeekFrom::Current(s) => {
125-
if s.is_negative() {
126-
self.remaining_size = self.remaining_size.saturating_add(s.unsigned_abs());
127-
} else {
128-
self.remaining_size = self.remaining_size.saturating_sub(s as u64);
129-
}
130-
131-
self.reader.seek(pos)
132-
},
133-
}
134-
}
135-
}
136-
137-
impl<R> Read for AtomReader<R>
138-
where
139-
R: Read + Seek,
140-
{
141-
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
142-
if self.remaining_size == 0 {
143-
return Ok(0);
144-
}
145-
146-
let r = self.reader.read(buf)?;
147-
self.remaining_size = self.remaining_size.saturating_sub(r as u64);
148-
149-
Ok(r)
150-
}
151-
}
17+
pub(super) use atom_reader::AtomReader;
15218

15319
pub(in crate::mp4) fn verify_mp4<R>(reader: &mut AtomReader<R>) -> Result<String>
15420
where

0 commit comments

Comments
 (0)