poppable/src/lib.rs

239 lines
6.7 KiB
Rust

use std::u16;
use anyhow::{Ok, Result};
//pub type BytesRef = AsRef<u8>;
pub trait Pushable {
fn push_u8(&mut self, byte: &u8) -> Result<()>;
fn push_vec(&mut self, bytes: Vec<u8>) -> Result<()>;
fn push_slice(&mut self, bytes: &[u8]) -> Result<()>;
}
impl Pushable for Vec<u8> {
fn push_u8(&mut self, byte: &u8) -> Result<()> {
// TODO: push can panic. Return Err instead of panicing.
self.push(byte.to_owned());
Ok(())
}
fn push_vec(&mut self, mut bytes: Vec<u8>) -> Result<()> {
// TODO: append can panic. Return Err instead of panicing.
self.append(&mut bytes);
Ok(())
}
fn push_slice(&mut self, bytes: &[u8]) -> Result<()> {
// TODO: extend can also panic. Return Err instead of panicing.
// https://doc.rust-lang.org/src/alloc/vec/mod.rs.html#2970 rustdoc 1.77.1 (7cf61ebde 2024-03-27)
// http://web.archive.org/web/20240324062202/doc.rust-lang.org/src/alloc/vec/mod.rs.html#2970
self.extend(bytes);
Ok(())
}
}
pub trait Poppable {
fn try_pop_be<T: PopFromBE>(&mut self) -> Result<T>;
fn try_pop_le<T: PopFromLE>(&mut self) -> Result<T>;
fn try_pop_ne<T: PopFromNE>(&mut self) -> Result<T>;
}
impl Poppable for &[u8] {
fn try_pop_be<T: PopFromBE>(&mut self) -> Result<T> {
Ok(T::pop_be_from(self)?)
}
fn try_pop_le<T: PopFromLE>(&mut self) -> Result<T> {
Ok(T::pop_le_from(self)?)
}
fn try_pop_ne<T: PopFromNE>(&mut self) -> Result<T> {
Ok(T::pop_ne_from(self)?)
}
}
pub trait PopFromBE {
fn pop_be_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized;
fn push_be_into<T: Pushable>(&self, dest: &mut T) -> Result<()>;
}
pub trait PopFromLE {
fn pop_le_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized;
fn push_le_into<T: Pushable>(&self, dest: &mut T) -> Result<()>;
}
pub trait PopFromNE {
fn pop_ne_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized;
fn push_ne_into<T: Pushable>(&self, dest: &mut T) -> Result<()>;
}
//
// Impl PopFromNE, PopFromLE, PopFromBE for core primitive types
//
impl PopFromNE for u8 {
fn pop_ne_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<u8>();
if source.len() < SIZE {
anyhow::bail!("Buffer too small to pop u8");
}
let value = source[0];
*source = &source[SIZE..];
Ok(value)
}
fn push_ne_into<T: Pushable>(&self, dest: &mut T) -> Result<()> {
dest.push_u8(self)
}
}
impl PopFromLE for u8 {
fn pop_le_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
u8::pop_ne_from(source)
}
fn push_le_into<T: Pushable>(&self, dest: &mut T) -> Result<()> {
dest.push_u8(self)
}
}
impl PopFromBE for u8 {
fn pop_be_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
let x = u8::from_be(u8::pop_ne_from(source)?);
Ok(x)
}
fn push_be_into<T: Pushable>(&self, dest: &mut T) -> Result<()> {
dest.push_u8(self)
}
}
macro_rules! core_impl_pop_ne {
($int_type: ident) => {
impl PopFromNE for $int_type {
fn pop_ne_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<$int_type>();
if source.len() < SIZE {
anyhow::bail!("Buffer too small to pop {}", stringify!($int_type));
}
let value = $int_type::from_ne_bytes(source[..SIZE].try_into()?);
*source = &source[SIZE..];
Ok(value)
}
fn push_ne_into<T: Pushable>(&self, dest: &mut T) -> Result<()> {
dest.push_slice(&self.to_ne_bytes())
}
}
};
}
macro_rules! int_impl_pop_non_ne {
($int_type: ident) => {
impl PopFromLE for $int_type {
fn pop_le_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
Ok(Self::from_le(Self::pop_ne_from(source)?))
}
fn push_le_into<T: Pushable>(&self, dest: &mut T) -> Result<()> {
dest.push_slice(&self.to_le_bytes())
}
}
impl PopFromBE for $int_type {
fn pop_be_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
Ok(Self::from_be(Self::pop_ne_from(source)?))
}
fn push_be_into<T: Pushable>(&self, dest: &mut T) -> Result<()> {
dest.push_slice(&self.to_be_bytes())
}
}
};
}
macro_rules! flt_impl_pop_non_ne {
($flt_type: ident) => {
impl PopFromLE for $flt_type {
fn pop_le_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<$flt_type>();
if source.len() < SIZE {
anyhow::bail!("Buffer to small to pop $flt_type");
}
let value = $flt_type::from_le_bytes(source[..SIZE].try_into()?);
*source = &source[SIZE..];
Ok(value)
}
fn push_le_into<T: Pushable>(&self, dest: &mut T) -> Result<()> {
dest.push_slice(&self.to_le_bytes())
}
}
impl PopFromBE for $flt_type {
fn pop_be_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<$flt_type>();
if source.len() < SIZE {
anyhow::bail!("Buffer to small to pop $flt_type");
}
let value = $flt_type::from_be_bytes(source[..SIZE].try_into()?);
*source = &source[SIZE..];
Ok(value)
}
fn push_be_into<T: Pushable>(&self, dest: &mut T) -> Result<()> {
dest.push_slice(&self.to_be_bytes())
}
}
};
}
core_impl_pop_ne!(u16);
core_impl_pop_ne!(u32);
core_impl_pop_ne!(u64);
core_impl_pop_ne!(u128);
core_impl_pop_ne!(usize);
core_impl_pop_ne!(i16);
core_impl_pop_ne!(i32);
core_impl_pop_ne!(i64);
core_impl_pop_ne!(i128);
core_impl_pop_ne!(isize);
int_impl_pop_non_ne!(u16);
int_impl_pop_non_ne!(u32);
int_impl_pop_non_ne!(u64);
int_impl_pop_non_ne!(u128);
int_impl_pop_non_ne!(usize);
int_impl_pop_non_ne!(i16);
int_impl_pop_non_ne!(i32);
int_impl_pop_non_ne!(i64);
int_impl_pop_non_ne!(i128);
int_impl_pop_non_ne!(isize);
core_impl_pop_ne!(f32);
flt_impl_pop_non_ne!(f32);
core_impl_pop_ne!(f64);
flt_impl_pop_non_ne!(f64);