This commit is contained in:
Numbers
2025-05-29 03:45:41 +02:00
commit ebf6bde22d
10 changed files with 654 additions and 0 deletions

173
src/impls/lib_alloc.rs Normal file
View File

@@ -0,0 +1,173 @@
use crate::{MalformedData, OverTheWire, Reader, Writer};
use alloc::{
// heap ptr's
boxed::Box,
sync::Arc,
rc::Rc,
// collections
collections::BTreeMap,
string::String,
vec::Vec,
};
impl<OTW: OverTheWire> OverTheWire for Box<OTW> {
#[inline]
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()> {
OTW::serialize(&*self, writer)
}
#[inline]
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self> {
Ok(Box::new(OTW::deserialize(reader)?))
}
#[inline]
fn size_hint() -> usize {
OTW::size_hint()
}
}
impl<OTW: OverTheWire> OverTheWire for Rc<OTW> {
#[inline]
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()> {
OTW::serialize(&*self, writer)
}
#[inline]
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self> {
Ok(Rc::new(OTW::deserialize(reader)?))
}
#[inline]
fn size_hint() -> usize {
OTW::size_hint()
}
}
impl<OTW: OverTheWire> OverTheWire for Arc<OTW> {
#[inline]
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()> {
OTW::serialize(&*self, writer)
}
#[inline]
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self> {
Ok(Arc::new(OTW::deserialize(reader)?))
}
#[inline]
fn size_hint() -> usize {
OTW::size_hint()
}
}
impl OverTheWire for String {
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()> {
(self.len() as u32).serialize(writer)?;
writer.write(self.as_bytes())?;
Ok(())
}
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self> {
let len = u32::deserialize(reader)?;
#[cfg(feature = "extra-validation")]
if len as usize > reader.remainder_hint() {
return Err(MalformedData)?;
}
let mut vec = Vec::with_capacity(len as usize);
unsafe { vec.set_len(len as usize) };
reader.read(vec.as_mut_slice())?;
let s = String::from_utf8(vec)
.map_err(|_|MalformedData)?;
Ok(s)
}
fn size_hint() -> usize {
u32::size_hint()
}
}
impl <OTW: OverTheWire> OverTheWire for Vec<OTW> {
#[inline]
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()> {
(self.len() as u32).serialize(writer)?;
for v in self {
v.serialize(writer)?;
}
Ok(())
}
#[inline]
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self> {
let len = u32::deserialize(reader)?;
#[cfg(feature = "extra-validation")]
if OTW::size_hint().saturating_mul(len as usize) > reader.remainder_hint() {
return Err(MalformedData)?;
}
let mut buffer = Vec::with_capacity(len as usize);
for _ in 0..len {
buffer.push(OTW::deserialize(reader)?);
}
Ok(buffer)
}
fn size_hint() -> usize {
u32::size_hint()
}
}
impl <K: OverTheWire + Ord, V: OverTheWire> OverTheWire for BTreeMap<K,V> {
#[inline]
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()> {
(self.len() as u32).serialize(writer)?;
for (k,v) in self {
k.serialize(writer)?;
v.serialize(writer)?;
}
Ok(())
}
#[inline]
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self> {
let len = u32::deserialize(reader)?;
#[cfg(feature = "extra-validation")]
if K::size_hint()
.saturating_add(V::size_hint())
.saturating_mul(len as usize) > reader.remainder_hint() {
return Err(MalformedData)?;
}
let mut buffer = BTreeMap::<K,V>::new();
for _ in 0..len {
let k = K::deserialize(reader)?;
let v = V::deserialize(reader)?;
buffer.insert(k,v);
}
Ok(buffer)
}
fn size_hint() -> usize {
u32::size_hint()
}
}

42
src/impls/lib_std.rs Normal file
View File

