#![forbid(unsafe_code)]
#![allow(
clippy::redundant_pub_crate,
reason = "crate-private helper used by demux.rs; module itself is private"
)]
use crate::vint;
use smallvec::SmallVec;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Lacing {
None,
Xiph,
FixedSize,
Ebml,
}
impl Lacing {
pub(crate) const fn from_flags(flags: u8) -> Self {
match (flags >> 1) & 0x03 {
0b01 => Self::Xiph,
0b10 => Self::FixedSize,
0b11 => Self::Ebml,
_ => Self::None,
}
}
}
pub(crate) fn split(
body: &[u8],
lace_start: usize,
lacing: Lacing,
) -> Option<SmallVec<[(usize, usize); 8]>> {
if matches!(lacing, Lacing::None) {
if lace_start > body.len() {
return None;
}
let mut single = SmallVec::new();
single.push((lace_start, body.len()));
return Some(single);
}
let frame_count = usize::from(*body.get(lace_start)?) + 1;
let mut pos = lace_start + 1;
if frame_count == 1 {
let mut single = SmallVec::new();
single.push((pos, body.len()));
return Some(single);
}
let explicit_count = frame_count - 1;
let mut sizes: SmallVec<[usize; 8]> = SmallVec::new();
match lacing {
Lacing::Xiph => {
for _ in 0..explicit_count {
let mut size = 0usize;
loop {
let b = *body.get(pos)?;
pos += 1;
size = size.checked_add(usize::from(b))?;
if b != 255 {
break;
}
}
sizes.push(size);
}
}
Lacing::FixedSize => {
let total = body.len().checked_sub(pos)?;
if total % frame_count != 0 {
return None;
}
let each = total / frame_count;
sizes.extend(std::iter::repeat_n(each, explicit_count));
}
Lacing::Ebml => {
let (first, len0) = vint::decode_size(&body[pos..]).ok()?;
pos += len0;
let mut prev = i64::try_from(first.value).ok()?;
sizes.push(usize::try_from(prev).ok()?);
for _ in 1..explicit_count {
let (v, len) = vint::decode_size(&body[pos..]).ok()?;
pos += len;
let bias = (1i64 << (7 * len as i64 - 1)) - 1;
let delta = i64::try_from(v.value).ok()? - bias;
prev = prev.checked_add(delta)?;
sizes.push(usize::try_from(prev).ok()?);
}
}
Lacing::None => unreachable!("handled above"),
}
let mut ranges = SmallVec::new();
let mut off = pos;
for &size in &sizes {
let end = off.checked_add(size)?;
if end > body.len() {
return None;
}
ranges.push((off, end));
off = end;
}
if off > body.len() {
return None;
}
ranges.push((off, body.len())); Some(ranges)
}
#[cfg(test)]
#[path = "lacing_tests.rs"]
mod tests;