use std::cmp::min;
use std::marker::PhantomData;
use std::mem::size_of;
use std::ptr::read_unaligned;
use std::slice;
use std::str::{self, Utf8Error};
use super::{align, Bytes};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Cursor<'a> {
ptr: *const u8,
len: usize,
pha: PhantomData<&'a ()>,
}
impl<'a> Cursor<'a> {
fn new(ptr: *const u8, len: usize) -> Self {
Self { ptr, len, pha: PhantomData }
}
pub fn next<T: Bytes>(&mut self) -> Option<(T, Self)> {
unsafe {
let size = align(size_of::<T>());
self.len = self.len.checked_sub(size)?;
let item = read_unaligned(self.ptr as *const T);
self.ptr = self.ptr.add(size);
let max = self.len;
let size = min(item.tail(max), max);
let tail = Self::new(self.ptr, size);
let size = min(align(size), max);
self.len = self.len.checked_sub(size)?;
self.ptr = self.ptr.add(size);
Some((item, tail))
}
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn asciiz(self) -> Result<&'a str, Utf8Error> {
let len = self.len.saturating_sub(1);
str::from_utf8(self.slice(len))
}
pub fn bytes(self) -> &'a [u8] {
self.slice(self.len)
}
pub fn copy<T: Bytes>(self) -> T {
self.read()
}
fn slice(&self, len: usize) -> &'a [u8] {
assert!(len <= self.len);
unsafe {
slice::from_raw_parts(self.ptr, len)
}
}
fn read<T: Bytes>(&self) -> T {
assert!(size_of::<T>() <= self.len);
unsafe {
read_unaligned(self.ptr as *const T)
}
}
}
impl Default for Cursor<'_> {
fn default() -> Self {
static EMPTY: &[u8] = &[];
Self {
ptr: EMPTY.as_ptr(),
len: 0,
pha: PhantomData,
}
}
}
impl<'a> From<&'a [u8]> for Cursor<'a> {
fn from(slice: &'a [u8]) -> Self {
Self::new(slice.as_ptr(), slice.len())
}
}