use std::mem::size_of;
use Error::BufferTooSmall;
use crate::error::Error;
pub struct ParseCursor<'a> {
data: &'a mut [u8],
position: usize,
}
impl<'a> ParseCursor<'a> {
#[inline]
pub fn new(data: &'a mut [u8]) -> Self {
Self { data, position: 0 }
}
#[inline]
pub fn remaining(&self) -> usize {
self.data.len().saturating_sub(self.position)
}
#[inline]
pub fn has_remaining(&self) -> bool {
self.position < self.data.len()
}
#[inline]
pub fn capacity(&self) -> usize {
self.data.len()
}
#[inline]
pub fn get_u32_le(&mut self) -> u32 {
const SIZE: usize = size_of::<u32>();
let bytes = &self.data[self.position..self.position + SIZE];
let value = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
self.position += SIZE;
value
}
#[inline]
pub fn try_get_u32_le(&mut self) -> Result<u32, Error> {
const SIZE: usize = size_of::<u32>();
if self.remaining() < SIZE {
return Err(BufferTooSmall(SIZE, self.remaining()));
}
Ok(self.get_u32_le())
}
#[inline]
pub fn get_u32(&mut self) -> u32 {
const SIZE: usize = size_of::<u32>();
let bytes = &self.data[self.position..self.position + SIZE];
let value = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
self.position += SIZE;
value
}
#[inline]
pub fn try_get_u32(&mut self) -> Result<u32, Error> {
const SIZE: usize = size_of::<u32>();
if self.remaining() < SIZE {
return Err(BufferTooSmall(SIZE, self.remaining()));
}
Ok(self.get_u32())
}
#[inline]
pub fn get_u64_le(&mut self) -> u64 {
const SIZE: usize = size_of::<u64>();
let bytes = &self.data[self.position..self.position + SIZE];
let value = u64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]);
self.position += SIZE;
value
}
#[inline]
pub fn try_get_u64_le(&mut self) -> Result<u64, Error> {
const SIZE: usize = size_of::<u64>();
if self.remaining() < SIZE {
return Err(BufferTooSmall(SIZE, self.remaining()));
}
Ok(self.get_u64_le())
}
#[inline]
pub fn get_u64(&mut self) -> u64 {
const SIZE: usize = size_of::<u64>();
let bytes = &self.data[self.position..self.position + SIZE];
let value = u64::from_be_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]);
self.position += SIZE;
value
}
#[inline]
pub fn try_get_u64(&mut self) -> Result<u64, Error> {
const SIZE: usize = size_of::<u64>();
if self.remaining() < SIZE {
return Err(BufferTooSmall(SIZE, self.remaining()));
}
Ok(self.get_u64())
}
#[inline]
pub fn copy_to_slice(&mut self, dst: &mut [u8]) {
dst.copy_from_slice(&self.data[self.position..self.position + dst.len()]);
self.position += dst.len();
}
#[inline]
pub fn try_copy_to_slice(&mut self, dst: &mut [u8]) -> Result<(), Error> {
if self.remaining() < dst.len() {
return Err(BufferTooSmall(dst.len(), self.remaining()));
}
self.copy_to_slice(dst);
Ok(())
}
#[inline]
pub fn get_fixed<const N: usize>(&mut self) -> [u8; N] {
let mut result = [0u8; N];
result.copy_from_slice(&self.data[self.position..self.position + N]);
self.position += N;
result
}
#[inline]
pub fn try_get_fixed<const N: usize>(&mut self) -> Result<[u8; N], Error> {
if self.remaining() < N {
return Err(BufferTooSmall(N, self.remaining()));
}
Ok(self.get_fixed())
}
#[inline]
pub fn put_fixed<const N: usize>(&mut self, data: &[u8; N]) {
self.data[self.position..self.position + N].copy_from_slice(data);
self.position += N;
}
#[inline]
pub fn try_put_fixed<const N: usize>(&mut self, data: &[u8; N]) -> Result<(), Error> {
if self.remaining() < N {
return Err(BufferTooSmall(N, self.remaining()));
}
self.put_fixed(data);
Ok(())
}
#[inline]
pub fn put_slice(&mut self, src: &[u8]) {
self.data[self.position..self.position + src.len()].copy_from_slice(src);
self.position += src.len();
}
#[inline]
pub fn try_put_slice(&mut self, src: &[u8]) -> Result<(), Error> {
if self.remaining() < src.len() {
return Err(BufferTooSmall(src.len(), self.remaining()));
}
self.put_slice(src);
Ok(())
}
#[inline]
pub fn peek(&self) -> &[u8] {
&self.data[self.position..]
}
#[inline]
pub fn put_u32_le(&mut self, value: u32) {
let bytes = value.to_le_bytes();
self.data[self.position..self.position + 4].copy_from_slice(&bytes);
self.position += 4;
}
#[inline]
pub fn try_put_u32_le(&mut self, value: u32) -> Result<(), Error> {
const SIZE: usize = size_of::<u32>();
if self.remaining() < SIZE {
return Err(BufferTooSmall(SIZE, self.remaining()));
}
self.put_u32_le(value);
Ok(())
}
#[inline]
pub fn put_u64_le(&mut self, value: u64) {
let bytes = value.to_le_bytes();
self.data[self.position..self.position + 8].copy_from_slice(&bytes);
self.position += 8;
}
#[inline]
pub fn put_u64(&mut self, value: u64) {
let bytes = value.to_be_bytes();
self.data[self.position..self.position + 8].copy_from_slice(&bytes);
self.position += 8;
}
#[inline]
pub fn try_put_u64_le(&mut self, value: u64) -> Result<(), Error> {
const SIZE: usize = size_of::<u64>();
if self.remaining() < SIZE {
return Err(BufferTooSmall(SIZE, self.remaining()));
}
self.put_u64_le(value);
Ok(())
}
#[inline]
pub fn try_put_u64(&mut self, value: u64) -> Result<(), Error> {
const SIZE: usize = size_of::<u64>();
if self.remaining() < SIZE {
return Err(BufferTooSmall(SIZE, self.remaining()));
}
self.put_u64(value);
Ok(())
}
#[inline]
pub fn advance(&mut self, n: usize) -> Result<(), Error> {
if self.remaining() < n {
return Err(BufferTooSmall(n, self.remaining()));
}
self.position += n;
Ok(())
}
#[inline]
pub fn reset(&mut self) {
self.position = 0;
}
#[inline]
pub fn position(&self) -> usize {
self.position
}
#[inline]
pub fn set_position(&mut self, pos: usize) {
self.position = pos;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_cursor() {
let mut data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
let mut cursor = ParseCursor::new(&mut data);
assert_eq!(cursor.remaining(), 8);
assert_eq!(cursor.try_get_u32_le().unwrap(), 0x04030201);
assert_eq!(cursor.remaining(), 4);
assert_eq!(cursor.try_get_u32().unwrap(), 0x05060708);
assert_eq!(cursor.remaining(), 0);
}
#[test]
fn test_parse_cursor_bounds() {
let mut data = [0x01, 0x02];
let mut cursor = ParseCursor::new(&mut data);
assert!(cursor.try_get_u32_le().is_err());
}
#[test]
fn test_put_u32_le() {
let mut data = [0u8; 8];
{
let mut cursor = ParseCursor::new(&mut data);
cursor.put_u32_le(0x12345678);
assert_eq!(cursor.position(), 4);
cursor.put_u32_le(0xABCDEF00);
assert_eq!(cursor.position(), 8);
}
assert_eq!(&data[0..4], &[0x78, 0x56, 0x34, 0x12]);
assert_eq!(&data[4..8], &[0x00, 0xEF, 0xCD, 0xAB]);
}
#[test]
fn test_try_put_u32_le_bounds() {
let mut data = [0u8; 2];
let mut cursor = ParseCursor::new(&mut data);
assert!(cursor.try_put_u32_le(0x12345678).is_err());
}
#[test]
fn test_put_u64_le() {
let mut data = [0u8; 16];
{
let mut cursor = ParseCursor::new(&mut data);
cursor.put_u64_le(0x123456789ABCDEF0);
assert_eq!(cursor.position(), 8);
}
assert_eq!(
&data[0..8],
&[0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]
);
}
#[test]
fn test_try_put_u64_le_bounds() {
let mut data = [0u8; 4];
let mut cursor = ParseCursor::new(&mut data);
assert!(cursor.try_put_u64_le(0x123456789ABCDEF0).is_err());
}
#[test]
fn test_round_trip() {
let mut data = [0u8; 16];
let mut cursor = ParseCursor::new(&mut data);
cursor.put_u32_le(0x12345678);
cursor.put_u64_le(0x9ABCDEF012345678);
cursor.reset();
assert_eq!(cursor.get_u32_le(), 0x12345678);
assert_eq!(cursor.get_u64_le(), 0x9ABCDEF012345678);
}
}