use std::{
cell::RefCell,
io::{Error, Result},
};
use crate::{
builder::{ToBox, WriteAndSeek},
BoxType,
};
pub struct PlaceholderDataBox {
tbox: BoxType,
size: usize,
offset: RefCell<Option<u64>>,
}
impl PlaceholderDataBox {
pub fn new(tbox: BoxType, size: usize) -> Self {
Self {
tbox,
size,
offset: RefCell::new(None),
}
}
pub fn offset(&self) -> Option<u64> {
self.offset.clone().into_inner()
}
pub fn replace_payload(&self, to_stream: &mut dyn WriteAndSeek, payload: &[u8]) -> Result<()> {
if payload.len() > self.size {
return Err(Error::other(
format!("replace_payload: payload ({len} bytes) is larger than reserved capacity ({reserve} bytes)", len = payload.len(), reserve = self.size)
));
}
let offset = self.offset.borrow();
if let Some(offset) = *offset {
to_stream.seek(std::io::SeekFrom::Start(offset))?;
to_stream.write_all(payload)
} else {
Err(Error::other(
"replace_payload: no offset recorded; call write_jumbf() first".to_string(),
))
}
}
}
impl ToBox for PlaceholderDataBox {
fn box_type(&self) -> BoxType {
self.tbox
}
fn payload_size(&self) -> Result<usize> {
Ok(self.size)
}
fn write_payload(&self, to_stream: &mut dyn WriteAndSeek) -> Result<()> {
let offset = to_stream.stream_position()?;
match offset {
0 => {
return Err(Error::other(
"placeholder stream should have some data already",
));
}
_ => {
self.offset.replace(Some(offset));
}
};
let zeros: Vec<u8> = vec![0; self.size];
to_stream.write_all(&zeros)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used)]
#![allow(clippy::panic)]
#![allow(clippy::unwrap_used)]
use std::io::{Cursor, Write};
use hex_literal::hex;
use crate::{
builder::{
to_box::{jumbf_size, write_jumbf},
PlaceholderDataBox, ToBox,
},
BoxType,
};
const RANDOM_BOX_TYPE: BoxType = BoxType(*b"abcd");
#[test]
fn simple_case() {
let expected_jumbf = hex!(
"00000018" "61626364" "00000000000000000000000000000000" );
let pbox = PlaceholderDataBox::new(RANDOM_BOX_TYPE, 16);
assert_eq!(pbox.box_type(), RANDOM_BOX_TYPE);
assert_eq!(pbox.payload_size().unwrap(), 16);
assert_eq!(jumbf_size(&pbox).unwrap(), 24);
let mut jumbf = Cursor::new(Vec::<u8>::new());
write_jumbf(&pbox, &mut jumbf).unwrap();
assert_eq!(*jumbf.get_ref(), expected_jumbf);
let expected_jumbf = hex!(
"00000018" "61626364" "31323334353637383930000000000000" );
pbox.replace_payload(&mut jumbf, &expected_jumbf[8..18])
.unwrap();
assert_eq!(*jumbf.get_ref(), expected_jumbf);
}
#[test]
fn error_write_payload_only() {
let pbox = PlaceholderDataBox::new(RANDOM_BOX_TYPE, 16);
let mut payload = Cursor::new(Vec::<u8>::new());
let err = pbox.write_payload(&mut payload).unwrap_err();
assert_eq!(
"Custom { kind: Other, error: \"placeholder stream should have some data already\" }",
format!("{err:?}")
);
}
#[test]
fn error_payload_too_large() {
let expected_jumbf = hex!(
"00000018" "61626364" "00000000000000000000000000000000" );
let pbox = PlaceholderDataBox::new(RANDOM_BOX_TYPE, 16);
let mut jumbf = Cursor::new(Vec::<u8>::new());
write_jumbf(&pbox, &mut jumbf).unwrap();
assert_eq!(*jumbf.get_ref(), expected_jumbf);
let payload_too_large = [1u8; 17];
let err = pbox
.replace_payload(&mut jumbf, &payload_too_large)
.unwrap_err();
assert_eq!(
"Custom { kind: Other, error: \"replace_payload: payload (17 bytes) is larger than reserved capacity (16 bytes)\" }",
format!("{err:?}")
);
assert_eq!(*jumbf.get_ref(), expected_jumbf);
}
#[test]
fn error_write_jumbf_not_called() {
let pbox = PlaceholderDataBox::new(RANDOM_BOX_TYPE, 16);
let mut jumbf = Cursor::new(Vec::<u8>::new());
let payload = [1u8; 16];
let err = pbox.replace_payload(&mut jumbf, &payload).unwrap_err();
assert_eq!(
"Custom { kind: Other, error: \"replace_payload: no offset recorded; call write_jumbf() first\" }",
format!("{err:?}")
);
assert_eq!(*jumbf.get_ref(), []);
}
#[test]
fn offset() {
let expected_jumbf = hex!(
"41424344" "00000018" "61626364" "00000000000000000000000000000000" );
let pbox = PlaceholderDataBox::new(RANDOM_BOX_TYPE, 16);
assert_eq!(pbox.box_type(), RANDOM_BOX_TYPE);
assert_eq!(pbox.payload_size().unwrap(), 16);
assert_eq!(jumbf_size(&pbox).unwrap(), 24);
let mut jumbf = Cursor::new(Vec::<u8>::new());
jumbf.write_all(b"ABCD").unwrap();
write_jumbf(&pbox, &mut jumbf).unwrap();
assert_eq!(*jumbf.get_ref(), expected_jumbf);
assert_eq!(pbox.offset(), Some(12));
}
#[test]
fn offset_before_write() {
let pbox = PlaceholderDataBox::new(RANDOM_BOX_TYPE, 16);
assert_eq!(pbox.box_type(), RANDOM_BOX_TYPE);
assert_eq!(pbox.payload_size().unwrap(), 16);
assert_eq!(jumbf_size(&pbox).unwrap(), 24);
assert_eq!(pbox.offset(), None);
}
}