#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
use crate::convert::TryToUsize;
use crate::error::FormatError;
pub trait FileSource {
fn len(&self) -> u64;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<(), FormatError>;
fn read_exact_at(&self, offset: u64, len: usize) -> Result<Vec<u8>, FormatError> {
let end = offset
.checked_add(len as u64)
.ok_or(FormatError::OffsetOverflow {
offset,
length: len as u64,
})?;
if end > self.len() {
return Err(FormatError::UnexpectedEof {
expected: end.to_usize().unwrap_or(usize::MAX),
available: self.len().to_usize().unwrap_or(usize::MAX),
});
}
let mut buf = vec![0u8; len];
self.read_at(offset, &mut buf)?;
Ok(buf)
}
}
impl<S: FileSource + ?Sized> FileSource for &S {
fn len(&self) -> u64 {
(**self).len()
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<(), FormatError> {
(**self).read_at(offset, buf)
}
}
#[cfg(feature = "std")]
impl<S: FileSource + ?Sized> FileSource for std::boxed::Box<S> {
fn len(&self) -> u64 {
(**self).len()
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<(), FormatError> {
(**self).read_at(offset, buf)
}
}
#[derive(Debug, Clone, Copy)]
pub struct BytesSource<T>(pub T);
impl<T: AsRef<[u8]>> BytesSource<T> {
pub fn new(bytes: T) -> Self {
BytesSource(bytes)
}
pub fn as_bytes(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<T: AsRef<[u8]>> FileSource for BytesSource<T> {
fn len(&self) -> u64 {
self.0.as_ref().len() as u64
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<(), FormatError> {
let bytes = self.0.as_ref();
let start = offset.to_usize()?;
let end = start
.checked_add(buf.len())
.ok_or(FormatError::OffsetOverflow {
offset,
length: buf.len() as u64,
})?;
if end > bytes.len() {
return Err(FormatError::UnexpectedEof {
expected: end,
available: bytes.len(),
});
}
buf.copy_from_slice(&bytes[start..end]);
Ok(())
}
}
#[cfg(feature = "std")]
pub struct ReadSeekSource<R> {
inner: std::sync::Mutex<R>,
len: u64,
}
#[cfg(feature = "std")]
impl<R: std::io::Read + std::io::Seek> ReadSeekSource<R> {
pub fn new(mut reader: R) -> Result<Self, FormatError> {
let len = reader
.seek(std::io::SeekFrom::End(0))
.map_err(|e| FormatError::Source(format_io(&e)))?;
Ok(ReadSeekSource {
inner: std::sync::Mutex::new(reader),
len,
})
}
pub fn into_inner(self) -> R {
self.inner
.into_inner()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
}
#[cfg(feature = "std")]
impl<R: std::io::Read + std::io::Seek> FileSource for ReadSeekSource<R> {
fn len(&self) -> u64 {
self.len
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<(), FormatError> {
let end = offset
.checked_add(buf.len() as u64)
.ok_or(FormatError::OffsetOverflow {
offset,
length: buf.len() as u64,
})?;
if end > self.len {
return Err(FormatError::UnexpectedEof {
expected: end.to_usize().unwrap_or(usize::MAX),
available: self.len.to_usize().unwrap_or(usize::MAX),
});
}
let mut guard = self
.inner
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
guard
.seek(std::io::SeekFrom::Start(offset))
.map_err(|e| FormatError::Source(format_io(&e)))?;
guard
.read_exact(buf)
.map_err(|e| FormatError::Source(format_io(&e)))?;
Ok(())
}
}
#[cfg(feature = "std")]
fn format_io(e: &std::io::Error) -> std::string::String {
std::format!("{e}")
}
pub struct Cursor<'s> {
source: &'s dyn FileSource,
pos: u64,
}
impl<'s> Cursor<'s> {
pub fn new(source: &'s dyn FileSource) -> Self {
Cursor { source, pos: 0 }
}
pub fn at(source: &'s dyn FileSource, pos: u64) -> Self {
Cursor { source, pos }
}
pub fn source(&self) -> &'s dyn FileSource {
self.source
}
pub fn len(&self) -> u64 {
self.source.len()
}
pub fn is_empty(&self) -> bool {
self.source.is_empty()
}
pub fn position(&self) -> u64 {
self.pos
}
pub fn seek(&mut self, pos: u64) {
self.pos = pos;
}
pub fn advance(&mut self, delta: u64) -> Result<(), FormatError> {
self.pos = self
.pos
.checked_add(delta)
.ok_or(FormatError::OffsetOverflow {
offset: self.pos,
length: delta,
})?;
Ok(())
}
pub fn remaining(&self) -> u64 {
self.source.len().saturating_sub(self.pos)
}
pub fn bytes_at(&self, offset: u64, n: usize) -> Result<Vec<u8>, FormatError> {
self.source.read_exact_at(offset, n)
}
pub fn read_bytes(&mut self, n: usize) -> Result<Vec<u8>, FormatError> {
let out = self.source.read_exact_at(self.pos, n)?;
self.advance(n as u64)?;
Ok(out)
}
pub fn read_u8(&mut self) -> Result<u8, FormatError> {
let mut b = [0u8; 1];
self.source.read_at(self.pos, &mut b)?;
self.advance(1)?;
Ok(b[0])
}
pub fn read_u16(&mut self) -> Result<u16, FormatError> {
let mut b = [0u8; 2];
self.source.read_at(self.pos, &mut b)?;
self.advance(2)?;
Ok(u16::from_le_bytes(b))
}
pub fn read_u32(&mut self) -> Result<u32, FormatError> {
let mut b = [0u8; 4];
self.source.read_at(self.pos, &mut b)?;
self.advance(4)?;
Ok(u32::from_le_bytes(b))
}
pub fn read_u64(&mut self) -> Result<u64, FormatError> {
let mut b = [0u8; 8];
self.source.read_at(self.pos, &mut b)?;
self.advance(8)?;
Ok(u64::from_le_bytes(b))
}
pub fn read_uint(&mut self, size: u8) -> Result<u64, FormatError> {
let v = match size {
1 => u64::from(self.read_u8()?),
2 => u64::from(self.read_u16()?),
4 => u64::from(self.read_u32()?),
8 => self.read_u64()?,
_ => return Err(FormatError::InvalidOffsetSize(size)),
};
Ok(v)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "std"))]
use alloc::vec;
#[test]
fn bytes_source_reads_and_reports_len() {
let data = (0u8..=255).collect::<Vec<u8>>();
let src = BytesSource::new(data.clone());
assert_eq!(src.len(), 256);
assert!(!src.is_empty());
let mut buf = [0u8; 4];
src.read_at(10, &mut buf).unwrap();
assert_eq!(buf, [10, 11, 12, 13]);
let owned = src.read_exact_at(250, 6).unwrap();
assert_eq!(owned, vec![250, 251, 252, 253, 254, 255]);
}
#[test]
fn bytes_source_short_read_is_eof() {
let src = BytesSource::new(vec![1u8, 2, 3]);
let mut buf = [0u8; 4];
let err = src.read_at(0, &mut buf).unwrap_err();
assert!(matches!(err, FormatError::UnexpectedEof { .. }));
let mut ok = [0u8; 3];
src.read_at(0, &mut ok).unwrap();
assert_eq!(ok, [1, 2, 3]);
}
#[test]
fn bytes_source_offset_past_end_is_eof() {
let src = BytesSource::new(vec![0u8; 8]);
let mut buf = [0u8; 1];
assert!(matches!(
src.read_at(8, &mut buf).unwrap_err(),
FormatError::UnexpectedEof { .. }
));
src.read_at(8, &mut []).unwrap();
}
#[test]
fn read_exact_at_rejects_oversized_len_without_allocating() {
let src = BytesSource::new(vec![1u8, 2, 3, 4]);
assert!(matches!(
src.read_exact_at(0, usize::MAX).unwrap_err(),
FormatError::UnexpectedEof { .. }
));
assert_eq!(src.read_exact_at(1, 3).unwrap(), vec![2, 3, 4]);
}
#[test]
fn empty_source() {
let src = BytesSource::new(Vec::<u8>::new());
assert_eq!(src.len(), 0);
assert!(src.is_empty());
}
#[test]
fn forwarding_through_reference() {
let src = BytesSource::new(vec![9u8, 8, 7]);
let r: &dyn FileSource = &src;
let mut buf = [0u8; 2];
r.read_at(1, &mut buf).unwrap();
assert_eq!(buf, [8, 7]);
}
#[cfg(feature = "std")]
#[test]
fn read_seek_source_matches_in_memory() {
use std::io::Cursor;
let data = (0u8..200).collect::<Vec<u8>>();
let mem = BytesSource::new(data.clone());
let seek = ReadSeekSource::new(Cursor::new(data.clone())).unwrap();
assert_eq!(seek.len(), mem.len());
for &(off, len) in &[(0u64, 1usize), (5, 10), (199, 1), (100, 50)] {
let a = mem.read_exact_at(off, len).unwrap();
let b = seek.read_exact_at(off, len).unwrap();
assert_eq!(a, b, "mismatch at offset {off} len {len}");
}
}
#[cfg(feature = "std")]
#[test]
fn read_seek_source_past_end_is_error() {
use std::io::Cursor;
let seek = ReadSeekSource::new(Cursor::new(vec![1u8, 2, 3, 4])).unwrap();
let mut buf = [0u8; 3];
assert!(matches!(
seek.read_at(2, &mut buf).unwrap_err(),
FormatError::UnexpectedEof { .. }
));
}
#[cfg(feature = "std")]
#[test]
fn read_seek_source_is_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<ReadSeekSource<std::io::Cursor<Vec<u8>>>>();
}
#[test]
fn cursor_sequential_little_endian_reads() {
let mut data = Vec::new();
data.push(0xABu8);
data.extend_from_slice(&0x1234u16.to_le_bytes());
data.extend_from_slice(&0xDEAD_BEEFu32.to_le_bytes());
data.extend_from_slice(&0x0102_0304_0506_0708u64.to_le_bytes());
let src = BytesSource::new(data);
let mut c = Cursor::new(&src);
assert_eq!(c.position(), 0);
assert_eq!(c.read_u8().unwrap(), 0xAB);
assert_eq!(c.read_u16().unwrap(), 0x1234);
assert_eq!(c.read_u32().unwrap(), 0xDEAD_BEEF);
assert_eq!(c.read_u64().unwrap(), 0x0102_0304_0506_0708);
assert_eq!(c.position(), 15);
assert_eq!(c.remaining(), 0);
}
#[test]
fn cursor_read_uint_widths() {
let data = vec![0x01u8, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
let src = BytesSource::new(data);
let mut c = Cursor::new(&src);
assert_eq!(c.read_uint(1).unwrap(), 0x01);
let mut c = Cursor::new(&src);
assert_eq!(c.read_uint(2).unwrap(), 0x0201);
let mut c = Cursor::new(&src);
assert_eq!(c.read_uint(4).unwrap(), 0x0403_0201);
let mut c = Cursor::new(&src);
assert_eq!(c.read_uint(8).unwrap(), 0x0807_0605_0403_0201);
let mut c = Cursor::new(&src);
assert!(matches!(
c.read_uint(3).unwrap_err(),
FormatError::InvalidOffsetSize(3)
));
}
#[test]
fn cursor_random_access_and_seek() {
let data = (0u8..32).collect::<Vec<u8>>();
let src = BytesSource::new(data);
let mut c = Cursor::new(&src);
assert_eq!(c.bytes_at(10, 3).unwrap(), vec![10, 11, 12]);
assert_eq!(c.position(), 0);
c.seek(20);
assert_eq!(c.read_bytes(4).unwrap(), vec![20, 21, 22, 23]);
assert_eq!(c.position(), 24);
}
#[test]
fn cursor_eof_is_reported() {
let src = BytesSource::new(vec![1u8, 2, 3]);
let mut c = Cursor::at(&src, 2);
assert_eq!(c.read_u8().unwrap(), 3);
assert!(matches!(
c.read_u8().unwrap_err(),
FormatError::UnexpectedEof { .. }
));
}
#[cfg(feature = "std")]
#[test]
fn cursor_drives_the_same_bytes_over_any_backend() {
let data = (0u8..=255).collect::<Vec<u8>>();
let mem = BytesSource::new(data.clone());
let seek = ReadSeekSource::new(std::io::Cursor::new(data)).unwrap();
let mut cm = Cursor::at(&mem, 100);
let mut cs = Cursor::at(&seek, 100);
assert_eq!(cm.read_u32().unwrap(), cs.read_u32().unwrap());
assert_eq!(cm.read_uint(8).unwrap(), cs.read_uint(8).unwrap());
assert_eq!(cm.bytes_at(0, 16).unwrap(), cs.bytes_at(0, 16).unwrap());
}
}