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