#![doc = include_str!("../README.md")]
#![no_std]
#![allow(clippy::must_use_candidate)]
use core::num::NonZeroUsize;
use core::{fmt, slice};
pub mod error;
pub use self::error::Error;
pub struct Reader<'a> {
buffer: &'a [u8],
cursor: usize,
}
#[cfg(not(feature = "feat-debug-buffer"))]
impl fmt::Debug for Reader<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Reader")
.field("buffer", &"[..]")
.field("cursor", &self.cursor)
.finish()
}
}
#[cfg(feature = "feat-debug-buffer")]
impl fmt::Debug for Reader<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Reader")
.field("buffer", &const_hex::encode(self.buffer))
.field("cursor", &self.cursor)
.finish()
}
}
macro_rules! advance_cursor_and {
($this:expr, $length:expr, $($tt:tt)+) => {{
let Some(cursor) = $this.cursor.checked_add($length) else {
return Err(Error::Overflow);
};
match cursor.checked_sub($this.buffer.len()) {
Some(0) | None => {}
Some(insufficient) => {
return Err(Error::InsufficientData {
missing: unsafe { NonZeroUsize::new_unchecked(insufficient) },
});
}
}
match $($tt)+ {
ret @ Ok(_) => {
$this.cursor = cursor;
ret
}
ret @ Err(_) => ret,
}
}};
}
impl<'a> Reader<'a> {
#[inline]
pub const fn init(bytes: &'a [u8]) -> Self {
Reader {
buffer: bytes,
cursor: 0,
}
}
#[inline]
pub const fn used(&self) -> usize {
self.cursor
}
#[inline]
#[track_caller]
pub const fn left(&self) -> usize {
let Some(left) = self
.buffer
.len()
.checked_sub(self.cursor)
else {
unreachable!()
};
left
}
#[inline]
#[track_caller]
pub const fn unread(&self) -> &'a [u8] {
unsafe {
slice::from_raw_parts(self.buffer.as_ptr().add(self.cursor), self.left())
}
}
#[inline]
pub const fn read(&mut self, length: usize) -> Result<&'a [u8], Error> {
advance_cursor_and!(
self,
length,
Ok(unsafe { slice::from_raw_parts(self.buffer.as_ptr().add(self.cursor), length) })
)
}
#[inline]
pub const fn read_array<const N: usize>(&mut self) -> Result<&'a [u8; N], Error> {
advance_cursor_and!(
self,
N,
Ok(unsafe {
&*(slice::from_raw_parts(self.buffer.as_ptr().add(self.cursor), N)
.as_ptr()
.cast::<[u8; N]>())
})
)
}
#[inline]
pub const fn read_all(&mut self) -> &'a [u8] {
let (_, unread) = self.buffer.split_at(self.cursor);
self.cursor = self.buffer.len();
unread
}
#[inline]
pub fn read_u8(&mut self) -> Result<u8, Error> {
match self.read_array() {
Ok(&[b0]) => Ok(b0),
Err(e) => Err(e),
}
}
#[inline]
pub fn read_u16(&mut self) -> Result<u16, Error> {
match self.read_array() {
Ok(&b) => Ok(u16::from_be_bytes(b)),
Err(e) => Err(e),
}
}
#[inline]
pub fn read_u24(&mut self) -> Result<u32, Error> {
match self.read_array() {
Ok(&[b0, b1, b2]) => Ok(u32::from_be_bytes([0, b0, b1, b2])),
Err(e) => Err(e),
}
}
#[inline]
pub fn read_u32(&mut self) -> Result<u32, Error> {
match self.read_array() {
Ok(&b) => Ok(u32::from_be_bytes(b)),
Err(e) => Err(e),
}
}
#[inline]
pub fn read_u64(&mut self) -> Result<u64, Error> {
match self.read_array() {
Ok(&b) => Ok(u64::from_be_bytes(b)),
Err(e) => Err(e),
}
}
#[inline]
pub fn read_u128(&mut self) -> Result<u128, Error> {
match self.read_array() {
Ok(&b) => Ok(u128::from_be_bytes(b)),
Err(e) => Err(e),
}
}
#[inline]
pub const fn sub(&mut self, length: usize) -> Result<Self, Error> {
match self.read(length) {
Ok(bytes) => Ok(Reader::init(bytes)),
Err(e) => Err(e),
}
}
#[inline]
pub const fn advance(&mut self, length: usize) -> Result<(), Error> {
advance_cursor_and!(self, length, Ok(()))
}
}