use crate::error::{ParseError, ParseResult};
#[derive(Debug, Clone)]
pub struct Cursor<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> Cursor<'a> {
#[inline]
pub const fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
#[inline]
pub const fn position(&self) -> usize {
self.pos
}
#[inline]
pub const fn remaining(&self) -> usize {
self.data.len().saturating_sub(self.pos)
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.pos >= self.data.len()
}
#[inline]
pub fn as_slice(&self) -> &'a [u8] {
debug_assert!(self.pos <= self.data.len());
&self.data[self.pos..]
}
#[inline]
pub fn read_bytes(&mut self, n: usize) -> ParseResult<&'a [u8]> {
let end = self.pos.checked_add(n).ok_or(ParseError::UnexpectedEof {
needed: n,
available: self.remaining(),
})?;
if end > self.data.len() {
return Err(ParseError::UnexpectedEof {
needed: n,
available: self.remaining(),
});
}
let slice = unsafe { self.data.get_unchecked(self.pos..end) };
self.pos = end;
Ok(slice)
}
#[inline]
pub fn read_u8(&mut self) -> ParseResult<u8> {
if self.pos >= self.data.len() {
return Err(ParseError::UnexpectedEof {
needed: 1,
available: 0,
});
}
let b = unsafe { *self.data.get_unchecked(self.pos) };
self.pos += 1;
Ok(b)
}
#[inline]
pub fn read_u16_le(&mut self) -> ParseResult<u16> {
let bytes = self.read_bytes(2)?;
Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
}
#[inline]
pub fn read_u32_le(&mut self) -> ParseResult<u32> {
let bytes = self.read_bytes(4)?;
Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
#[inline]
pub fn read_i32_le(&mut self) -> ParseResult<i32> {
let bytes = self.read_bytes(4)?;
Ok(i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
#[inline]
pub fn read_u64_le(&mut self) -> ParseResult<u64> {
let bytes = self.read_bytes(8)?;
Ok(u64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]))
}
#[inline]
pub fn read_i64_le(&mut self) -> ParseResult<i64> {
let bytes = self.read_bytes(8)?;
Ok(i64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]))
}
#[inline]
pub fn read_varint(&mut self) -> ParseResult<u64> {
match self.read_u8()? {
v @ 0x00..=0xfc => Ok(v as u64),
0xfd => self.read_u16_le().map(|v| v as u64),
0xfe => self.read_u32_le().map(|v| v as u64),
0xff => self.read_u64_le(),
}
}
#[inline]
pub fn read_var_bytes(&mut self, max: usize) -> ParseResult<&'a [u8]> {
let len_u64 = self.read_varint()?;
let len: usize = len_u64
.try_into()
.map_err(|_| ParseError::IntegerTooLarge { value: len_u64 })?;
if len > max {
return Err(ParseError::OversizedData { size: len, max });
}
self.read_bytes(len)
}
#[inline]
pub fn read_array<const N: usize>(&mut self) -> ParseResult<&'a [u8; N]> {
let bytes = self.read_bytes(N)?;
Ok(unsafe { &*(bytes.as_ptr() as *const [u8; N]) })
}
#[inline]
pub fn skip(&mut self, n: usize) -> ParseResult<()> {
let _ = self.read_bytes(n)?;
Ok(())
}
#[inline]
pub fn split(&mut self, n: usize) -> ParseResult<Cursor<'a>> {
let slice = self.read_bytes(n)?;
Ok(Cursor::new(slice))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_bytes_zero_copy() {
let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
let mut c = Cursor::new(&data);
let slice = c.read_bytes(4).unwrap();
assert_eq!(slice.as_ptr(), data.as_ptr());
assert_eq!(slice, &[1, 2, 3, 4]);
assert_eq!(c.remaining(), 4);
}
#[test]
fn varint_single_byte() {
let data = [0xfc];
let mut c = Cursor::new(&data);
assert_eq!(c.read_varint().unwrap(), 0xfc);
}
#[test]
fn varint_two_bytes() {
let data = [0xfd, 0x01, 0x00];
let mut c = Cursor::new(&data);
assert_eq!(c.read_varint().unwrap(), 1);
}
#[test]
fn varint_nine_bytes() {
let mut data = [0u8; 9];
data[0] = 0xff;
data[1..].copy_from_slice(&u64::MAX.to_le_bytes());
let mut c = Cursor::new(&data);
assert_eq!(c.read_varint().unwrap(), u64::MAX);
}
#[test]
fn eof_error() {
let data = [1u8, 2];
let mut c = Cursor::new(&data);
assert!(matches!(
c.read_bytes(3),
Err(ParseError::UnexpectedEof {
needed: 3,
available: 2
})
));
}
}