use std::io::{Read, Result as IoResult};
use crate::Error;
#[inline]
pub fn advance_read<R: Read + ?Sized>(reader: &mut R, mut len: usize) -> Result<(), Error> {
let mut buf = [0u8; 4096];
while len > 0 {
let chunk = len.min(buf.len());
reader.read_exact(&mut buf[..chunk]).map_err(Error::Io)?;
len -= chunk;
}
Ok(())
}
#[derive(Clone, Copy, Debug)]
pub struct SliceReader<'a> {
inner: &'a [u8],
original_len: usize,
}
impl<'a> SliceReader<'a> {
#[inline]
pub fn new(slice: &'a [u8]) -> Self {
Self {
original_len: slice.len(),
inner: slice,
}
}
#[inline]
pub fn remaining(&self) -> &[u8] {
self.inner
}
#[inline]
pub fn consumed_len(&self) -> usize {
self.original_len - self.inner.len()
}
#[inline]
pub fn consume(&mut self, n: usize) -> Result<(), Error> {
if n > self.inner.len() {
return Err(Error::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected EOF while skipping",
)));
}
self.inner = &self.inner[n..];
Ok(())
}
#[inline]
pub fn sub(&self, offset: usize, len: usize) -> Result<SliceReader<'a>, Error> {
let inner_offset = offset.checked_sub(self.consumed_len()).ok_or_else(|| {
Error::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"SliceReader::sub: offset precedes current cursor",
))
})?;
let end = inner_offset.checked_add(len).ok_or_else(|| {
Error::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"SliceReader::sub: offset + len overflow",
))
})?;
if end > self.inner.len() {
return Err(Error::OptimisedSubReaderOverrun);
}
Ok(SliceReader::new(&self.inner[inner_offset..end]))
}
}
impl Read for SliceReader<'_> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
let n = buf.len().min(self.inner.len());
buf[..n].copy_from_slice(&self.inner[..n]);
self.inner = &self.inner[n..];
Ok(n)
}
}
pub unsafe trait BorrowedReader: Read {
fn peek_bytes(&self, n: usize) -> Result<&[u8], Error>;
fn advance(&mut self, n: usize) -> Result<(), Error>;
fn position(&self) -> usize;
#[inline]
fn remaining(&self) -> &[u8] {
debug_assert!(
false,
"BorrowedReader::remaining() default impl invoked — your impl must \
override it (returns &[]; the macro-emitted walk_<field> Wire fast \
path will produce silently empty field views otherwise)"
);
&[]
}
}
unsafe impl BorrowedReader for &[u8] {
#[inline]
fn peek_bytes(&self, n: usize) -> Result<&[u8], Error> {
self.get(..n).ok_or_else(|| {
Error::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected EOF while peeking borrowed bytes",
))
})
}
#[inline]
fn advance(&mut self, n: usize) -> Result<(), Error> {
if n > self.len() {
return Err(Error::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected EOF while advancing slice reader",
)));
}
*self = &self[n..];
Ok(())
}
#[inline]
fn position(&self) -> usize {
0
}
#[inline]
fn remaining(&self) -> &[u8] {
self
}
}
unsafe impl<'a> BorrowedReader for SliceReader<'a> {
#[inline]
fn peek_bytes(&self, n: usize) -> Result<&[u8], Error> {
self.inner.get(..n).ok_or_else(|| {
Error::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected EOF while peeking borrowed bytes",
))
})
}
#[inline]
fn advance(&mut self, n: usize) -> Result<(), Error> {
self.consume(n)
}
#[inline]
fn position(&self) -> usize {
self.consumed_len()
}
#[inline]
fn remaining(&self) -> &[u8] {
self.inner
}
}
#[inline]
pub fn read_borrowed_bytes<'r, R: BorrowedReader + ?Sized>(
reader: &'r mut R,
n: usize,
) -> Result<&'r [u8], Error> {
let peeked = reader.peek_bytes(n)?;
let ptr = peeked.as_ptr();
reader.advance(n)?;
let slice: &'r [u8] = unsafe { std::slice::from_raw_parts(ptr, n) };
Ok(slice)
}
#[inline]
pub fn take_bytes_slice<'a>(reader: &mut &'a [u8], n: usize) -> Result<&'a [u8], Error> {
if n > reader.len() {
return Err(Error::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected EOF while taking borrowed bytes",
)));
}
let (head, tail) = reader.split_at(n);
*reader = tail;
Ok(head)
}
#[inline]
pub fn take_bytes_reader<'r, 'a: 'r>(
reader: &'r mut SliceReader<'a>,
n: usize,
) -> Result<&'a [u8], Error> {
if n > reader.inner.len() {
return Err(Error::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected EOF while taking borrowed bytes",
)));
}
let (head, tail) = reader.inner.split_at(n);
reader.inner = tail;
Ok(head)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn slice_reader_position_tracks_consumed() {
let data = [0u8, 1, 2, 3, 4];
let mut r = SliceReader::new(&data);
assert_eq!(r.position(), 0);
r.consume(2).unwrap();
assert_eq!(r.position(), 2);
r.consume(1).unwrap();
assert_eq!(r.position(), 3);
}
#[test]
fn slice_reader_sub_carves_subrange() {
let data = [0u8, 1, 2, 3, 4, 5];
let r = SliceReader::new(&data);
let sub = r.sub(2, 3).unwrap();
assert_eq!(sub.remaining(), &[2, 3, 4]);
}
#[test]
fn slice_reader_sub_rejects_overflow() {
let data = [0u8, 1, 2, 3];
let r = SliceReader::new(&data);
assert!(matches!(r.sub(2, 3), Err(Error::OptimisedSubReaderOverrun)));
}
#[test]
fn slice_reader_sub_after_consume() {
let data = [0u8, 1, 2, 3, 4, 5];
let mut r = SliceReader::new(&data);
r.consume(2).unwrap();
let sub = r.sub(3, 2).unwrap();
assert_eq!(sub.remaining(), &[3, 4]);
}
#[test]
fn slice_reader_sub_rejects_offset_before_cursor() {
let data = [0u8, 1, 2, 3];
let mut r = SliceReader::new(&data);
r.consume(2).unwrap();
assert!(r.sub(1, 1).is_err());
}
#[test]
fn take_bytes_slice_advances_and_returns_borrow() {
let data: &[u8] = &[1, 2, 3, 4];
let mut cursor = data;
let taken = take_bytes_slice(&mut cursor, 2).unwrap();
assert_eq!(taken, &[1, 2]);
assert_eq!(cursor, &[3, 4]);
}
#[test]
fn take_bytes_reader_advances_and_returns_borrow() {
let data = [1u8, 2, 3, 4];
let mut r = SliceReader::new(&data);
let taken = take_bytes_reader(&mut r, 3).unwrap();
assert_eq!(taken, &[1, 2, 3]);
assert_eq!(r.remaining(), &[4]);
}
}