lilliput-core 0.1.0

Low-level implementation of encoding/decoding logic for lilliput format
Documentation
use super::{ToZigZag, TryFromInt, WithBeBytes, WithPackedBeBytes};

macro_rules! impl_try_from_int {
    ($t:ty, infallible: [$($i:ty),* $(,)?], fallible: [$($f:ty),* $(,)?]) => {
        impl_try_from_int!($t, infallible: [$($i),*]);
        impl_try_from_int!($t, fallible: [$($f),*]);
    };
    ($t:ty, infallible: [$($i:ty),* $(,)?]) => {
        $(
            impl TryFromInt<$i> for $t
            where
                $t: TryFrom<$i, Error = core::convert::Infallible>
            {
                #[inline]
                fn try_from_int(int: $i) -> Result<$t, core::num::TryFromIntError> {
                    Ok(int.try_into().unwrap())
                }
            }
        )*
    };
    ($t:ty, fallible: [$($f:ty),* $(,)?]) => {
        $(
            impl TryFromInt<$f> for $t {
                #[inline]
                fn try_from_int(int: $f) -> Result<Self, core::num::TryFromIntError> {
                    int.try_into()
                }
            }
        )*
    };
}

impl_try_from_int!(i8, infallible: [i8], fallible: [i16, i32, i64, u8, u16, u32, u64]);
impl_try_from_int!(i16, infallible: [i8, i16, u8], fallible: [i32, i64, u16, u32, u64]);
impl_try_from_int!(i32, infallible: [i8, i16, i32, u8, u16], fallible: [i64, u32, u64]);
impl_try_from_int!(i64, infallible: [i8, i16, i32, i64, u8, u16, u32], fallible: [u64]);
impl_try_from_int!(isize, infallible: [i8, i16, u8], fallible: [i32, i64, u16, u32, u64]);
impl_try_from_int!(u8, infallible: [u8], fallible: [u16, u32, u64, i8, i16, i32, i64]);
impl_try_from_int!(u16, infallible: [u8, u16], fallible: [u32, u64, i8, i16, i32, i64]);
impl_try_from_int!(u32, infallible: [u8, u16, u32], fallible: [u64, i8, i16, i32, i64]);
impl_try_from_int!(u64, infallible: [u8, u16, u32, u64], fallible: [i8, i16, i32, i64]);
impl_try_from_int!(usize, infallible: [u8, u16, usize], fallible: [u32, u64, i8, i16, i32, i64]);

macro_rules! impl_with_packed_be_bytes_for_unsigned_int {
    ($t:ty) => {
        impl WithBeBytes for $t {
            #[inline]
            fn with_be_bytes<T, F>(&self, f: F) -> T
            where
                F: FnOnce(&[u8]) -> T,
            {
                let bytes = self.to_be_bytes();
                let width = bytes.len();
                debug_assert_eq!(width, bytes.len());

                f(&bytes)
            }
        }

        impl WithPackedBeBytes for $t {
            #[inline]
            fn with_native_packed_be_bytes<T, F>(&self, f: F) -> T
            where
                F: FnOnce(&[u8]) -> T,
            {
                let be_bytes = self.to_be_bytes();
                let width: u8 = {
                    let overflows_u8 = if u8::BITS < Self::BITS {
                        (*self > u8::MAX as Self) as u8
                    } else {
                        0
                    };

                    let overflows_u16 = if u16::BITS < Self::BITS {
                        (*self > u16::MAX as Self) as u8
                    } else {
                        0
                    };

                    let overflows_u32 = if u32::BITS < Self::BITS {
                        (*self > u32::MAX as Self) as u8
                    } else {
                        0
                    };

                    (overflows_u32 << 2) + (overflows_u16 << 1) + overflows_u8 + 1
                };

                let bytes: &[u8] = &be_bytes[(be_bytes.len() - (width as usize))..];

                f(bytes)
            }

            #[inline]
            fn with_optimal_packed_be_bytes<T, F>(&self, f: F) -> T
            where
                F: FnOnce(&[u8]) -> T,
            {
                let be_bytes = self.to_be_bytes();
                let width: u8 = {
                    let leading_zero_bytes = (self.leading_zeros() / u8::BITS) as u8;
                    let native_width = (Self::BITS / u8::BITS) as u8;
                    (native_width - leading_zero_bytes).max(1)
                };
                let bytes: &[u8] = &be_bytes[(be_bytes.len() - (width as usize))..];

                f(bytes)
            }
        }
    };
}

impl_with_packed_be_bytes_for_unsigned_int!(u8);
impl_with_packed_be_bytes_for_unsigned_int!(u16);
impl_with_packed_be_bytes_for_unsigned_int!(u32);
impl_with_packed_be_bytes_for_unsigned_int!(u64);
impl_with_packed_be_bytes_for_unsigned_int!(usize);

macro_rules! impl_with_packed_be_bytes_for_signed_int {
    ($t:ty) => {
        impl WithBeBytes for $t
        where
            $t: ToZigZag,
        {
            #[inline]
            fn with_be_bytes<T, F>(&self, f: F) -> T
            where
                F: FnOnce(&[u8]) -> T,
            {
                self.to_zig_zag().with_be_bytes(f)
            }
        }

        impl WithPackedBeBytes for $t
        where
            $t: ToZigZag,
        {
            #[inline]
            fn with_native_packed_be_bytes<T, F>(&self, f: F) -> T
            where
                F: FnOnce(&[u8]) -> T,
            {
                self.to_zig_zag().with_native_packed_be_bytes(f)
            }

            #[inline]
            fn with_optimal_packed_be_bytes<T, F>(&self, f: F) -> T
            where
                F: FnOnce(&[u8]) -> T,
            {
                self.to_zig_zag().with_optimal_packed_be_bytes(f)
            }
        }
    };
}

impl_with_packed_be_bytes_for_signed_int!(i8);
impl_with_packed_be_bytes_for_signed_int!(i16);
impl_with_packed_be_bytes_for_signed_int!(i32);
impl_with_packed_be_bytes_for_signed_int!(i64);
impl_with_packed_be_bytes_for_signed_int!(isize);