@@ -0,0 +1,42 @@
use crate::{MalformedData, OverTheWire, Reader, Writer};
use std::collections::HashMap;
use std::hash::Hash;
impl <K: OverTheWire + Hash + Eq, V: OverTheWire> OverTheWire for HashMap<K,V> {
#[inline]
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()> {
(self.len() as u32).serialize(writer)?;
for (k,v) in self {
k.serialize(writer)?;
v.serialize(writer)?;
}
Ok(())
}
#[inline]
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self> {
let len = u32::deserialize(reader)?;
#[cfg(feature = "extra-validation")]
if K::size_hint()
.saturating_add(V::size_hint())
.saturating_mul(len as usize) > reader.remainder_hint() {
return Err(MalformedData)?;
}
let mut buffer = HashMap::<K,V>::new();
for _ in 0..len {
let k = K::deserialize(reader)?;
let v = V::deserialize(reader)?;
buffer.insert(k,v);
}
Ok(buffer)
}
#[inline]
fn size_hint() -> usize {
u32::size_hint()
}
}

27
src/impls/magic.rs Normal file
View File

@@ -0,0 +1,27 @@
use crate::{MalformedData, OverTheWire, Reader, Writer};
/// Zero-sized type that is written as a 4-byte magic number used for validation
pub struct Magic<const VALUE: u32>;
impl<const V: u32> OverTheWire for Magic<V> {
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()> {
writer.write(V.to_le_bytes().as_slice())
}
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self> {
let _read = u32::from_le_bytes(reader.bytes::<4>()?);
#[cfg(feature = "extra-validation")]
if _read != V { return Err(MalformedData)? }
Ok(Self)
}
fn size_hint() -> usize {
4
}
}

129
src/impls/primitives.rs Normal file
View File

@@ -0,0 +1,129 @@
use crate::{OverTheWire, Reader, Writer};
//╶───╴Numbers╶──────────────────────────────────────────────────────────────╴
macro_rules! impl_integer {
($($t:ty),+) => {
$(impl OverTheWire for $t {
#[inline]
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()> {
writer.write(self.to_le_bytes().as_slice())
}
#[inline]
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self> {
Ok(Self::from_le_bytes(reader.bytes()?))
}
fn size_hint() -> usize {
size_of::<Self>()
}
})+
};
}
impl_integer!(u8, u16, u32, u64, u128);
impl_integer!(i8, i16, i32, i64, i128);
impl_integer!(f32, f64);
//╶───╴Boolean╶──────────────────────────────────────────────────────────────╴
/// Implement for booleans
impl OverTheWire for bool {
#[inline]
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()> {
writer.write(&[*self as u8])
}
#[inline]
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self> {
Ok(reader.bytes::<1>()?[0] != 0)
}
fn size_hint() -> usize { 1 }
}
/// Implement for unit types...
impl OverTheWire for () {
fn serialize<T: Writer>(&self, _: &mut T) -> e::Result<()> { Ok(()) }
fn deserialize<T: Reader>(_: &mut T) -> e::Result<Self> { Ok(()) }
fn size_hint() -> usize { 0 }
}
//╶───╴Arrays╶───────────────────────────────────────────────────────────────╴
impl<const S: usize, OTW: OverTheWire> OverTheWire for [OTW; S] {
#[inline]
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()> {
for i in self {
i.serialize(writer)?
}
Ok(())
}
#[inline]
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self> {
let mut array = core::mem::MaybeUninit::<[OTW; S]>::uninit();
for i in 0..S {
match OTW::deserialize(reader) {
Ok(v) => unsafe {
// assign the value directly into the uninit array...
*array.as_mut_ptr().cast::<OTW>().add(i) = v;
}
Err(error) => unsafe {
// drop only the value's that were assigned
for i in 0..i {
core::ptr::drop_in_place(array.as_mut_ptr().cast::<OTW>().add(i))
}
// forward the error
return Err(error);
}
}
}
Ok(unsafe { array.assume_init() })
}
fn size_hint() -> usize {
OTW::size_hint().saturating_mul(S)
}
}
//╶───╴Tuples╶───────────────────────────────────────────────────────────────╴
macro_rules! impl_tuple {
( $($t:ident : $l:tt),+ ) => {
impl < $($t: OverTheWire),+ > OverTheWire for ($($t),+, ) {
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()> {
$(self.$l.serialize(writer)?;)+
Ok(())
}
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self> {
Ok((
$($t::deserialize(reader)?),+,
))
}
fn size_hint() -> usize {
0usize $( .saturating_add($t::size_hint()) )+
}
}
};
}
impl_tuple!( K0:0 );
impl_tuple!( K0:0, K1:1 );
impl_tuple!( K0:0, K1:1, K2:2 );
impl_tuple!( K0:0, K1:1, K2:2, K3:3 );
impl_tuple!( K0:0, K1:1, K2:2, K3:3, K4:4 );
impl_tuple!( K0:0, K1:1, K2:2, K3:3, K4:4, K5:5 );
impl_tuple!( K0:0, K1:1, K2:2, K3:3, K4:4, K5:5, K6:6 );
impl_tuple!( K0:0, K1:1, K2:2, K3:3, K4:4, K5:5, K6:6, K7:7 );
impl_tuple!( K0:0, K1:1, K2:2, K3:3, K4:4, K5:5, K6:6, K7:7, K8:8 );
impl_tuple!( K0:0, K1:1, K2:2, K3:3, K4:4, K5:5, K6:6, K7:7, K8:8, K9:9 );

