use std::io;
#[allow(clippy::len_without_is_empty)]
pub trait RangeReader {
fn read_exact_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()>;
fn len(&self) -> Option<u64> {
None
}
}
pub(super) fn unexpected_eof() -> io::Error {
io::Error::new(
io::ErrorKind::UnexpectedEof,
"read past the end of the range source",
)
}
pub struct SliceReader<T> {
data: T,
}
impl<T: AsRef<[u8]>> SliceReader<T> {
pub fn new(data: T) -> Self {
Self { data }
}
pub fn into_inner(self) -> T {
self.data
}
}
impl<T: AsRef<[u8]>> RangeReader for SliceReader<T> {
fn read_exact_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> {
let data = self.data.as_ref();
let start = usize::try_from(offset).map_err(|_| unexpected_eof())?;
let end = start.checked_add(buf.len()).ok_or_else(unexpected_eof)?;
let src = data.get(start..end).ok_or_else(unexpected_eof)?;
buf.copy_from_slice(src);
Ok(())
}
fn len(&self) -> Option<u64> {
Some(self.data.as_ref().len() as u64)
}
}
#[cfg(any(unix, windows))]
pub struct FileReader {
file: std::fs::File,
len: u64,
}
#[cfg(any(unix, windows))]
impl FileReader {
pub fn open(path: impl AsRef<std::path::Path>) -> io::Result<Self> {
Self::from_file(std::fs::File::open(path)?)
}
pub fn from_file(file: std::fs::File) -> io::Result<Self> {
let len = file.metadata()?.len();
Ok(Self { file, len })
}
}
#[cfg(any(unix, windows))]
impl RangeReader for FileReader {
fn read_exact_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<()> {
#[cfg(unix)]
{
std::os::unix::fs::FileExt::read_exact_at(&self.file, buf, offset)
}
#[cfg(windows)]
{
use std::os::windows::fs::FileExt;
let mut filled = 0usize;
while filled < buf.len() {
let n = self
.file
.seek_read(&mut buf[filled..], offset + filled as u64)?;
if n == 0 {
return Err(unexpected_eof());
}
filled += n;
}
Ok(())
}
}
fn len(&self) -> Option<u64> {
Some(self.len)
}
}