#![allow(incomplete_features)]
#![feature(
more_qualified_paths,
iter_next_chunk,
generic_const_exprs,
iter_array_chunks
)]
#![no_std]
#[cfg(feature = "non_fixed")]
extern crate alloc;
use core::fmt::Debug;
#[cfg(feature = "non_fixed")]
use alloc::vec::Vec;
#[derive(Debug)]
pub enum ParserError {
TooLittleData(usize),
HeaderIncomplete(usize),
InvalidMagic,
ValueNotUnderstood,
ArrayTooShort,
}
#[derive(Debug, Copy, Clone)]
pub enum Endian {
Little,
Big,
}
pub trait StaticallySized {
const SIZE: usize;
}
pub trait Read
where
Self: Sized,
{
fn from_bytes(data: &mut (impl ExactSizeIterator<Item = u8> + Clone)) -> Result<Self, ParserError>;
}
#[cfg(feature = "non_fixed")]
pub trait ReadCtx<Ctx>
where
Self: Sized,
{
fn from_bytes(
data: &mut (impl ExactSizeIterator<Item = u8> + Clone),
ctx: Ctx,
) -> Result<Self, ParserError>;
}
#[cfg(feature = "non_fixed")]
pub trait Write {
fn to_bytes(&self) -> Vec<u8>;
}
#[cfg(feature = "capped")]
pub trait WriteCapped<const CAP: usize> {
fn to_bytes(&self) -> heapless::Vec<u8, CAP>;
}
#[cfg(feature = "non_fixed")]
pub trait WriteCtx<Ctx> {
fn to_bytes(&self, ctx: Ctx) -> Vec<u8>;
}#[cfg(feature = "capped")]
pub trait WriteCappedCtx<const CAP: usize, Ctx> {
fn to_bytes(&self, ctx: Ctx) -> heapless::Vec<u8, CAP>;
}
pub trait ReadFixed<const N: usize>
where
Self: Sized,
{
fn from_bytes(data: &[u8; N]) -> Result<Self, ParserError>;
}
pub trait ReadFixedCtx<const N: usize, Ctx>
where
Self: Sized,
{
fn from_bytes(data: &[u8; N], ctx: Ctx) -> Result<Self, ParserError>;
}
pub trait WriteFixed<const N: usize>
where
Self: Sized,
{
fn to_bytes(&self) -> [u8; N];
}
pub trait WriteFixedCtx<const N: usize, Ctx>
where
Self: Sized,
{
fn to_bytes(&self, ctx: Ctx) -> [u8; N];
}
#[cfg(feature = "non_fixed")]
impl<T> Read for Vec<T>
where
T: Read,
{
fn from_bytes(data: &mut (impl ExactSizeIterator<Item = u8> + Clone)) -> Result<Self, ParserError> {
Ok((0..).map_while(|_| T::from_bytes(data).ok()).collect()) }
}
#[cfg(feature = "non_fixed")]
impl<T, Ctx> ReadCtx<Ctx> for Vec<T>
where
T: ReadCtx<Ctx>,
Ctx: Clone,
{
fn from_bytes(
data: &mut (impl ExactSizeIterator<Item = u8> + Clone),
ctx: Ctx,
) -> Result<Self, ParserError> {
Ok((0..)
.map_while(|_| T::from_bytes(data, ctx.clone()).ok())
.collect()) }
}
#[cfg(feature = "non_fixed")]
impl<T> Write for Vec<T>
where
T: Write,
{
fn to_bytes(&self) -> Vec<u8> {
self.iter().flat_map(T::to_bytes).collect()
}
}
#[cfg(feature = "non_fixed")]
impl<T, Ctx> WriteCtx<Ctx> for Vec<T>
where
T: WriteCtx<Ctx>,
Ctx: Clone,
{
fn to_bytes(&self, ctx: Ctx) -> Vec<u8> {
self.iter().flat_map(|x| x.to_bytes(ctx.clone())).collect()
}
}
impl<const N: usize, T> ReadFixed<{ T::SIZE * N }> for [T; N]
where
T: StaticallySized + ReadFixed<{ T::SIZE }> + Default + Copy,
[(); T::SIZE * N]:,
{
fn from_bytes(data: &[u8; T::SIZE * N]) -> Result<Self, ParserError> {
let mut output = [T::default(); N];
for (i, x) in data
.iter()
.copied()
.array_chunks::<{ T::SIZE }>()
.map(|x| T::from_bytes(&x))
.enumerate()
{
output[i] = x?;
}
Ok(output)
}
}
impl<const N: usize, T> WriteFixed<{ T::SIZE * N }> for [T; N]
where
T: StaticallySized + WriteFixed<{ T::SIZE }> + Default + Copy,
[(); T::SIZE * N]:,
{
fn to_bytes(&self) -> [u8; T::SIZE * N] {
self.iter().flat_map(T::to_bytes).next_chunk().unwrap()
}
}
#[cfg(feature = "non_fixed")]
impl<const N: usize, T: Read + Default + Copy> Read for [T; N] {
fn from_bytes(data: &mut (impl ExactSizeIterator<Item = u8> + Clone)) -> Result<Self, ParserError> {
(0..N)
.map_while(|_| T::from_bytes(data).ok())
.next_chunk()
.map_err(|_| ParserError::ArrayTooShort)
}
}
#[cfg(feature = "non_fixed")]
impl<const N: usize, T: Write> Write for [T; N] {
fn to_bytes(&self) -> Vec<u8> {
self.iter().flat_map(T::to_bytes).collect()
}
}
#[macro_export]
macro_rules! enum_to_int {
($a:ty, $b:ty, $($x:expr, $y:path), +) => {
impl From<$a> for $b {
fn from(value: $a) -> Self {
match value {
$($x => $y,)+
_ => Self::Unknown(value),
}
}
}
impl From<$b> for $a {
fn from(value: $b) -> Self {
match value {
$($y => $x,)+
<$b>::Unknown(value) => value
}
}
}
}
}
#[cfg(feature = "numeric_rw")]
mod numeric_rw {
use super::*;
macro_rules! impl_rw_numeric {
($number_type:ty) => {
impl StaticallySized for $number_type {
const SIZE: usize = { ::core::mem::size_of::<$number_type>() };
}
impl ReadFixedCtx<{ ::core::mem::size_of::<$number_type>() }, &Endian> for $number_type {
fn from_bytes(
data: &[u8; { ::core::mem::size_of::<$number_type>() }],
ctx: &Endian,
) -> Result<Self, ParserError> {
let function = match *ctx {
Endian::Little => <$number_type>::from_le_bytes,
Endian::Big => <$number_type>::from_be_bytes,
};
Ok(function(*data))
}
}
#[cfg(feature = "non_fixed")]
impl ReadCtx<&Endian> for $number_type {
fn from_bytes(
data: &mut (impl ExactSizeIterator<Item = u8> + Clone),
ctx: &Endian,
) -> Result<Self, ParserError> {
let mut iter =
::try_take::try_take(data, { ::core::mem::size_of::<$number_type>() })
.map_err(ParserError::TooLittleData)?;
<$number_type as ReadFixedCtx<
{ ::core::mem::size_of::<$number_type>() },
&Endian,
>>::from_bytes(&iter.next_chunk().unwrap(), ctx)
}
}
impl WriteFixedCtx<{ ::core::mem::size_of::<$number_type>() }, &Endian>
for $number_type
{
fn to_bytes(
&self,
ctx: &Endian,
) -> [u8; { ::core::mem::size_of::<$number_type>() }] {
let function = match *ctx {
Endian::Little => <$number_type>::to_le_bytes,
Endian::Big => <$number_type>::to_be_bytes,
};
function(*self)
}
}
#[cfg(feature = "non_fixed")]
impl<'a> WriteCtx<&Endian> for $number_type {
fn to_bytes(&self, ctx: &Endian) -> ::alloc::vec::Vec<u8> {
<Self as WriteFixedCtx<{ ::core::mem::size_of::<$number_type>() }, &Endian>>::to_bytes(self, ctx).to_vec()
}
}
};
}
impl_rw_numeric!(u8);
impl_rw_numeric!(i8);
impl_rw_numeric!(u16);
impl_rw_numeric!(i16);
impl_rw_numeric!(u32);
impl_rw_numeric!(i32);
impl_rw_numeric!(u64);
impl_rw_numeric!(i64);
impl_rw_numeric!(u128);
impl_rw_numeric!(i128);
}
#[cfg(feature = "numeric_rw")]
pub use numeric_rw::*;