recode 0.5.0-alpha.3

Reusable encoding/decoding constructs for Rust
Documentation
pub use ux::{i24, i40, i48, i56, u24, u40, u48, u56};

use crate::{
    bytes::{Buf, BufMut, BytesMut},
    util::EncoderExt,
    Decoder,
    Encoder,
    RawDecoder,
};

macro_rules! impl_ux {
    ($t:ty; size: $s:literal; rep: $r:ty) => {
        impl Decoder for $t {
            type Error = crate::Error;

            #[inline]
            fn decode(buf: &mut BytesMut) -> Result<Self, Self::Error> {
                let (num, off) = Self::raw_decode(buf)?;
                buf.advance(off);

                Ok(num)
            }

            #[inline]
            fn has_enough_bytes(buf: &BytesMut) -> bool {
                buf.remaining() >= $s
            }
        }

        impl RawDecoder for $t {
            type Error = crate::Error;

            fn raw_decode<'a>(buf: &'a [u8]) -> Result<($t, usize), Self::Error>
            where
                $t: 'a,
            {
                const REPR_LEN: usize = std::mem::size_of::<$r>();

                if buf.remaining() < $s {
                    return Err(crate::Error::BytesNeeded {
                        needed: $s - buf.remaining(),
                        full_len: $s,
                        available: buf.remaining(),
                    });
                }

                let mut be_repr = [0u8; REPR_LEN];
                be_repr[(REPR_LEN - $s)..].copy_from_slice(&buf[..$s]);

                Ok((<$t>::new(<$r>::from_be_bytes(be_repr)), $s))
            }
        }

        impl Encoder for $t {
            type Error = std::convert::Infallible;

            fn encode(
                item: &$t,
                buf: &mut BytesMut,
            ) -> Result<(), Self::Error> {
                const REPR_LEN: usize = std::mem::size_of::<$r>();

                let bytes = &<$r>::from(*item).to_be_bytes()[(REPR_LEN - $s)..];

                buf.put_slice(bytes);

                Ok(())
            }

            #[inline]
            fn size_of(_: &$t) -> usize {
                $s
            }
        }

        impl Decoder<usize> for $t {
            type Error = crate::Error;

            fn decode(buf: &mut bytes::BytesMut) -> Result<usize, Self::Error> {
                let value = <Self as Decoder>::decode(buf)?;

                usize::try_from(<$r>::from(value))
                    .map_err(|_| super::number::TryFromIntError(()))
                    .map_err(Into::into)
            }

            #[inline]
            fn has_enough_bytes(buf: &BytesMut) -> bool {
                <Self as Decoder>::has_enough_bytes(buf)
            }
        }

        impl RawDecoder<usize> for $t {
            type Error = crate::Error;

            #[inline]
            fn raw_decode<'a>(
                buf: &'a [u8],
            ) -> Result<(usize, usize), Self::Error>
            where
                $t: 'a,
            {
                let (value, rx) = <Self as RawDecoder>::raw_decode(buf)?;
                let value: usize = <$r>::from(value)
                    .try_into()
                    .map_err(|_| super::number::TryFromIntError(()))?;

                Ok((value, rx))
            }
        }

        impl Encoder<usize> for $t {
            type Error = crate::Error;

            fn encode(
                item: &usize,
                buf: &mut BytesMut,
            ) -> Result<(), Self::Error> {
                let value = <$r>::try_from(*item)
                    .map_err(|_| super::number::TryFromIntError(()))?;

                <$t>::new(value).encode_to(buf).map_err(Into::into)
            }

            #[inline]
            fn size_of(_: &usize) -> usize {
                $s
            }
        }
    };
}

impl_ux!(i24; size: 3; rep: i32);
impl_ux!(u24; size: 3; rep: u32);

impl_ux!(i40; size: 5; rep: i64);
impl_ux!(u40; size: 5; rep: u64);

impl_ux!(i48; size: 6; rep: i64);
impl_ux!(u48; size: 6; rep: u64);

impl_ux!(i56; size: 7; rep: i64);
impl_ux!(u56; size: 7; rep: u64);

#[cfg(test)]
mod tests {
    use bytes::BytesMut;
    use fake::Fake;

    use super::*;
    use crate::util::EncoderExt;

    macro_rules! test_ux {
        ($t:ty; size: $s:literal; rep: $r:ty ) => {
            paste::paste! {
                #[test]
                fn [<test_ $t>]() {
                    const REPR_LEN: usize = std::mem::size_of::<$r>();

                    let rmax: $r = <$t>::MAX.into();
                    let repr: $r = (0..rmax).fake();

                    if stringify!($t).starts_with("u") {
                        assert_eq!(rmax.trailing_ones(), $s * 8);
                    } else {
                        assert_eq!(rmax.trailing_ones(), $s * 8 - 1);
                    }

                    assert_eq!(repr & !rmax, 0);

                    let value = <$t>::new(repr);
                    let mut bytes = BytesMut::new();

                    assert_eq!($s, value.size());

                    value.encode_to(&mut bytes).unwrap();

                    assert_eq!($s, bytes.len());
                    assert_eq!(&repr.to_be_bytes()[(REPR_LEN - $s)..], &bytes[..]);

                    let (peek, rx): ($t, usize) =
                                      <$t>::raw_decode(bytes.chunk()).unwrap();

                    assert_eq!(rx, $s);
                    assert_eq!(peek, value);

                    let decoded: $t = <$t>::decode(&mut bytes).unwrap();

                    assert_eq!(decoded, value);
                    assert!(!bytes.has_remaining());
                }
            }
        };
    }

    test_ux!(i24; size: 3; rep: i32);
    test_ux!(u24; size: 3; rep: u32);

    test_ux!(i40; size: 5; rep: i64);
    test_ux!(u40; size: 5; rep: u64);

    test_ux!(i48; size: 6; rep: i64);
    test_ux!(u48; size: 6; rep: u64);

    test_ux!(i56; size: 7; rep: i64);
    test_ux!(u56; size: 7; rep: u64);
}