bin_utils/
lib.rs

1#![allow(incomplete_features)]
2#![feature(
3    more_qualified_paths,
4    iter_next_chunk,
5    generic_const_exprs,
6    iter_array_chunks
7)]
8#![no_std]
9
10#[cfg(feature = "non_fixed")]
11extern crate alloc;
12
13use core::fmt::Debug;
14
15#[cfg(feature = "non_fixed")]
16use alloc::vec::Vec;
17
18#[derive(Debug)]
19/// A generic error encountered while parsing.
20pub enum ParserError {
21    /// The parser expected more data than it got.
22    TooLittleData(usize),
23    /// Just like TooLittleData, but more specific.
24    HeaderIncomplete(usize),
25    /// The expected magic was invalid.
26    InvalidMagic,
27    /// A value wasn't understood by the parser.
28    ValueNotUnderstood,
29    /// While parsing an array, the parser errored earlier than expected.
30    ArrayTooShort,
31}
32
33/// Relevant for parsing numbers.
34#[derive(Debug, Copy, Clone)]
35pub enum Endian {
36    Little,
37    Big,
38}
39/// A trait indicating, that a type only has one fixed size.
40pub trait StaticallySized {
41    const SIZE: usize;
42}
43/// A trait for reading a non fixed amount of data.
44pub trait Read
45where
46    Self: Sized,
47{
48    fn from_bytes(data: &mut impl ExactSizeIterator<Item = u8>) -> Result<Self, ParserError>;
49}
50/// A trait for reading a non fixed amount of data, with context.
51pub trait ReadCtx<Ctx>
52where
53    Self: Sized,
54{
55    fn from_bytes(
56        data: &mut impl ExactSizeIterator<Item = u8>,
57        ctx: Ctx,
58    ) -> Result<Self, ParserError>;
59}
60#[cfg(feature = "non_fixed")]
61/// A trait for writing data of variable length.
62pub trait Write {
63    fn to_bytes(&self) -> Vec<u8>;
64}
65#[cfg(feature = "capped")]
66/// A trait for writing a capped amount of data.
67pub trait WriteCapped<const CAP: usize> {
68    fn to_bytes(&self) -> heapless::Vec<u8, CAP>;
69}
70#[cfg(feature = "non_fixed")]
71/// A trait for writing data of variable length, with context.
72pub trait WriteCtx<Ctx> {
73    fn to_bytes(&self, ctx: Ctx) -> Vec<u8>;
74}#[cfg(feature = "capped")]
75/// A trait for writing a capped amount of data, with context.
76pub trait WriteCappedCtx<const CAP: usize, Ctx> {
77    fn to_bytes(&self, ctx: Ctx) -> heapless::Vec<u8, CAP>;
78}
79
80/// A trait for reading data of fixed length.
81pub trait ReadFixed<const N: usize>
82where
83    Self: Sized,
84{
85    fn from_bytes(data: &[u8; N]) -> Result<Self, ParserError>;
86}
87/// A trait for reading data of fixed length, with context.
88pub trait ReadFixedCtx<const N: usize, Ctx>
89where
90    Self: Sized,
91{
92    fn from_bytes(data: &[u8; N], ctx: Ctx) -> Result<Self, ParserError>;
93}
94/// A trait for writing data of fixed length.
95pub trait WriteFixed<const N: usize>
96where
97    Self: Sized,
98{
99    fn to_bytes(&self) -> [u8; N];
100}
101/// A trait for writing data of fixed length, with context.
102pub trait WriteFixedCtx<const N: usize, Ctx>
103where
104    Self: Sized,
105{
106    fn to_bytes(&self, ctx: Ctx) -> [u8; N];
107}
108#[cfg(feature = "non_fixed")]
109impl<T> Read for Vec<T>
110where
111    T: Read,
112{
113    fn from_bytes(data: &mut impl ExactSizeIterator<Item = u8>) -> Result<Self, ParserError> {
114        Ok((0..).map_while(|_| T::from_bytes(data).ok()).collect()) // Create an infinte iterator, map until from_bytes returns an Error and collect.
115    }
116}
117
118#[cfg(feature = "non_fixed")]
119impl<T, Ctx> ReadCtx<Ctx> for Vec<T>
120where
121    T: ReadCtx<Ctx>,
122    Ctx: Clone,
123{
124    fn from_bytes(
125        data: &mut impl ExactSizeIterator<Item = u8>,
126        ctx: Ctx,
127    ) -> Result<Self, ParserError> {
128        Ok((0..)
129            .map_while(|_| T::from_bytes(data, ctx.clone()).ok())
130            .collect()) // Create an infinte iterator, map until from_bytes returns an Error and collect.
131    }
132}
133#[cfg(feature = "non_fixed")]
134impl<T> Write for Vec<T>
135where
136    T: Write,
137{
138    fn to_bytes(&self) -> Vec<u8> {
139        self.iter().flat_map(T::to_bytes).collect()
140    }
141}
142#[cfg(feature = "non_fixed")]
143impl<T, Ctx> WriteCtx<Ctx> for Vec<T>
144where
145    T: WriteCtx<Ctx>,
146    Ctx: Clone,
147{
148    fn to_bytes(&self, ctx: Ctx) -> Vec<u8> {
149        self.iter().flat_map(|x| x.to_bytes(ctx.clone())).collect()
150    }
151}
152impl<const N: usize, T> ReadFixed<{ T::SIZE * N }> for [T; N]
153where
154    T: StaticallySized + ReadFixed<{ T::SIZE }> + Default + Copy,
155    [(); T::SIZE * N]:,
156{
157    fn from_bytes(data: &[u8; T::SIZE * N]) -> Result<Self, ParserError> {
158        let mut output = [T::default(); N];
159        for (i, x) in data
160            .iter()
161            .copied()
162            .array_chunks::<{ T::SIZE }>()
163            .map(|x| T::from_bytes(&x))
164            .enumerate()
165        {
166            output[i] = x?;
167        }
168        Ok(output)
169    }
170}
171impl<const N: usize, T> WriteFixed<{ T::SIZE * N }> for [T; N]
172where
173    T: StaticallySized + WriteFixed<{ T::SIZE }> + Default + Copy,
174    [(); T::SIZE * N]:,
175{
176    fn to_bytes(&self) -> [u8; T::SIZE * N] {
177        self.iter().flat_map(T::to_bytes).next_chunk().unwrap()
178    }
179}
180#[cfg(feature = "non_fixed")]
181impl<const N: usize, T: Read + Default + Copy> Read for [T; N] {
182    fn from_bytes(data: &mut impl ExactSizeIterator<Item = u8>) -> Result<Self, ParserError> {
183        (0..N)
184            .map_while(|_| T::from_bytes(data).ok())
185            .next_chunk()
186            .map_err(|_| ParserError::ArrayTooShort)
187    }
188}
189#[cfg(feature = "non_fixed")]
190impl<const N: usize, T: Write> Write for [T; N] {
191    fn to_bytes(&self) -> Vec<u8> {
192        self.iter().flat_map(T::to_bytes).collect()
193    }
194}
195#[macro_export]
196/// This macro allows turning enums into numbers and vice versa.
197/// ```
198/// #![feature(more_qualified_paths)]
199///
200/// #[derive(Debug, PartialEq)]
201/// enum ABC {
202///     A,
203///     B,
204///     C,
205///     Unknown(u8),
206/// }
207/// bin_utils::enum_to_int! {
208///     u8,
209///     ABC,
210///
211///     0x01,
212///     ABC::A,
213///     0x02,
214///     ABC::B,
215///     0x03,
216///     ABC::C
217/// }
218///
219/// let a: ABC = 0x01.into();
220/// assert_eq!(a, ABC::A);
221/// let a: u8 = a.into();
222/// assert_eq!(a, 0x01);
223/// let unknown: ABC = 0xff.into();
224/// assert_eq!(unknown, ABC::Unknown(0xff));
225/// ```
226macro_rules! enum_to_int {
227    ($a:ty, $b:ty, $($x:expr, $y:path), +) => {
228        impl From<$a> for $b {
229            fn from(value: $a) -> Self {
230                match value {
231                    $($x => $y,)+
232                    _ => Self::Unknown(value),
233                }
234            }
235        }
236        impl From<$b> for $a {
237            fn from(value: $b) -> Self {
238                match value {
239                    $($y => $x,)+
240                    <$b>::Unknown(value) => value
241                }
242            }
243        }
244    }
245}
246
247#[cfg(feature = "numeric_rw")]
248mod numeric_rw {
249    use super::*;
250    macro_rules! impl_rw_numeric {
251        ($number_type:ty) => {
252            impl StaticallySized for $number_type {
253                const SIZE: usize = { ::core::mem::size_of::<$number_type>() };
254            }
255            impl ReadFixedCtx<{ ::core::mem::size_of::<$number_type>() }, &Endian> for $number_type {
256                fn from_bytes(
257                    data: &[u8; { ::core::mem::size_of::<$number_type>() }],
258                    ctx: &Endian,
259                ) -> Result<Self, ParserError> {
260                    let function = match *ctx {
261                        Endian::Little => <$number_type>::from_le_bytes,
262                        Endian::Big => <$number_type>::from_be_bytes,
263                    };
264                    Ok(function(*data))
265                }
266            }
267            #[cfg(feature = "non_fixed")]
268            impl ReadCtx<&Endian> for $number_type {
269                fn from_bytes(
270                    data: &mut impl ExactSizeIterator<Item = u8>,
271                    ctx: &Endian,
272                ) -> Result<Self, ParserError> {
273                    let mut iter =
274                        ::try_take::try_take(data, { ::core::mem::size_of::<$number_type>() })
275                            .map_err(ParserError::TooLittleData)?;
276                    <$number_type as ReadFixedCtx<
277                        { ::core::mem::size_of::<$number_type>() },
278                        &Endian,
279                    >>::from_bytes(&iter.next_chunk().unwrap(), ctx)
280                }
281            }
282            impl WriteFixedCtx<{ ::core::mem::size_of::<$number_type>() }, &Endian>
283                for $number_type
284            {
285                fn to_bytes(
286                    &self,
287                    ctx: &Endian,
288                ) -> [u8; { ::core::mem::size_of::<$number_type>() }] {
289                    let function = match *ctx {
290                        Endian::Little => <$number_type>::to_le_bytes,
291                        Endian::Big => <$number_type>::to_be_bytes,
292                    };
293                    function(*self)
294                }
295            }
296            #[cfg(feature = "non_fixed")]
297            impl<'a> WriteCtx<&Endian> for $number_type {
298                fn to_bytes(&self, ctx: &Endian) -> ::alloc::vec::Vec<u8> {
299                    <Self as WriteFixedCtx<{ ::core::mem::size_of::<$number_type>() }, &Endian>>::to_bytes(self, ctx).to_vec()
300                }
301            }
302        };
303    }
304    impl_rw_numeric!(u8);
305    impl_rw_numeric!(i8);
306    impl_rw_numeric!(u16);
307    impl_rw_numeric!(i16);
308    impl_rw_numeric!(u32);
309    impl_rw_numeric!(i32);
310    impl_rw_numeric!(u64);
311    impl_rw_numeric!(i64);
312    impl_rw_numeric!(u128);
313    impl_rw_numeric!(i128);
314}
315#[cfg(feature = "numeric_rw")]
316pub use numeric_rw::*;