use crate::*;
pub enum NextChunk {
NotEnoughData(u64),
U8(u8),
U32(u32),
Bytes(Vec<u8>),
}
pub struct SafeHeaderReader<'a, T: std::io::Read + std::io::Seek> {
buf: &'a mut T,
spos: u64,
read: u64,
len: u64,
}
impl<'a, T: std::io::Read + std::io::Seek> SafeHeaderReader<'a, T> {
pub fn new(buf: &'a mut T) -> Result<Self, Error> {
let spos = buf.stream_position()?;
let len = buf.seek(std::io::SeekFrom::End(0))? - spos;
buf.seek(std::io::SeekFrom::Start(spos))?;
Ok(Self {
spos,
len,
buf,
read: 0,
})
}
pub fn next_u8(&mut self) -> Result<NextChunk, Error> {
if self.len < self.read + 1 {
self.buf.seek(std::io::SeekFrom::Start(self.spos))?;
return Ok(NextChunk::NotEnoughData(self.read + 1 - self.len));
}
let mut dest = [0u8; 1];
self.buf.read_exact(&mut dest)?;
self.read += 1;
Ok(NextChunk::U8(dest[0]))
}
pub fn next_u32(&mut self) -> Result<NextChunk, Error> {
if self.len < self.read + 4u64 {
self.buf.seek(std::io::SeekFrom::Start(self.spos))?;
return Ok(NextChunk::NotEnoughData(self.read + 4u64 - self.len));
}
let mut dest = [0u8; 4];
self.buf.read_exact(&mut dest)?;
self.read += 4u64;
Ok(NextChunk::U32(u32::from_le_bytes(dest)))
}
pub fn next_bytes(&mut self, capacity: u64) -> Result<NextChunk, Error> {
if self.len < self.read + capacity {
self.buf.seek(std::io::SeekFrom::Start(self.spos))?;
return Ok(NextChunk::NotEnoughData(self.read + capacity - self.len));
}
let mut dest = vec![0u8; capacity as usize];
self.buf.read_exact(&mut dest)?;
self.read += capacity;
Ok(NextChunk::Bytes(dest))
}
}
#[cfg(test)]
mod tests {
use super::{NextChunk, SafeHeaderReader};
use std::io::{Cursor, Seek};
#[test]
fn safe_header_reader_reads_chunks_in_order() {
let bytes = vec![7_u8, 1, 2, 3, 4, 10, 11, 12, 13];
let mut cursor = Cursor::new(bytes);
let mut reader = SafeHeaderReader::new(&mut cursor).expect("new");
match reader.next_u8().expect("u8") {
NextChunk::U8(v) => assert_eq!(v, 7),
_ => panic!("expected U8"),
}
match reader.next_bytes(4).expect("bytes") {
NextChunk::Bytes(v) => assert_eq!(v, vec![1, 2, 3, 4]),
_ => panic!("expected Bytes"),
}
match reader.next_u32().expect("u32") {
NextChunk::U32(v) => assert_eq!(v, u32::from_le_bytes([10, 11, 12, 13])),
_ => panic!("expected U32"),
}
}
#[test]
fn safe_header_reader_not_enough_resets_stream_position() {
let bytes = vec![1_u8, 2, 3];
let mut cursor = Cursor::new(bytes);
let start = cursor.stream_position().expect("start pos");
let mut reader = SafeHeaderReader::new(&mut cursor).expect("new");
match reader.next_bytes(8).expect("next bytes") {
NextChunk::NotEnoughData(need) => assert_eq!(need, 5),
_ => panic!("expected NotEnoughData"),
}
assert_eq!(cursor.stream_position().expect("pos"), start);
}
}