use std::io::{self, SeekFrom};
pub struct Reader<R> {
inner: R,
start: u64,
end: u64,
}
impl<R: io::Read + io::Seek> Reader<R> {
pub fn try_new(mut inner: R, start: u64, end: u64) -> io::Result<Self> {
assert!(start <= end);
inner.seek(SeekFrom::Start(start))?;
Ok(Self { inner, start, end })
}
}
impl<R: io::Read + io::Seek> io::Read for Reader<R> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut position = self.inner.stream_position()?;
if position < self.start {
position = self.inner.seek(SeekFrom::Start(self.start))?;
}
if position < self.start {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
"Can not read before start of substream",
));
}
if position >= self.end {
return Ok(0);
}
let available_bytes = self.end - position;
if available_bytes >= buf.len() as u64 {
return self.inner.read(buf);
}
self.inner.read(&mut buf[0..(available_bytes as usize)])
}
}
impl<R: io::Seek> io::Seek for Reader<R> {
#[inline]
fn stream_len(&mut self) -> io::Result<u64> {
Ok(self.end - self.start)
}
#[inline]
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
match pos {
SeekFrom::Start(offset) => {
let result = self.inner.seek(SeekFrom::Start(self.start + offset))?;
if self.start <= result {
return Ok(result - self.start);
}
Ok(self.end)
}
SeekFrom::End(end) => {
let target = self.end as i64 + end;
if target < 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Can not seek before start of the stream",
));
}
self.seek(SeekFrom::Start(target as u64))
}
SeekFrom::Current(relative) => {
let current = self.stream_position()?;
let new = current as i64 + relative;
if new < 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Can not seek before start of the stream",
));
}
self.seek(SeekFrom::Start(new as u64))
}
}
}
#[inline]
fn stream_position(&mut self) -> io::Result<u64> {
let pos = self.inner.stream_position()?;
if self.start <= pos {
return Ok(pos - self.start);
}
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Can not seek before start of the stream",
))
}
}
#[cfg(test)]
mod test {
use std::io::{self, Read};
use crate::Reader;
#[test]
fn empty() {
let mut buffer = [0xAB; 12];
let inner = io::Cursor::new(b"");
let mut reader = Reader::try_new(inner, 0, 0).unwrap();
assert!(matches!(reader.read(&mut buffer), Ok(0)));
let inner = io::Cursor::new(b"");
let mut reader = Reader::try_new(inner, 0, 10).unwrap();
assert!(matches!(reader.read(&mut buffer), Ok(0)));
let inner = io::Cursor::new(b"");
let mut reader = Reader::try_new(inner, 5, 10).unwrap();
assert!(matches!(reader.read(&mut buffer), Ok(0)));
}
#[test]
fn simple() {
let mut buffer = [0xAB; 1];
let inner = io::Cursor::new(b"\x01\x02\x03\x04\x05");
let mut reader = Reader::try_new(inner, 1, 2).unwrap();
assert!(matches!(reader.read(&mut buffer), Ok(1)));
assert_eq!(buffer, [0x02]);
let mut buffer = [0xAB; 3];
let inner = io::Cursor::new(b"\x01\x02\x03\x04\x05");
let mut reader = Reader::try_new(inner, 2, 5).unwrap();
assert!(matches!(reader.read(&mut buffer), Ok(3)));
assert_eq!(buffer, [0x03, 0x04, 0x05]);
}
#[test]
fn capping() {
let mut buffer = [0xAB; 3];
let inner = io::Cursor::new(b"\x01\x02\x03\x04\x05");
let mut reader = Reader::try_new(inner, 1, 1).unwrap();
assert!(matches!(reader.read(&mut buffer), Ok(0)));
let mut buffer = [0xAB; 3];
let inner = io::Cursor::new(b"\x01\x02\x03\x04\x05");
let mut reader = Reader::try_new(inner, 1, 2).unwrap();
assert!(matches!(reader.read(&mut buffer), Ok(1)));
assert_eq!(buffer, [0x02, 0xAB, 0xAB]);
let mut buffer = [0xAB; 3];
let inner = io::Cursor::new(b"\x01\x02\x03\x04\x05");
let mut reader = Reader::try_new(inner, 1, 3).unwrap();
assert!(matches!(reader.read(&mut buffer), Ok(2)));
assert_eq!(buffer, [0x02, 0x03, 0xAB]);
}
}