use std::cmp;
use std::io;
use std::io::ErrorKind;
use std::io::Read;
use crate::four_cc::FourCC;
const CHUNK_BUFFER_SIZE: usize = 1 << 16;
#[non_exhaustive]
pub(crate) struct BinaryReader {}
impl BinaryReader {
pub(crate) fn read_i8<R: Read>(reader: &mut R) -> Result<i8, io::Error> {
let mut data: [u8; 1] = [0; 1];
reader.read_exact(&mut data)?;
Ok(i8::from_le_bytes(data))
}
pub(crate) fn read_u8<R: Read>(reader: &mut R) -> Result<u8, io::Error> {
let mut data: [u8; 1] = [0; 1];
reader.read_exact(&mut data)?;
Ok(u8::from_le_bytes(data))
}
pub(crate) fn read_i16<R: Read>(reader: &mut R) -> Result<i16, io::Error> {
let mut data: [u8; 2] = [0; 2];
reader.read_exact(&mut data)?;
Ok(i16::from_le_bytes(data))
}
pub(crate) fn read_u16<R: Read>(reader: &mut R) -> Result<u16, io::Error> {
let mut data: [u8; 2] = [0; 2];
reader.read_exact(&mut data)?;
Ok(u16::from_le_bytes(data))
}
pub(crate) fn read_i32<R: Read>(reader: &mut R) -> Result<i32, io::Error> {
let mut data: [u8; 4] = [0; 4];
reader.read_exact(&mut data)?;
Ok(i32::from_le_bytes(data))
}
pub(crate) fn read_u32<R: Read>(reader: &mut R) -> Result<u32, io::Error> {
let mut data: [u8; 4] = [0; 4];
reader.read_exact(&mut data)?;
Ok(u32::from_le_bytes(data))
}
pub(crate) fn read_i16_big_endian<R: Read>(reader: &mut R) -> Result<i16, io::Error> {
let mut data: [u8; 2] = [0; 2];
reader.read_exact(&mut data)?;
Ok(i16::from_be_bytes(data))
}
pub(crate) fn read_i32_big_endian<R: Read>(reader: &mut R) -> Result<i32, io::Error> {
let mut data: [u8; 4] = [0; 4];
reader.read_exact(&mut data)?;
Ok(i32::from_be_bytes(data))
}
pub(crate) fn read_i32_variable_length<R: Read>(reader: &mut R) -> Result<i32, io::Error> {
let mut acc: i32 = 0;
let mut count: i32 = 0;
loop {
let value = BinaryReader::read_u8(reader)? as i32;
acc = (acc << 7) | (value & 127);
if (value & 128) == 0 {
break;
}
count += 1;
if count == 4 {
return Err(io::Error::new(
ErrorKind::InvalidData,
"the length of the value must be equal to or less than 4",
));
}
}
Ok(acc)
}
pub(crate) fn read_four_cc<R: Read>(reader: &mut R) -> Result<FourCC, io::Error> {
let mut data: [u8; 4] = [0; 4];
reader.read_exact(&mut data)?;
Ok(FourCC::from_bytes(data))
}
pub(crate) fn read_fixed_length_string<R: Read>(
reader: &mut R,
length: usize,
) -> Result<String, io::Error> {
let mut data: Vec<u8> = BinaryReader::read_raw_data(reader, length)?;
let mut actual_length: usize = 0;
for value in &data {
if *value == 0 {
break;
}
actual_length += 1;
}
for value in &mut data[0..actual_length] {
if !(9..=126).contains(value) {
*value = 63; }
}
data.truncate(actual_length);
Ok(String::from_utf8(data).unwrap())
}
pub(crate) fn discard_data<R: Read>(reader: &mut R, mut size: usize) -> Result<(), io::Error> {
let mut data: [u8; 4096] = [0; 4096];
while size > 0 {
let chunk = cmp::min(size, data.len());
reader.read_exact(&mut data[..chunk])?;
size -= chunk;
}
Ok(())
}
pub(crate) fn reserve<T>(capacity: usize) -> Vec<T> {
let mut data: Vec<T> = Vec::new();
let _ = data.try_reserve_exact(capacity);
data
}
pub(crate) fn read_raw_data<R: Read>(
reader: &mut R,
size: usize,
) -> Result<Vec<u8>, io::Error> {
const CHUNK: usize = 4096;
let mut data: Vec<u8> = Vec::new();
if data.try_reserve_exact(size).is_ok() {
data.resize(size, 0);
reader.read_exact(&mut data)?;
return Ok(data);
}
let mut buffer: [u8; CHUNK] = [0; CHUNK];
let mut remaining = size;
while remaining > 0 {
let count = cmp::min(remaining, CHUNK);
reader.read_exact(&mut buffer[..count])?;
data.extend_from_slice(&buffer[..count]);
remaining -= count;
}
Ok(data)
}
pub(crate) fn read_chunked<R: Read>(
reader: &mut R,
size: usize,
mut sink: impl FnMut(&[u8]),
) -> Result<(), io::Error> {
let mut buffer: [u8; CHUNK_BUFFER_SIZE] = [0; CHUNK_BUFFER_SIZE];
let mut remaining = size;
while remaining > 0 {
let length = cmp::min(remaining, CHUNK_BUFFER_SIZE);
reader.read_exact(&mut buffer[..length])?;
sink(&buffer[..length]);
remaining -= length;
}
Ok(())
}
pub(crate) fn skip_chunk_padding<R: Read>(
reader: &mut R,
size: usize,
bytes_left_in_list: usize,
) -> Result<(), io::Error> {
if !size.is_multiple_of(2) && bytes_left_in_list > 0 {
BinaryReader::read_u8(reader)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_read_raw_data_does_not_trust_the_declared_size() {
let mut reader = Cursor::new(vec![0_u8; 8]);
let result = BinaryReader::read_raw_data(&mut reader, usize::MAX / 2);
assert!(matches!(
result,
Err(ref error) if error.kind() == ErrorKind::UnexpectedEof
));
}
#[test]
fn test_read_raw_data_reads_the_requested_size() {
let mut reader = Cursor::new(vec![1_u8, 2, 3, 4, 5, 6]);
let data = BinaryReader::read_raw_data(&mut reader, 5).unwrap();
assert_eq!(data, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_reserve_does_not_trust_an_impossible_capacity() {
let data: Vec<u8> = BinaryReader::reserve(usize::MAX / 2);
assert!(data.is_empty());
assert_eq!(data.capacity(), 0);
}
#[test]
fn test_skip_chunk_padding() {
let mut reader = Cursor::new(vec![0xAA, 0xBB]);
BinaryReader::skip_chunk_padding(&mut reader, 3, 4).unwrap();
assert_eq!(reader.position(), 1);
let mut reader = Cursor::new(vec![0xAA, 0xBB]);
BinaryReader::skip_chunk_padding(&mut reader, 4, 4).unwrap();
assert_eq!(reader.position(), 0);
let mut reader = Cursor::new(vec![0xAA, 0xBB]);
BinaryReader::skip_chunk_padding(&mut reader, 3, 0).unwrap();
assert_eq!(reader.position(), 0);
}
}