#[derive(Debug, PartialEq)]
pub enum Error {
ReachedEnd,
InvalidSliceBounds,
}
#[derive(Debug)]
pub struct SliceInputBuffer<'a> {
data: &'a [u8],
pos: usize,
}
pub trait InputBuffer {
fn is_past_end(&self) -> bool;
fn consume_byte(&mut self) -> Result<u8, Error>;
}
impl InputBuffer for SliceInputBuffer<'_> {
fn is_past_end(&self) -> bool {
self.pos > self.data.len()
}
fn consume_byte(&mut self) -> Result<u8, Error> {
match self.data.get(self.pos) {
Some(&byte) => {
self.pos = self.pos.checked_add(1).ok_or(Error::InvalidSliceBounds)?;
Ok(byte)
}
None => {
self.pos = self.pos.checked_add(1).ok_or(Error::InvalidSliceBounds)?;
Err(Error::ReachedEnd)
}
}
}
}
impl<'a> SliceInputBuffer<'a> {
pub fn current_pos(&self) -> usize {
self.pos
}
pub fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
pub fn slice(&self, start: usize, end: usize) -> Result<&'a [u8], Error> {
self.data.get(start..end).ok_or(Error::InvalidSliceBounds)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_buffer_boundary_behavior() {
let data = b"abc"; let mut buffer = SliceInputBuffer::new(data);
assert_eq!(buffer.current_pos(), 0);
assert!(!buffer.is_past_end(), "pos=0 should not be past end");
assert_eq!(buffer.consume_byte(), Ok(b'a'));
assert_eq!(buffer.current_pos(), 1);
assert!(!buffer.is_past_end(), "pos=1 should not be past end");
assert_eq!(buffer.consume_byte(), Ok(b'b'));
assert_eq!(buffer.current_pos(), 2);
assert!(!buffer.is_past_end(), "pos=2 should not be past end");
assert_eq!(buffer.consume_byte(), Ok(b'c'));
assert_eq!(buffer.current_pos(), 3);
assert_eq!(
buffer.current_pos(),
data.len(),
"pos should equal data.len()"
);
assert!(
!buffer.is_past_end(),
"pos == data.len() should NOT be past end (allows tokenizer.finish())"
);
assert!(
buffer.consume_byte().is_err(),
"consume_byte() should fail when pos == data.len() (no bytes)"
);
assert_eq!(buffer.current_pos(), 4);
assert!(buffer.is_past_end(), "pos > data.len() should be past end");
assert!(
buffer.consume_byte().is_err(),
"consume_byte() should fail when pos > data.len()"
);
}
}