use std::cmp;
use std::io;
use super::{FiniteStream, ReadBytes, SeekBuffered};
#[inline(always)]
fn out_of_bounds_error<T>() -> io::Result<T> {
Err(io::Error::new(io::ErrorKind::UnexpectedEof, "out of bounds"))
}
pub struct ScopedStream<B: ReadBytes> {
inner: B,
start: u64,
len: u64,
read: u64,
}
impl<B: ReadBytes> ScopedStream<B> {
pub fn new(inner: B, len: u64) -> Self {
ScopedStream { start: inner.pos(), inner, len, read: 0 }
}
pub fn inner(&self) -> &B {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut B {
&mut self.inner
}
pub fn ignore(&mut self) -> io::Result<()> {
self.inner.ignore_bytes(self.len - self.read)
}
pub fn into_inner(self) -> B {
self.inner
}
}
impl<B: ReadBytes> FiniteStream for ScopedStream<B> {
fn byte_len(&self) -> u64 {
self.len
}
fn bytes_read(&self) -> u64 {
self.read
}
fn bytes_available(&self) -> u64 {
self.len - self.read
}
}
impl<B: ReadBytes> ReadBytes for ScopedStream<B> {
#[inline(always)]
fn read_byte(&mut self) -> io::Result<u8> {
if self.len - self.read < 1 {
return out_of_bounds_error();
}
self.read += 1;
self.inner.read_byte()
}
#[inline(always)]
fn read_double_bytes(&mut self) -> io::Result<[u8; 2]> {
if self.len - self.read < 2 {
return out_of_bounds_error();
}
self.read += 2;
self.inner.read_double_bytes()
}
#[inline(always)]
fn read_triple_bytes(&mut self) -> io::Result<[u8; 3]> {
if self.len - self.read < 3 {
return out_of_bounds_error();
}
self.read += 3;
self.inner.read_triple_bytes()
}
#[inline(always)]
fn read_quad_bytes(&mut self) -> io::Result<[u8; 4]> {
if self.len - self.read < 4 {
return out_of_bounds_error();
}
self.read += 4;
self.inner.read_quad_bytes()
}
fn read_buf(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let scoped_len = cmp::min(self.len - self.read, buf.len() as u64) as usize;
let result = self.inner.read_buf(&mut buf[0..scoped_len])?;
self.read += result as u64;
Ok(result)
}
fn read_buf_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
if self.len - self.read < buf.len() as u64 {
return out_of_bounds_error();
}
self.read += buf.len() as u64;
self.inner.read_buf_exact(buf)
}
#[inline(always)]
fn scan_bytes_aligned<'a>(
&mut self,
pattern: &[u8],
align: usize,
buf: &'a mut [u8],
) -> io::Result<&'a mut [u8]> {
if self.len - self.read < buf.len() as u64 {
return out_of_bounds_error();
}
let result = self.inner.scan_bytes_aligned(pattern, align, buf)?;
self.read += result.len() as u64;
Ok(result)
}
#[inline(always)]
fn ignore_bytes(&mut self, count: u64) -> io::Result<()> {
if self.len - self.read < count {
return out_of_bounds_error();
}
self.read += count;
self.inner.ignore_bytes(count)
}
#[inline(always)]
fn pos(&self) -> u64 {
self.inner.pos()
}
}
impl<B: ReadBytes + SeekBuffered> SeekBuffered for ScopedStream<B> {
#[inline(always)]
fn ensure_seekback_buffer(&mut self, len: usize) {
self.inner.ensure_seekback_buffer(len)
}
#[inline(always)]
fn unread_buffer_len(&self) -> usize {
self.inner.unread_buffer_len().min((self.len - self.read) as usize)
}
#[inline(always)]
fn read_buffer_len(&self) -> usize {
self.inner.read_buffer_len().min(self.read as usize)
}
#[inline(always)]
fn seek_buffered(&mut self, pos: u64) -> u64 {
self.inner.seek_buffered(pos.clamp(self.start, self.start + self.len))
}
#[inline(always)]
fn seek_buffered_rel(&mut self, delta: isize) -> u64 {
let max_back = self.read.min(std::isize::MAX as u64) as isize;
let max_forward = (self.len - self.read).min(std::isize::MAX as u64) as isize;
self.inner.seek_buffered_rel(delta.clamp(-max_back, max_forward))
}
}