use std::io::{self, Read, Seek, SeekFrom};
#[derive(Debug)]
pub struct OffsetReader<R> {
inner: R,
base: u64,
len: u64,
pos: u64,
}
impl<R: Read + Seek> OffsetReader<R> {
pub fn new(mut inner: R, base: u64, len: u64) -> io::Result<Self> {
inner.seek(SeekFrom::Start(base))?;
Ok(Self { inner, base, len, pos: 0 })
}
#[must_use]
pub fn len(&self) -> u64 {
self.len
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
impl<R: Read + Seek> Read for OffsetReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let remaining = self.len.saturating_sub(self.pos);
if remaining == 0 {
return Ok(0);
}
let cap = remaining.min(buf.len() as u64) as usize;
let n = self.inner.read(&mut buf[..cap])?;
self.pos += n as u64;
Ok(n)
}
}
impl<R: Read + Seek> Seek for OffsetReader<R> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
let target = match pos {
SeekFrom::Start(n) => n as i64,
SeekFrom::Current(d) => self.pos as i64 + d,
SeekFrom::End(d) => self.len as i64 + d,
};
if target < 0 {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "seek before start of window"));
}
let target = target as u64;
self.inner.seek(SeekFrom::Start(self.base + target))?;
self.pos = target;
Ok(target)
}
}