use alloc::vec::Vec;
use crate::error::{Error, Result};
pub const NAL_LENGTH_SIZE: usize = 4;
pub const VALID_NAL_LENGTH_SIZES: [usize; 3] = [1, 2, NAL_LENGTH_SIZE];
pub const NAL_LENGTH_SIZE_MINUS_ONE: u8 = (NAL_LENGTH_SIZE - 1) as u8;
pub fn iter_annexb_nals(annexb: &[u8]) -> AnnexBNalIter<'_> {
AnnexBNalIter {
data: annexb,
code_positions: start_code_positions(annexb),
idx: 0,
}
}
pub(crate) fn find_start_code_prefix(data: &[u8], from: usize) -> Option<usize> {
let n = data.len();
let mut p = from;
while p + 3 <= n {
let i = p + 2 + memchr::memchr(1, &data[p + 2..])?;
if data[i - 1] == 0 && data[i - 2] == 0 {
return Some(i - 2);
}
p = i - 1;
}
None
}
pub(crate) fn start_code_positions(data: &[u8]) -> Vec<usize> {
let mut positions = Vec::new();
let mut p = 0usize;
while let Some(at) = find_start_code_prefix(data, p) {
positions.push(at);
p = at + 3;
}
positions
}
pub struct AnnexBNalIter<'a> {
data: &'a [u8],
code_positions: Vec<usize>,
idx: usize,
}
impl<'a> Iterator for AnnexBNalIter<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<&'a [u8]> {
loop {
if self.idx >= self.code_positions.len() {
return None;
}
let start = self.code_positions[self.idx] + 3;
let end = self
.code_positions
.get(self.idx + 1)
.copied()
.unwrap_or(self.data.len());
self.idx += 1;
let mut slice = &self.data[start..end];
while let Some((&0, rest)) = slice.split_last() {
slice = rest;
}
if slice.is_empty() {
continue;
}
return Some(slice);
}
}
}
pub fn annexb_to_length_prefixed(annexb: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(annexb.len());
for nal in iter_annexb_nals(annexb) {
out.extend_from_slice(&(nal.len() as u32).to_be_bytes());
out.extend_from_slice(nal);
}
out
}
pub fn iter_length_prefixed_nals(lp: &[u8]) -> Result<Vec<&[u8]>> {
iter_length_prefixed_nals_with(lp, NAL_LENGTH_SIZE)
}
pub fn iter_length_prefixed_nals_with(lp: &[u8], length_size: usize) -> Result<Vec<&[u8]>> {
if !VALID_NAL_LENGTH_SIZES.contains(&length_size) {
return Err(Error::InvalidValue {
field: "NAL length size",
value: length_size as u64,
reason: "must be 1, 2 or 4 (lengthSizeMinusOne 0/1/3; 2 is reserved)",
});
}
let mut nals = Vec::new();
let mut off = 0usize;
while off < lp.len() {
let prefix_end = off.checked_add(length_size).ok_or(Error::BufferTooShort {
need: usize::MAX,
have: lp.len(),
what: "NAL length prefix",
})?;
if prefix_end > lp.len() {
return Err(Error::BufferTooShort {
need: prefix_end,
have: lp.len(),
what: "NAL length prefix",
});
}
let mut len: usize = 0;
for &byte in &lp[off..prefix_end] {
len = (len << 8) | usize::from(byte);
}
let start = prefix_end;
let end = start.checked_add(len).ok_or(Error::BufferTooShort {
need: usize::MAX,
have: lp.len(),
what: "NAL length prefix overflows the buffer offset",
})?;
if end > lp.len() {
return Err(Error::BufferTooShort {
need: end,
have: lp.len(),
what: "NAL payload",
});
}
nals.push(&lp[start..end]);
off = end;
}
Ok(nals)
}
pub fn normalise_nal_length_size(au: &[u8], length_size: usize) -> Result<Vec<u8>> {
let nals = iter_length_prefixed_nals_with(au, length_size)?;
if length_size == NAL_LENGTH_SIZE {
return Ok(au.to_vec());
}
let total: usize = nals.iter().map(|n| NAL_LENGTH_SIZE + n.len()).sum();
let mut out = Vec::with_capacity(total);
for nal in nals {
let len = u32::try_from(nal.len())
.map_err(|_| Error::InvalidInput("NAL unit longer than u32::MAX"))?;
out.extend_from_slice(&len.to_be_bytes());
out.extend_from_slice(nal);
}
Ok(out)
}
pub fn length_prefixed_to_annexb(lp: &[u8]) -> Result<Vec<u8>> {
let nals = iter_length_prefixed_nals(lp)?;
let mut out = Vec::with_capacity(lp.len());
for nal in nals {
out.extend_from_slice(&[0, 0, 0, 1]);
out.extend_from_slice(nal);
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn find_start_code_prefix_resumes_and_is_exhaustive() {
let d = [9, 0, 0, 1, 7, 0, 0, 0, 1, 8, 0, 0];
assert_eq!(find_start_code_prefix(&d, 0), Some(1));
assert_eq!(find_start_code_prefix(&d, 2), Some(6));
assert_eq!(find_start_code_prefix(&d, 7), None);
assert_eq!(find_start_code_prefix(&d, 99), None);
assert_eq!(start_code_positions(&d), vec![1, 6]);
}
fn find_start_code_prefix_byte_by_byte(data: &[u8], from: usize) -> Option<usize> {
let mut p = from;
while p + 3 <= data.len() {
if data[p] == 0 && data[p + 1] == 0 && data[p + 2] == 1 {
return Some(p);
}
p += 1;
}
None
}
fn assert_same_as_byte_by_byte(d: &[u8]) {
for from in 0..=d.len() + 1 {
assert_eq!(
find_start_code_prefix(d, from),
find_start_code_prefix_byte_by_byte(d, from),
"from {from} in {d:02x?}"
);
}
}
#[test]
fn find_start_code_prefix_matches_byte_by_byte_scan() {
let fixed: [&[u8]; 13] = [
b"",
b"\x01",
b"\x00\x00\x01",
b"\x00\x00\x01\x09",
b"\x00\x00\x00\x01\x67\x42\x00\x00\x01\x68",
b"\x01\x00\x00\x01\x41",
b"\x00\x01\x00\x00\x01\x00\x00\x01\x65",
b"\x00\x00\x01\x00\x00\x01",
b"\x00\x00\x00\x00\x00\x01\x06\x00\x00",
b"\x00\x00\x01\x01\x01\x00\x00\x01\x01",
b"\x00\x00\x02\x00\x00\x01\x09\x10\x00\x00\x00",
b"\x01\x01\x01\x00\x01\x00\x00",
b"\x09\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01",
];
for d in fixed {
assert_same_as_byte_by_byte(d);
}
let mut seed = 0x9e37_79b9_7f4a_7c15u64;
let mut rnd = move || {
seed ^= seed << 13;
seed ^= seed >> 7;
seed ^= seed << 17;
seed
};
for _ in 0..5_000 {
let len = (rnd() % 96) as usize;
let d: Vec<u8> = (0..len)
.map(|_| match rnd() % 8 {
0..=3 => 0,
4 | 5 => 1,
_ => rnd() as u8,
})
.collect();
assert_same_as_byte_by_byte(&d);
}
}
#[test]
fn splits_mixed_start_codes_and_strips_trailing_zeros() {
let annexb = [
0x00, 0x00, 0x00, 0x01, 0x67, 0x42,
0x00, 0x00, 0x00, 0x01, 0x65, 0x00, 0x00, 0x03, 0x88, 0x00,
0x00, ];
let nals: Vec<&[u8]> = iter_annexb_nals(&annexb).collect();
assert_eq!(nals.len(), 2);
assert_eq!(nals[0], &[0x67, 0x42]);
assert_eq!(nals[1], &[0x65, 0x00, 0x00, 0x03, 0x88]);
}
#[test]
fn annexb_to_length_prefixed_bijection() {
let annexb = [
0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x00, 0x00, 0x01, 0x65, 0x88, 0x99,
];
let lp = annexb_to_length_prefixed(&annexb);
assert_eq!(&lp[0..4], &2u32.to_be_bytes());
assert_eq!(&lp[4..6], &[0x67, 0x42]);
assert_eq!(&lp[6..10], &3u32.to_be_bytes());
assert_eq!(&lp[10..13], &[0x65, 0x88, 0x99]);
let back = length_prefixed_to_annexb(&lp).unwrap();
let lp2 = annexb_to_length_prefixed(&back);
assert_eq!(lp, lp2, "length↔annexb round-trip must be canonical");
}
#[test]
fn length_prefixed_rejects_overrun() {
let lp = [0x00, 0x00, 0x00, 0x63, 0xAA, 0xBB];
assert!(iter_length_prefixed_nals(&lp).is_err());
}
#[test]
fn length_prefixed_rejects_maximal_declared_length() {
let lp = [0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0xBB];
let err = iter_length_prefixed_nals(&lp).expect_err("must not be accepted");
assert!(matches!(err, Error::BufferTooShort { .. }), "{err:?}");
}
#[test]
fn length_prefixed_rejects_maximal_declared_length_mid_buffer() {
let lp = [
0x00, 0x00, 0x00, 0x01, 0x67, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, ];
assert!(iter_length_prefixed_nals(&lp).is_err());
}
#[test]
fn run_of_empty_nals_does_not_recurse() {
let mut data = Vec::new();
for _ in 0..200_000 {
data.extend_from_slice(&[0x00, 0x00, 0x01]);
}
data.extend_from_slice(&[0x00, 0x00, 0x01, 0x65, 0x88]);
assert_eq!(iter_annexb_nals(&data).count(), 1);
assert_eq!(annexb_to_length_prefixed(&data).len(), 4 + 2);
}
#[test]
fn normalise_two_byte_lengths_to_four() {
let au = [0x00, 0x03, 0xAA, 0xBB, 0xCC, 0x00, 0x02, 0xDD, 0xEE];
let out = normalise_nal_length_size(&au, 2).expect("2-byte AU normalises");
assert_eq!(
out,
[
0x00, 0x00, 0x00, 0x03, 0xAA, 0xBB, 0xCC, 0x00, 0x00, 0x00, 0x02, 0xDD, 0xEE, ]
);
assert_eq!(iter_length_prefixed_nals(&out).unwrap().len(), 2);
}
#[test]
fn normalise_three_byte_lengths_is_rejected() {
let au = [0x00, 0x00, 0x04, 0xAA, 0xBB, 0xCC, 0xDD];
assert!(matches!(
normalise_nal_length_size(&au, 3),
Err(Error::InvalidValue {
field: "NAL length size",
value: 3,
..
})
));
}
#[test]
fn normalise_one_byte_lengths_to_four() {
let au = [0x02, 0xAA, 0xBB];
let out = normalise_nal_length_size(&au, 1).expect("1-byte AU normalises");
assert_eq!(out, [0x00, 0x00, 0x00, 0x02, 0xAA, 0xBB]);
}
#[test]
fn normalise_four_byte_validates_framing() {
let well_formed = [0x00, 0x00, 0x00, 0x02, 0xAA, 0xBB];
assert_eq!(
normalise_nal_length_size(&well_formed, 4).unwrap(),
well_formed
);
assert!(matches!(
normalise_nal_length_size(&[0x00, 0x00, 0xFF, 0xFF, 0xAA], 4),
Err(Error::BufferTooShort { .. })
));
assert!(matches!(
normalise_nal_length_size(&[0x00, 0x00, 0x00, 0x01, 0xAA, 0x00, 0x00], 4),
Err(Error::BufferTooShort { .. })
));
}
#[test]
fn normalise_rejects_hostile_input() {
assert!(normalise_nal_length_size(&[0x00], 0).is_err());
assert!(normalise_nal_length_size(&[0x00], 5).is_err());
assert!(normalise_nal_length_size(&[0x00], 2).is_err());
assert!(normalise_nal_length_size(&[0xFF, 0xFF, 0xAA], 2).is_err());
let out = normalise_nal_length_size(&[0x00, 0x00], 2).unwrap();
assert_eq!(out, [0x00, 0x00, 0x00, 0x00]);
}
#[test]
fn iter_length_prefixed_with_rejects_bad_size() {
assert!(iter_length_prefixed_nals_with(&[0x00], 0).is_err());
assert!(iter_length_prefixed_nals_with(&[0x00], 3).is_err());
assert!(iter_length_prefixed_nals_with(&[0x00], 5).is_err());
for size in VALID_NAL_LENGTH_SIZES {
assert!(iter_length_prefixed_nals_with(&[], size).is_ok());
}
}
}