pub(crate) const MAGIC: &[u8] = b"RIFF";
pub(crate) const OFFSET: usize = 0;
use crate::{
utils::{decode_tags, encode_tags, or_eof, passthrough, read_stack, skip},
Error,
};
use std::io::{Read, Seek, Write};
const TAGS_ID: &[u8; 4] = b"meme";
pub fn read_tags(src: &mut (impl Read + Seek)) -> Result<Vec<String>, Error> {
let _ = read_stack::<12>(src)?; while let Some(chunk_id) = or_eof(read_stack::<4>(src))? {
let chunk_size = u32::from_le_bytes(read_stack::<4>(src)?);
if &chunk_id == TAGS_ID {
return decode_tags(src);
}
skip(src, chunk_size as i64)?;
if chunk_size & 1 == 1 {
skip(src, 1)?;
}
}
Ok(Vec::new())
}
pub fn write_tags(
src: &mut (impl Read + Seek),
dest: &mut impl Write,
tags: impl IntoIterator<Item = impl AsRef<str>>,
) -> Result<(), Error> {
passthrough(src, dest, 4)?;
skip(src, 4)?;
let mut data = Vec::new();
passthrough(src, &mut data, 4)?;
while let Some(chunk_id) = or_eof(read_stack::<4>(src))? {
let chunk_size_bytes = read_stack::<4>(src)?;
let chunk_size = u32::from_le_bytes(chunk_size_bytes);
if &chunk_id == TAGS_ID {
skip(src, chunk_size as i64)?;
if chunk_size & 1 == 1 {
skip(src, 1)?;
}
} else {
data.extend(&chunk_id);
data.extend(&chunk_size_bytes);
if passthrough(src, &mut data, chunk_size as u64)? != chunk_size as u64 {
Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?;
};
if chunk_size & 1 == 1 {
data.write_all(&[0])?;
}
}
}
let mut tags_bytes = Vec::new();
encode_tags(tags, &mut tags_bytes)?;
data.extend(TAGS_ID);
data.extend(&(tags_bytes.len() as u32).to_le_bytes());
data.extend(&tags_bytes);
if tags_bytes.len() & 1 == 1 {
data.push(0);
}
dest.write_all(&(data.len() as u32).to_le_bytes())?;
dest.write_all(&data)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
const TAGS: &[&[u8]] = &[TAGS_ID, &[0x01], &[0; 5]];
const ODD: &[&[u8]] = &[&[0; 4], &[0x01], &[0; 5]];
#[test]
fn odd_size_chunk() {
let src = &[MAGIC, &[0x0E], &[0; 7], &ODD.concat()].concat();
assert_eq!(read_tags(&mut Cursor::new(src)).unwrap(), Vec::<String>::new());
let mut dest = Vec::new();
write_tags(&mut Cursor::new(src), &mut dest, Vec::<String>::new()).unwrap();
let expected = &[MAGIC, &[0x18], &[0; 7], &ODD.concat(), &TAGS.concat()].concat();
assert_eq!(&dest, expected);
}
}
crate::utils::standard_tests!("webp");