76
src/lib.rs Normal file
View File

@@ -0,0 +1,76 @@
#![cfg_attr(not(feature = "std"), no_std)]
/*
TODO - Future Improvements
implement var-int support for length & integers
*/
#[cfg(feature = "alloc")]
extern crate alloc;
//╶───╴Modules╶──────────────────────────────────────────────────────────────╴
mod impls {
mod primitives;
mod magic;
#[cfg(feature = "alloc")]
mod lib_alloc;
#[cfg(feature = "std")]
mod lib_std;
}
mod streams;
//╶───╴Core Traits╶──────────────────────────────────────────────────────────╴
e::new_exception!(MalformedData);
pub trait Writer {
fn write(&mut self, bytes: &[u8]) -> e::Result<()>;
}
pub trait Reader {
fn read(&mut self, bytes: &mut [u8]) -> e::Result<()>;
#[inline(always)]
fn bytes<const S: usize>(&mut self) -> e::Result<[u8;S]> {
Ok(unsafe {
let mut buffer = core::mem::MaybeUninit::<[u8;S]>::uninit();
self.read(buffer.assume_init_mut().as_mut_slice())?;
buffer.assume_init()
})
}
/// used for validating large arrays of data...
fn remainder_hint(&self) -> usize;
}
pub use derive_macro::OverTheWire;
pub trait OverTheWire: Sized {
fn serialize<T: Writer>(&self, writer: &mut T) -> e::Result<()>;
fn deserialize<T: Reader>(reader: &mut T) -> e::Result<Self>;
/// the minimum size of this type, used for extra validation
fn size_hint() -> usize;
}
#[inline(always)]
pub fn min_wire_size<T: OverTheWire>() -> usize {
T::size_hint()
}

33
src/streams.rs Normal file
View File

@@ -0,0 +1,33 @@
use crate::{MalformedData, Reader, Writer};
impl Reader for &[u8] {
#[inline(always)]
fn read(&mut self, bytes: &mut [u8]) -> e::Result<()> {
if bytes.len() > self.len() { Err(MalformedData)? }
unsafe {
core::ptr::copy_nonoverlapping(
self.as_ptr(),
bytes.as_mut_ptr(),
bytes.len()
)
}
*self = &self[bytes.len()..];
Ok(())
}
#[inline(always)]
fn remainder_hint(&self) -> usize {
self.len()
}
}
#[cfg(feature = "alloc")]
impl Writer for alloc::vec::Vec<u8> {
fn write(&mut self, bytes: &[u8]) -> e::Result<()> {
self.extend_from_slice(bytes);
Ok(())
}
}