1#![cfg_attr(feature = "docs", doc = "\n\nSee the [changelog][changelog] for a full release history.")]
12#![cfg_attr(feature = "docs", doc = "## Feature flags")]
13#![cfg_attr(feature = "docs", doc = document_features::document_features!())]
14#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]
21#![cfg_attr(docsrs, feature(doc_auto_cfg))]
22#![deny(missing_docs)]
23#![deny(unsafe_code)]
24#![deny(unreachable_pub)]
25
26use std::fmt::Debug;
27use std::io;
28
29use scuffle_bytes_util::BytesCow;
30use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize};
31
32pub mod boxes;
33mod common_types;
34mod conformance_tests;
35mod file;
36mod header;
37mod sized;
38mod utils;
39
40pub use common_types::*;
41pub use file::*;
42pub use header::*;
43pub use isobmff_derive::IsoBox;
44pub use sized::*;
45
46#[doc(hidden)]
47pub mod reexports {
48 pub use scuffle_bytes_util;
49}
50
51#[cfg(feature = "docs")]
53#[scuffle_changelog::changelog]
54pub mod changelog {}
55
56pub trait IsoBox: IsoSized {
58 const TYPE: BoxType;
60
61 fn add_header_size(payload_size: usize) -> usize {
65 let mut box_size = payload_size;
66 box_size += 4 + 4; if let BoxType::Uuid(_) = Self::TYPE {
68 box_size += 16; }
70
71 if box_size > u32::MAX as usize {
73 box_size += 8; }
75
76 box_size
77 }
78
79 fn box_header(&self) -> BoxHeader {
81 BoxHeader {
82 size: self.size().into(),
83 box_type: Self::TYPE,
84 }
85 }
86
87 fn serialize_box_header<W>(&self, writer: W) -> std::io::Result<()>
89 where
90 W: std::io::Write,
91 {
92 self.box_header().serialize(writer)
93 }
94}
95
96#[derive(PartialEq, Eq)]
103pub struct UnknownBox<'a> {
104 pub header: BoxHeader,
106 pub data: BytesCow<'a>,
108}
109
110impl Debug for UnknownBox<'_> {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 f.debug_struct("UnknownBox")
113 .field("header", &self.header)
114 .field("data.len", &self.data.len())
115 .finish()
116 }
117}
118
119impl<'a> Deserialize<'a> for UnknownBox<'a> {
120 fn deserialize<R>(mut reader: R) -> std::io::Result<Self>
121 where
122 R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
123 {
124 let header = BoxHeader::deserialize(&mut reader)?;
125 Self::deserialize_seed(&mut reader, header)
126 }
127}
128
129impl<'a> DeserializeSeed<'a, BoxHeader> for UnknownBox<'a> {
130 fn deserialize_seed<R>(mut reader: R, seed: BoxHeader) -> std::io::Result<Self>
131 where
132 R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
133 {
134 Ok(Self {
135 header: seed,
136 data: reader.try_read_to_end()?,
137 })
138 }
139}
140
141impl Serialize for UnknownBox<'_> {
142 fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
143 where
144 W: std::io::Write,
145 {
146 self.header.serialize(&mut writer)?;
147 self.data.serialize(&mut writer)?;
148 Ok(())
149 }
150}
151
152impl<'a> UnknownBox<'a> {
153 pub fn try_from_box(box_: impl IsoBox + Serialize) -> Result<Self, io::Error> {
155 #[derive(Debug)]
156 struct SkipWriter<W> {
157 writer: W,
158 skip_size: usize,
159 }
160
161 impl<W> io::Write for SkipWriter<W>
162 where
163 W: io::Write,
164 {
165 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
166 let skip = self.skip_size.min(buf.len());
168
169 let n = self.writer.write(&buf[skip..])? + skip;
172 self.skip_size = self.skip_size.saturating_sub(n);
174 Ok(n)
175 }
176
177 fn flush(&mut self) -> io::Result<()> {
178 self.writer.flush()
179 }
180 }
181
182 let header = box_.box_header();
183
184 let mut data = if let Some(size) = header.payload_size() {
185 Vec::with_capacity(size)
186 } else {
187 Vec::new()
188 };
189 box_.serialize(SkipWriter {
190 writer: &mut data,
191 skip_size: header.size(),
192 })?;
193
194 data.shrink_to_fit();
195
196 Ok(Self {
197 header,
198 data: data.into(),
199 })
200 }
201
202 pub fn deserialize_as<T, S>(self) -> std::io::Result<T>
204 where
205 T: DeserializeSeed<'a, S>,
206 S: DeserializeSeed<'a, BoxHeader>,
207 {
208 let mut reader = scuffle_bytes_util::zero_copy::BytesBuf::from(self.data.into_bytes());
209 let seed = S::deserialize_seed(&mut reader, self.header)?;
210 T::deserialize_seed(&mut reader, seed)
211 }
212
213 pub fn deserialize_as_box<B>(self) -> std::io::Result<B>
215 where
216 B: IsoBox + Deserialize<'a>,
217 {
218 if self.header.box_type != B::TYPE {
219 return Err(std::io::Error::new(
220 std::io::ErrorKind::InvalidData,
221 format!("Box type mismatch: expected {:?}, found {:?}", B::TYPE, self.header.box_type),
222 ));
223 }
224
225 let reader = scuffle_bytes_util::zero_copy::BytesBuf::from(self.data.into_bytes());
226 B::deserialize(reader)
227 }
228}
229
230impl IsoSized for UnknownBox<'_> {
231 fn size(&self) -> usize {
232 self.header.size() + self.data.size()
233 }
234}