first commit

This commit is contained in:
Lucas Schumacher 2024-04-02 21:29:02 -04:00
commit 51666b629c
5 changed files with 577 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
Cargo.lock

11
Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
workspace = { members = ["poppable-derive"] }
[package]
name = "poppable"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.81"
poppable-derive = { path = "poppable-derive"}

View File

@ -0,0 +1,13 @@
[package]
name = "poppable-derive"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
quote = "1.0.35"
syn = "2.0.57"
[lib]
proc_macro = true

View File

@ -0,0 +1,8 @@
use proc_macro::TokenStream;
use syn::DeriveInput;
#[proc_macro_derive(PushPop)]
pub fn pushpop_derive_macro(item: TokenStream) -> TokenStream {
let _ast: DeriveInput = syn::parse(item).unwrap();
todo!()
}

543
src/lib.rs Normal file
View File

@ -0,0 +1,543 @@
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
// TODO: Create macro(s) for impls. Most of these impls are the same except for the the type name.
// TODO: Impl for u128 and i128
// TODO: Impl for usize and isize
//
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,
{
u8::pop_ne_from(source)
}
fn push_be_into<T: Pushable>(&self, dest: &mut T) -> Result<()> {
dest.push_u8(self)
}
}
// Impls for u16
impl PopFromNE for u16 {
fn pop_ne_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<u16>();
if source.len() < SIZE {
anyhow::bail!("Buffer too small to pop u16");
}
let value = u16::from_be_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())
}
}
impl PopFromLE for u16 {
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 u16 {
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())
}
}
//Impls for u32
impl PopFromNE for u32 {
fn pop_ne_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<u32>();
if source.len() < SIZE {
anyhow::bail!("Buffer too small to pop u32");
}
let value = u32::from_be_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())
}
}
impl PopFromLE for u32 {
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 u32 {
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())
}
}
//Impls for u64
impl PopFromNE for u64 {
fn pop_ne_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<u64>();
if source.len() < SIZE {
anyhow::bail!("Buffer too small to pop u64");
}
let value = u64::from_be_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())
}
}
impl PopFromLE for u64 {
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 u64 {
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())
}
}
impl PopFromNE for i8 {
fn pop_ne_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<i8>();
if source.len() < SIZE {
anyhow::bail!("Buffer too small to pop i8");
}
let value = source[0] as i8;
*source = &source[SIZE..];
Ok(value)
}
fn push_ne_into<T: Pushable>(&self, dest: &mut T) -> Result<()> {
dest.push_u8(&(*self as u8))
}
}
impl PopFromLE for i8 {
fn pop_le_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
i8::pop_ne_from(source)
}
fn push_le_into<T: Pushable>(&self, dest: &mut T) -> Result<()> {
self.push_ne_into(dest)
}
}
impl PopFromBE for i8 {
fn pop_be_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
i8::pop_ne_from(source)
}
fn push_be_into<T: Pushable>(&self, dest: &mut T) -> Result<()> {
self.push_ne_into(dest)
}
}
impl PopFromNE for i16 {
fn pop_ne_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<i16>();
if source.len() < SIZE {
anyhow::bail!("Buffer too small to pop i16");
}
let value = i16::from_be_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())
}
}
impl PopFromNE for i32 {
fn pop_ne_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<i32>();
if source.len() < SIZE {
anyhow::bail!("Buffer too small to pop i32");
}
let value = i32::from_be_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())
}
}
impl PopFromNE for i64 {
fn pop_ne_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<i64>();
if source.len() < SIZE {
anyhow::bail!("Buffer too small to pop i64");
}
let value = i64::from_be_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())
}
}
impl PopFromLE for i16 {
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 PopFromLE for i32 {
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 PopFromLE for i64 {
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 i16 {
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())
}
}
impl PopFromBE for i32 {
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())
}
}
impl PopFromBE for i64 {
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())
}
}
//
// Floating Point
//
impl PopFromNE for f32 {
fn pop_ne_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<f32>();
if source.len() < SIZE {
anyhow::bail!("Buffer to small to pop f32");
}
let value = f32::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())
}
}
impl PopFromLE for f32 {
fn pop_le_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<f32>();
if source.len() < SIZE {
anyhow::bail!("Buffer to small to pop f32");
}
let value = f32::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 f32 {
fn pop_be_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<f32>();
if source.len() < SIZE {
anyhow::bail!("Buffer to small to pop f32");
}
let value = f32::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())
}
}
impl PopFromNE for f64 {
fn pop_ne_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<f64>();
if source.len() < SIZE {
anyhow::bail!("Buffer to small to pop f64");
}
let value = f64::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())
}
}
impl PopFromLE for f64 {
fn pop_le_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<f64>();
if source.len() < SIZE {
anyhow::bail!("Buffer to small to pop f64");
}
let value = f64::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 f64 {
fn pop_be_from(source: &mut &[u8]) -> Result<Self>
where
Self: Sized,
{
const SIZE: usize = std::mem::size_of::<f64>();
if source.len() < SIZE {
anyhow::bail!("Buffer to small to pop f64");
}
let value = f64::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())
}
}