1use std::io::Read;
2
3use crate::*;
4
5pub trait Atom: Sized {
7 const KIND: FourCC;
8
9 fn decode_body<B: Buf>(buf: &mut B) -> Result<Self>;
10 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()>;
11
12 fn decode_unknown(atom: &crate::Any) -> Result<()> {
14 crate::decode_unknown(atom, Self::KIND)
15 }
16}
17
18impl<T: Atom> Encode for T {
19 fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
20 buf.encode_atom(Self::KIND, |buf| self.encode_body(buf))
22 }
23}
24
25impl<T: Atom> Decode for T {
26 fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
27 Self::decode_maybe(buf)?.ok_or(Error::OutOfBounds)
28 }
29}
30
31impl<T: Atom> DecodeMaybe for T {
32 fn decode_maybe<B: Buf>(buf: &mut B) -> Result<Option<Self>> {
33 let remaining = buf.remaining();
36 let mut peek = buf.slice(remaining);
37 let header = match Header::decode_maybe(&mut peek)? {
38 Some(header) => header,
39 None => return Ok(None),
40 };
41
42 if header.kind != T::KIND {
43 return Err(Error::UnexpectedBox(header.kind));
44 }
45
46 let size = header.size.unwrap_or(peek.remaining());
47 if size > peek.remaining() {
48 return Ok(None);
49 }
50
51 buf.advance(remaining - peek.remaining());
52 let body = &mut buf.slice(size);
53
54 let atom = match Self::decode_body(body) {
55 Ok(atom) => atom,
56 Err(Error::OutOfBounds) => return Err(Error::OverDecode(T::KIND)),
57 Err(Error::ShortRead) => return Err(Error::UnderDecode(T::KIND)),
58 Err(err) => return Err(err),
59 };
60
61 if body.has_remaining() {
62 return Err(Error::UnderDecode(T::KIND));
63 }
64
65 buf.advance(size);
66
67 Ok(Some(atom))
68 }
69}
70
71impl<T: Atom> ReadFrom for T {
72 fn read_from<R: Read + ?Sized>(r: &mut R) -> Result<Self> {
73 <Option<T> as ReadFrom>::read_from(r)?.ok_or(Error::MissingBox(T::KIND))
74 }
75}
76
77impl<T: Atom> ReadFrom for Option<T> {
78 fn read_from<R: Read + ?Sized>(r: &mut R) -> Result<Self> {
79 let header = match <Option<Header> as ReadFrom>::read_from(r)? {
80 Some(header) => header,
81 None => return Ok(None),
82 };
83
84 if header.kind != T::KIND {
85 return Err(Error::UnexpectedBox(header.kind));
86 }
87
88 let body = &mut header.read_body(r)?;
89
90 let atom = match T::decode_body(body) {
91 Ok(atom) => atom,
92 Err(Error::OutOfBounds) => return Err(Error::OverDecode(T::KIND)),
93 Err(Error::ShortRead) => return Err(Error::UnderDecode(T::KIND)),
94 Err(err) => return Err(err),
95 };
96
97 if body.has_remaining() {
98 return Err(Error::UnderDecode(T::KIND));
99 }
100
101 Ok(Some(atom))
102 }
103}
104
105impl<T: Atom> ReadUntil for T {
106 fn read_until<R: Read + ?Sized>(r: &mut R) -> Result<Self> {
107 <Option<T> as ReadUntil>::read_until(r)?.ok_or(Error::MissingBox(T::KIND))
108 }
109}
110
111impl<T: Atom> ReadUntil for Option<T> {
112 fn read_until<R: Read + ?Sized>(r: &mut R) -> Result<Self> {
113 while let Some(header) = <Option<Header> as ReadFrom>::read_from(r)? {
114 if header.kind == T::KIND {
115 let body = &mut header.read_body(r)?;
116 return Ok(Some(T::decode_atom(&header, body)?));
117 }
118 }
119
120 Ok(None)
121 }
122}
123
124impl<T: Atom> DecodeAtom for T {
125 fn decode_atom<B: Buf>(header: &Header, buf: &mut B) -> Result<T> {
126 if header.kind != T::KIND {
127 return Err(Error::UnexpectedBox(header.kind));
128 }
129
130 let size = header.size.unwrap_or(buf.remaining());
131 if size > buf.remaining() {
132 return Err(Error::OutOfBounds);
133 }
134
135 let body = &mut buf.slice(size);
136
137 let atom = match T::decode_body(body) {
138 Ok(atom) => atom,
139 Err(Error::OutOfBounds) => return Err(Error::OverDecode(T::KIND)),
140 Err(Error::ShortRead) => return Err(Error::UnderDecode(T::KIND)),
141 Err(err) => return Err(err),
142 };
143
144 if body.has_remaining() {
145 return Err(Error::UnderDecode(T::KIND));
146 }
147
148 buf.advance(size);
149
150 Ok(atom)
151 }
152}
153
154impl<T: Atom> ReadAtom for T {
155 fn read_atom<R: Read + ?Sized>(header: &Header, r: &mut R) -> Result<Self> {
156 if header.kind != T::KIND {
157 return Err(Error::UnexpectedBox(header.kind));
158 }
159
160 let body = &mut header.read_body(r)?;
161 Self::decode_atom(header, body)
162 }
163}
164
165macro_rules! nested {
175 (required: [$($required:ident),*$(,)?], optional: [$($optional:ident),*$(,)?], multiple: [$($multiple:ident),*$(,)?],) => {
176 pastey::paste! {
177 fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
178 $( let mut [<$required:lower>] = None;)*
179 $( let mut [<$optional:lower>] = None;)*
180 $( let mut [<$multiple:lower>] = Vec::new();)*
181
182 while let Some(atom) = Any::decode_maybe(buf)? {
183 match atom {
184 $(Any::$required(atom) => {
185 if [<$required:lower>].is_some() {
186 return Err(Error::DuplicateBox($required::KIND));
187 }
188 [<$required:lower>] = Some(atom);
189 },)*
190 $(Any::$optional(atom) => {
191 if [<$optional:lower>].is_some() {
192 return Err(Error::DuplicateBox($optional::KIND));
193 }
194 [<$optional:lower>] = Some(atom);
195 },)*
196 $(Any::$multiple(atom) => {
197 [<$multiple:lower>].push(atom.into());
198 },)*
199 Any::Skip(atom) => tracing::debug!(size = atom.zeroed.size, "skipping skip box"),
200 Any::Free(atom) => tracing::debug!(size = atom.zeroed.size, "skipping free box"),
201 unknown => Self::decode_unknown(&unknown)?,
202 }
203 }
204 skip_trailing_padding(buf);
207
208 Ok(Self {
209 $([<$required:lower>]: [<$required:lower>].ok_or(Error::MissingBox($required::KIND))? ,)*
210 $([<$optional:lower>],)*
211 $([<$multiple:lower>],)*
212 })
213 }
214
215 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
216 $( self.[<$required:lower>].encode(buf)?; )*
217 $( self.[<$optional:lower>].encode(buf)?; )*
218 $( self.[<$multiple:lower>].iter().map(|x| x.encode(buf)).collect::<Result<()>>()?; )*
219
220 Ok(())
221 }
222 }
223 };
224}
225
226pub(crate) use nested;
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231 use std::io::Cursor;
232
233 const FTYP_BODY_TAGGED_AS_MOOV: &[u8] = b"\0\0\0\x14mooviso6\0\0\x02\0mp41";
234
235 #[test]
236 fn typed_decode_rejects_unexpected_box() {
237 let err = Ftyp::decode(&mut Cursor::new(FTYP_BODY_TAGGED_AS_MOOV)).unwrap_err();
238 assert!(matches!(err, Error::UnexpectedBox(kind) if kind == Moov::KIND));
239 }
240
241 #[test]
242 fn typed_read_from_rejects_unexpected_box() {
243 let err =
244 <Ftyp as ReadFrom>::read_from(&mut Cursor::new(FTYP_BODY_TAGGED_AS_MOOV)).unwrap_err();
245 assert!(matches!(err, Error::UnexpectedBox(kind) if kind == Moov::KIND));
246 }
247}