#[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)
}
}
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,
})
}
}
#[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}")
}
#[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>>>>();
}
}