use crate::container::bmff;
use crate::container::ogg;
use crate::container::riff;
use crate::container::zip;
use crate::formats::ParseLimits;
use crate::formats::tags;
use crate::report::InspectOptions;
pub fn zip_round_trip(data: &[u8]) {
let limits = ParseLimits::default();
let Ok(entries) = zip::read(data, &limits) else {
return;
};
let mut budget = limits.max_expanded_bytes;
let mut outputs = Vec::with_capacity(entries.len());
for entry in entries {
if let Ok(contents) = entry.contents(budget) {
let len = u64::try_from(contents.len()).unwrap_or(u64::MAX);
if zip::spend(&mut budget, len).is_err() {
return;
}
}
outputs.push(zip::Output::Copied(entry));
}
let Ok(written) = zip::write(&outputs) else {
return;
};
assert!(
zip::read(&written, &limits).is_ok(),
"the ZIP writer produced an archive the ZIP reader refuses"
);
}
pub fn bmff_round_trip(data: &[u8]) {
let limits = ParseLimits::default();
let mut budget = limits.max_items;
let Ok((top, _trailing)) = bmff::top_level(data, &mut budget) else {
return;
};
let mut out = Vec::new();
for b in &top {
descend(b, limits.max_depth, &mut budget);
if bmff::write_box(&mut out, b.kind, |o| {
o.extend_from_slice(b.payload);
Ok(())
})
.is_err()
{
return;
}
}
let mut read_budget = limits.max_items;
assert!(
bmff::children(&out, 0, &mut read_budget).is_ok(),
"the BMFF writer produced a tree the walker refuses"
);
}
pub fn riff_round_trip(data: &[u8]) {
let limits = ParseLimits::default();
for form in [*b"WEBP", *b"WAVE"] {
let mut budget = limits.max_items;
let Ok((chunks, _trailing)) = riff::read(data, form, &mut budget) else {
continue;
};
let mut body = Vec::new();
for chunk in &chunks {
if let Some((_, rest)) = riff::list_form(chunk.data) {
let _ = riff::chunks(rest, chunk.offset, &mut budget);
}
if riff::write_chunk(&mut body, chunk.kind, chunk.data).is_err() {
return;
}
}
let Ok(written) = riff::write(form, &body) else {
return;
};
let mut read_budget = limits.max_items;
assert!(
riff::read(&written, form, &mut read_budget).is_ok(),
"the RIFF writer produced a file the RIFF walker refuses"
);
}
}
fn descend(parent: &bmff::Box<'_>, depth: u32, budget: &mut u32) {
let children = bmff::children_at(parent, parent.payload, depth, budget).or_else(|_| {
let rest = parent.full().map(|(_, _, rest)| rest).unwrap_or_default();
bmff::children_at(parent, rest, depth, budget)
});
let Ok(children) = children else {
return;
};
for child in &children {
descend(child, depth.saturating_sub(1), budget);
}
}
pub fn tags_scan(data: &[u8]) {
let options = InspectOptions::with_values();
let Ok((head, start)) = tags::head(data) else {
return;
};
for tag in &head {
let _ = tags::findings(tag, &options);
}
let Ok((tail, end)) = tags::tail(data, start) else {
return;
};
for tag in &tail {
let _ = tags::findings(tag, &options);
}
assert!(end >= start, "a tail tag reached below the head tags");
assert!(end <= data.len(), "a tag span ran past the end of the file");
}
pub fn ogg_restamp(data: &mut [u8]) {
ogg::restamp(data);
}
pub fn ogg_round_trip(data: &[u8]) {
let limits = ParseLimits::default();
let mut budget = limits.max_items;
let Ok(pages) = ogg::pages(data, &mut budget) else {
return;
};
let Ok(packets) = ogg::packets(&pages) else {
return;
};
let emitted: Vec<ogg::Emit<'_>> = packets.iter().map(ogg::Emit::copied).collect();
let Ok(written) = ogg::write(0, &emitted) else {
return;
};
let mut read_budget = limits.max_items;
let reread = ogg::pages(&written, &mut read_budget)
.ok()
.and_then(|pages| ogg::packets(&pages).ok());
assert!(
reread.is_some(),
"the Ogg writer produced a stream the page walker refuses"
);
let reread = reread.unwrap_or_default();
assert!(
reread.len() == packets.len(),
"the Ogg round trip changed the packet count"
);
for (before, after) in packets.iter().zip(&reread) {
assert!(before.bytes() == after.bytes(), "a packet's bytes changed");
assert!(before.granule == after.granule, "a granule position moved");
}
}