use crate::error::ParseError;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct SliceReader<'a> {
buf: &'a [u8],
offset: usize,
}
impl<'a> SliceReader<'a> {
#[inline(always)]
pub fn new(buf: &'a [u8]) -> Self {
Self { buf, offset: 0 }
}
#[cfg(test)]
pub fn new_with_offset(offset: usize, buf: &'a [u8]) -> Self {
Self { buf, offset }
}
#[inline(always)]
pub fn offset(&self) -> usize {
self.offset
}
#[inline(always)]
pub fn remaining(&self) -> usize {
self.buf.len()
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
#[inline(always)]
pub fn as_slice(&self) -> &'a [u8] {
self.buf
}
#[inline(always)]
pub fn read_u8(&mut self) -> Result<u8, ParseError> {
match self.buf.split_first() {
Some((&v, rest)) => {
self.buf = rest;
self.offset += 1;
Ok(v)
}
None => Err(ParseError::eof(self.offset, 1, 0)),
}
}
#[inline(always)]
pub fn read_array<const N: usize>(&mut self) -> Result<[u8; N], ParseError> {
match self.buf.split_first_chunk::<N>() {
Some((chunk, rest)) => {
self.buf = rest;
self.offset += N;
Ok(*chunk)
}
None => Err(ParseError::eof(self.offset, N, self.buf.len())),
}
}
#[inline(always)]
pub fn read_u16_be(&mut self) -> Result<u16, ParseError> {
self.read_array::<2>().map(u16::from_be_bytes)
}
#[inline(always)]
pub fn read_u32_be(&mut self) -> Result<u32, ParseError> {
self.read_array::<4>().map(u32::from_be_bytes)
}
#[inline(always)]
pub fn read_u64_be(&mut self) -> Result<u64, ParseError> {
self.read_array::<8>().map(u64::from_be_bytes)
}
#[inline(always)]
pub fn read_u128_be(&mut self) -> Result<u128, ParseError> {
self.read_array::<16>().map(u128::from_be_bytes)
}
#[inline(always)]
pub fn read_i32_be(&mut self) -> Result<i32, ParseError> {
self.read_array::<4>().map(i32::from_be_bytes)
}
#[inline(always)]
pub fn read_i64_be(&mut self) -> Result<i64, ParseError> {
self.read_array::<8>().map(i64::from_be_bytes)
}
#[inline(always)]
pub fn read_f32_be(&mut self) -> Result<f32, ParseError> {
self.read_array::<4>().map(f32::from_be_bytes)
}
#[inline(always)]
pub fn read_f64_be(&mut self) -> Result<f64, ParseError> {
self.read_array::<8>().map(f64::from_be_bytes)
}
#[inline]
pub fn read_bytes(&mut self, len: usize) -> Result<&'a [u8], ParseError> {
match self.buf.split_at_checked(len) {
Some((head, tail)) => {
self.buf = tail;
self.offset += len;
Ok(head)
}
None => Err(ParseError::eof(self.offset, len, self.buf.len())),
}
}
#[inline]
pub fn take_slice(&mut self, len: usize) -> Result<SliceReader<'a>, ParseError> {
let start = self.offset;
self.read_bytes(len)
.map(|buf| SliceReader { buf, offset: start })
}
#[inline(always)]
pub fn peek_u8(&mut self) -> Result<u8, ParseError> {
match self.buf.split_first() {
Some((&v, _rest)) => Ok(v),
None => Err(ParseError::eof(self.offset, 1, 0)),
}
}
#[inline(always)]
pub fn peek_u16_be(&self) -> Result<u16, ParseError> {
self.peek_array::<2>().map(u16::from_be_bytes)
}
#[inline(always)]
pub fn peek_u32_be(&self) -> Result<u32, ParseError> {
self.peek_array::<4>().map(u32::from_be_bytes)
}
#[inline(always)]
pub fn peek_array<const N: usize>(&self) -> Result<[u8; N], ParseError> {
self.buf
.first_chunk::<N>()
.copied()
.ok_or_else(|| ParseError::eof(self.offset, N, self.buf.len()))
}
#[inline]
pub fn read_padded<const N: usize>(&mut self, len: usize) -> Result<[u8; N], ParseError> {
if len > N {
return Err(ParseError::invalid_padding_length(self.offset, len, N));
}
let src = self.read_bytes(len)?;
let mut out = [0u8; N];
out[..len].copy_from_slice(src);
Ok(out)
}
#[inline]
pub fn read_unsigned32_be(&mut self, len: usize) -> Result<u32, ParseError> {
Ok(match len {
0 => 0,
1 => u32::from(self.read_array::<1>()?[0]),
2 => u32::from(u16::from_be_bytes(self.read_array::<2>()?)),
3 => {
let b = self.read_array::<3>()?;
u32::from_be_bytes([0, b[0], b[1], b[2]])
}
4 => u32::from_be_bytes(self.read_array::<4>()?),
_ => return Err(ParseError::invalid_padding_length(self.offset, len, 4)),
})
}
#[inline]
pub fn read_unsigned64_be(&mut self, len: usize) -> Result<u64, ParseError> {
Ok(match len {
0 => 0,
1 => u64::from(self.read_array::<1>()?[0]),
2 => u64::from(u16::from_be_bytes(self.read_array::<2>()?)),
3 => {
let b = self.read_array::<3>()?;
u64::from(u32::from_be_bytes([0, b[0], b[1], b[2]]))
}
4 => u64::from(u32::from_be_bytes(self.read_array::<4>()?)),
5 => {
let b = self.read_array::<5>()?;
u64::from_be_bytes([0, 0, 0, b[0], b[1], b[2], b[3], b[4]])
}
6 => {
let b = self.read_array::<6>()?;
u64::from_be_bytes([0, 0, b[0], b[1], b[2], b[3], b[4], b[5]])
}
7 => {
let b = self.read_array::<7>()?;
u64::from_be_bytes([0, b[0], b[1], b[2], b[3], b[4], b[5], b[6]])
}
8 => u64::from_be_bytes(self.read_array::<8>()?),
_ => return Err(ParseError::invalid_padding_length(self.offset, len, 8)),
})
}
#[inline]
pub fn read_signed32_be(&mut self, len: usize) -> Result<i32, ParseError> {
let v = self.read_unsigned32_be(len)? as i32;
let shift = ((4 - len) * 8) & 31; Ok((v << shift) >> shift)
}
#[inline]
pub fn read_signed64_be(&mut self, len: usize) -> Result<i64, ParseError> {
let v = self.read_unsigned64_be(len)? as i64;
let shift = ((8 - len) * 8) & 63; Ok((v << shift) >> shift)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reads_advance_and_track_offset() {
let data = [0x00, 0x01, 0x02, 0x03, 0x04];
let mut r = SliceReader::new(&data);
assert_eq!(r.read_u8(), Ok(0x00));
assert_eq!(r.read_u16_be(), Ok(0x0102));
assert_eq!(r.offset(), 3);
assert_eq!(r.remaining(), 2);
}
#[test]
fn eof_reports_offset_needed_available() {
let data = [0xAA, 0xBB];
let mut r = SliceReader::new(&data);
let _ = r.read_u8().unwrap();
let err = r.read_u32_be();
assert_eq!(err, Err(ParseError::eof(1, 4, 1)));
assert_eq!(err.map_err(|x| x.is_incomplete()), Err(true));
}
#[test]
fn copy_gives_free_checkpoint() {
let data = [1, 2, 3, 4];
let mut r = SliceReader::new(&data);
let save = r; let _ = r.read_u16_be().unwrap();
assert_eq!(r.offset(), 2);
r = save; assert_eq!(r.offset(), 0);
}
#[test]
fn peek_does_not_advance() {
let data = [0x12, 0x34];
let r = SliceReader::new(&data);
assert_eq!(r.peek_u16_be(), Ok(0x1234));
assert_eq!(r.offset(), 0);
}
#[test]
fn take_slice_carries_absolute_offset() {
let data = [0, 1, 2, 3, 4, 5];
let mut r = SliceReader::new(&data);
let _ = r.read_u16_be().unwrap();
let sub = r.take_slice(3);
assert_eq!(sub, Ok(SliceReader::new_with_offset(2, &[2, 3, 4])));
}
#[test]
fn len_equal_to_n_reads_full_no_padding() {
let data = [0xDE, 0xAD, 0xBE, 0xEF];
let mut r = SliceReader::new(&data);
let out = r.read_padded::<4>(4);
assert_eq!(out, Ok([0xDE, 0xAD, 0xBE, 0xEF]));
assert_eq!(r.offset(), 4);
assert_eq!(r.remaining(), 0);
}
#[test]
fn short_len_left_aligns_and_zero_pads_tail() {
let data = [0xAA, 0xBB, 0xCC];
let mut r = SliceReader::new(&data);
let out = r.read_padded::<4>(2);
assert_eq!(out, Ok([0xAA, 0xBB, 0x00, 0x00]));
assert_eq!(r.offset(), 2);
assert_eq!(r.as_slice(), &[0xCC]);
}
#[test]
fn zero_len_yields_all_zeros_without_advancing() {
let data = [0x11, 0x22];
let mut r = SliceReader::new(&data);
let out = r.read_padded::<4>(0);
assert_eq!(out, Ok([0, 0, 0, 0]));
assert_eq!(r.offset(), 0);
assert_eq!(r.remaining(), 2);
}
#[test]
fn len_within_n_but_buffer_too_short_is_eof() {
let data = [0xAA]; let mut r = SliceReader::new(&data);
let err = r.read_padded::<4>(3);
assert_eq!(err, Err(ParseError::eof(0, 3, 1)));
assert_eq!(r.offset(), 0); }
#[test]
fn capacity_error_reports_current_offset() {
let data = [0x00, 0x00, 0xAA, 0xBB, 0xCC];
let mut r = SliceReader::new(&data);
let _ = r.read_u16_be().unwrap(); let err = r.read_padded::<2>(5);
assert_eq!(err, Err(ParseError::invalid_padding_length(2, 5, 2)));
assert_eq!(r.offset(), 2); }
#[test]
fn uint_be_right_aligns_shortened_values() {
for wire in [
&[0xAB, 0xCD][..],
&[0x00, 0xAB, 0xCD],
&[0x00, 0x00, 0xAB, 0xCD],
] {
let mut r = SliceReader::new(wire);
assert_eq!(r.read_unsigned64_be(wire.len()), Ok(0xABCD));
assert_eq!(r.remaining(), 0);
}
}
#[test]
fn uint_be_full_width_matches_fixed_read() {
let data = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF];
let mut a = SliceReader::new(&data);
let mut b = SliceReader::new(&data);
assert_eq!(a.read_unsigned64_be(8), b.read_u64_be());
}
#[test]
fn uint_be_zero_len_reads_nothing() {
let data = [0xAA];
let mut r = SliceReader::new(&data);
assert_eq!(r.read_unsigned64_be(0), Ok(0));
assert_eq!(r.offset(), 0);
}
#[test]
fn uint_be_rejects_len_beyond_width() {
let data = [0u8; 16];
let mut r = SliceReader::new(&data);
assert_eq!(
r.read_unsigned64_be(9),
Err(ParseError::invalid_padding_length(0, 9, 8))
);
assert_eq!(r.offset(), 0); }
#[test]
fn uint_be_short_buffer_is_eof() {
let data = [0xAA, 0xBB];
let mut r = SliceReader::new(&data);
assert_eq!(r.read_unsigned64_be(4), Err(ParseError::eof(0, 4, 2)));
}
#[test]
fn int_be_sign_extends_shortened_negative() {
let data = [0xFE];
let mut r = SliceReader::new(&data);
assert_eq!(r.read_signed64_be(1), Ok(-2));
}
#[test]
fn int_be_keeps_positive_values_unsigned() {
let data = [0x7F];
let mut r = SliceReader::new(&data);
assert_eq!(r.read_signed64_be(1), Ok(127));
}
#[test]
fn int_be_full_width_matches_fixed_read() {
let data = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE];
let mut a = SliceReader::new(&data);
let mut b = SliceReader::new(&data);
assert_eq!(a.read_signed64_be(8), b.read_i64_be());
assert_eq!(a.read_signed64_be(8), Err(ParseError::eof(8, 8, 0)));
}
#[test]
fn int_be_zero_len_is_not_negative() {
let data = [0xFF];
let mut r = SliceReader::new(&data);
assert_eq!(r.read_signed64_be(0), Ok(0));
assert_eq!(r.offset(), 0);
}
#[test]
fn uint32_be_right_aligns_shortened_values() {
for wire in [
&[0xAB, 0xCD][..],
&[0x00, 0xAB, 0xCD],
&[0x00, 0x00, 0xAB, 0xCD],
] {
let mut r = SliceReader::new(wire);
assert_eq!(r.read_unsigned32_be(wire.len()), Ok(0xABCD));
assert_eq!(r.remaining(), 0);
}
}
#[test]
fn uint32_be_full_width_matches_fixed_read() {
let data = [0x01, 0x23, 0x45, 0x67];
let mut a = SliceReader::new(&data);
let mut b = SliceReader::new(&data);
assert_eq!(a.read_unsigned32_be(4), b.read_u32_be());
}
#[test]
fn uint32_be_zero_len_reads_nothing() {
let data = [0xAA];
let mut r = SliceReader::new(&data);
assert_eq!(r.read_unsigned32_be(0), Ok(0));
assert_eq!(r.offset(), 0);
}
#[test]
fn uint32_be_rejects_len_beyond_width() {
let data = [0u8; 16];
let mut r = SliceReader::new(&data);
assert_eq!(
r.read_unsigned32_be(5),
Err(ParseError::invalid_padding_length(0, 5, 4))
);
assert_eq!(r.offset(), 0); }
#[test]
fn uint32_be_short_buffer_is_eof() {
let data = [0xAA];
let mut r = SliceReader::new(&data);
assert_eq!(r.read_unsigned32_be(4), Err(ParseError::eof(0, 4, 1)));
}
#[test]
fn int32_be_sign_extends_shortened_negative() {
let data = [0xFE];
let mut r = SliceReader::new(&data);
assert_eq!(r.read_signed32_be(1), Ok(-2));
}
#[test]
fn int32_be_keeps_positive_values_unsigned() {
let data = [0x7F];
let mut r = SliceReader::new(&data);
assert_eq!(r.read_signed32_be(1), Ok(127));
}
#[test]
fn int32_be_full_width_matches_fixed_read() {
let data = [0xFF, 0xFF, 0xFF, 0xFE];
let mut a = SliceReader::new(&data);
let mut b = SliceReader::new(&data);
assert_eq!(a.read_signed32_be(4), b.read_i32_be());
assert_eq!(a.read_signed32_be(4), Err(ParseError::eof(4, 4, 0)));
}
#[test]
fn int32_be_zero_len_is_not_negative() {
let data = [0xFF];
let mut r = SliceReader::new(&data);
assert_eq!(r.read_signed32_be(0), Ok(0));
assert_eq!(r.offset(), 0);
}
}