#[derive(Debug, Clone)]
pub(crate) struct ByteReader<'a> {
buf: &'a [u8],
pos: usize,
}
impl<'a> ByteReader<'a> {
pub(crate) fn new(buf: &'a [u8]) -> Self {
Self { buf, pos: 0 }
}
pub(crate) fn position(&self) -> usize {
self.pos
}
pub(crate) fn u8(&mut self) -> Option<u8> {
let b = *self.buf.get(self.pos)?;
self.pos += 1;
Some(b)
}
pub(crate) fn u16_be(&mut self) -> Option<u16> {
let s = self.take(2)?;
Some(u16::from_be_bytes([s[0], s[1]]))
}
pub(crate) fn u32_be(&mut self) -> Option<u32> {
let s = self.take(4)?;
Some(u32::from_be_bytes([s[0], s[1], s[2], s[3]]))
}
pub(crate) fn take(&mut self, n: usize) -> Option<&'a [u8]> {
let end = self.pos.checked_add(n)?;
let s = self.buf.get(self.pos..end)?;
self.pos = end;
Some(s)
}
pub(crate) fn skip(&mut self, n: usize) -> Option<()> {
let end = self.pos.checked_add(n)?;
if end > self.buf.len() {
return None;
}
self.pos = end;
Some(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reads_advance_and_bound_check() {
let mut r = ByteReader::new(&[0x12, 0x34, 0x56, 0x78, 0x9A]);
assert_eq!(r.u8(), Some(0x12));
assert_eq!(r.u16_be(), Some(0x3456));
assert_eq!(r.position(), 3);
assert_eq!(r.u32_be(), None); assert_eq!(r.take(2), Some(&[0x78, 0x9A][..]));
assert_eq!(r.u8(), None);
}
#[test]
fn skip_bounds_checks() {
let mut r = ByteReader::new(&[0x00, 0x01, 0x02, 0xFF]);
assert_eq!(r.skip(3), Some(()));
assert_eq!(r.position(), 3);
assert_eq!(r.skip(2), None); assert_eq!(r.position(), 3);
assert_eq!(r.u8(), Some(0xFF));
}
#[test]
fn empty_buffer_never_panics() {
let mut r = ByteReader::new(&[]);
assert_eq!(r.u8(), None);
assert_eq!(r.u16_be(), None);
assert_eq!(r.u32_be(), None);
assert_eq!(r.take(5), None);
assert_eq!(r.skip(3), None);
}
}