use core::array::TryFromSliceError;
use core::mem::size_of;
use paste::paste;
use super::*;
pub trait FromLeBytes<const N: usize>: Sized {
fn from_le_bytes(bytes: [u8; N]) -> Self;
}
pub trait LeBytesInto<T> {
fn le_bytes_into(self) -> T;
}
impl<T, const N: usize> LeBytesInto<T> for [u8; N]
where
T: FromLeBytes<N>,
{
fn le_bytes_into(self) -> T {
T::from_le_bytes(self)
}
}
impl FromLeBytes<1> for u8 {
fn from_le_bytes(bytes: [u8; 1]) -> Self {
bytes[0]
}
}
impl<const N: usize> FromLeBytes<N> for [u8; N] {
fn from_le_bytes(bytes: [u8; N]) -> Self {
bytes
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Le<T>(pub T);
impl<T: FromLeBytes<N>, const N: usize> From<[u8; N]> for Le<T> {
fn from(data: [u8; N]) -> Self {
Le(data.le_bytes_into())
}
}
pub trait TryFromLeBytes<const N: usize>: Sized + FromLeBytes<N> {
fn try_from_le_bytes(slice: &[u8]) -> Result<Seq<Self, &[u8]>, TryFromSliceError>;
}
impl<T: FromLeBytes<N>, const N: usize> TryFromLeBytes<N> for T {
fn try_from_le_bytes(slice: &[u8]) -> Result<Seq<Self, &[u8]>, TryFromSliceError> {
let (head, tail) = slice.split_at(slice.len().min(size_of::<Self>()));
let head = head.try_into().map(FromLeBytes::from_le_bytes)?;
Ok(Seq { head, tail })
}
}
pub trait TryFromBeBytes<const N: usize>: Sized + FromBeBytes<N> {
fn try_from_be_bytes(slice: &[u8]) -> Result<Seq<Self, &[u8]>, TryFromSliceError>;
}
impl<T: FromBeBytes<N>, const N: usize> TryFromBeBytes<N> for T {
fn try_from_be_bytes(slice: &[u8]) -> Result<Seq<Self, &[u8]>, TryFromSliceError> {
let (head, tail) = slice.split_at(slice.len().min(size_of::<Self>()));
let head = head.try_into().map(FromBeBytes::from_be_bytes)?;
Ok(Seq { head, tail })
}
}
pub trait LeBytesTryInto<'a, T, const N: usize> {
fn le_bytes_try_into(self) -> Result<Seq<T, &'a [u8]>, TryFromSliceError>;
}
impl<'a, T, const N: usize> LeBytesTryInto<'a, T, N> for &'a [u8]
where
T: TryFromLeBytes<N>,
{
fn le_bytes_try_into(self) -> Result<Seq<T, &'a [u8]>, TryFromSliceError> {
T::try_from_le_bytes(self)
}
}
pub trait BeBytesTryInto<'a, T, const N: usize> {
fn be_bytes_try_into(self) -> Result<Seq<T, &'a [u8]>, TryFromSliceError>;
}
impl<'a, T, const N: usize> BeBytesTryInto<'a, T, N> for &'a [u8]
where
T: TryFromBeBytes<N>,
{
fn be_bytes_try_into(self) -> Result<Seq<T, &'a [u8]>, TryFromSliceError> {
T::try_from_be_bytes(self)
}
}
pub trait FromBeBytes<const N: usize>: Sized {
fn from_be_bytes(bytes: [u8; N]) -> Self;
}
pub trait BeBytesInto<T> {
fn be_bytes_into(self) -> T;
}
impl<T, const N: usize> BeBytesInto<T> for [u8; N]
where
T: FromBeBytes<N>,
{
fn be_bytes_into(self) -> T {
T::from_be_bytes(self)
}
}
impl FromBeBytes<1> for u8 {
fn from_be_bytes(bytes: [u8; 1]) -> Self {
bytes[0]
}
}
impl<const N: usize> FromBeBytes<N> for [u8; N] {
fn from_be_bytes(bytes: [u8; N]) -> Self {
bytes
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Be<T>(pub T);
impl<T: FromBeBytes<N>, const N: usize> From<[u8; N]> for Be<T> {
fn from(data: [u8; N]) -> Self {
Be(data.be_bytes_into())
}
}
macro_rules! endianness_integer_impl {
(Common: $e:ident => $($t:ty),+ $(,)?) => { paste!{ $(
impl [<From $e Bytes>]<{ size_of::<Self>() }> for $t {
fn [<from_ $e:lower _bytes>](bytes: [u8; size_of::<$t>()]) -> Self {
$t::[<from_ $e:lower _bytes>](bytes)
}
}
impl<const N: usize, const M: usize> [<From $e Bytes>]<N> for [$t;M] {
fn [<from_ $e:lower _bytes>](bytes: [u8;N]) -> Self {
const {
const MSG: &str =
concat!("The size of [", stringify!($t), "; M] and [u8; N] are different");
assert!(size_of::<Self>() == N, "{}", MSG);
}
const SIZE: usize = size_of::<$t>();
let mut result = [0;M];
for (n, data) in bytes.chunks_exact(SIZE).enumerate() {
match <[u8;SIZE]>::try_from(data) {
Ok(data) => result[n] = data.[<$e:lower _bytes_into>](),
Err(_) => break,
}
}
result
}
}
)+ }};
(Le => $($t:ty),+ $(,)?) => { $(
)+ };
(Be => $($t:ty),+ $(,)?) => { $(
)+ };
($($ty:ty),+ $(,)?) => {
endianness_integer_impl!(Common: Le => $($ty,)+);
endianness_integer_impl!(Common: Be => $($ty,)+);
};
}
endianness_integer_impl!(u16, u32, u64, u128, usize);
macro_rules! endianness_tuple_impl {
(Common: $e:ident => $len:literal: $($cl:ident),+ $(,)?) => { paste!{
impl<$($cl,)+> From<($($e<$cl>,)+)> for $e<($($cl,)+)> {
fn from(($($e([<$cl:lower>]),)+): ($($e<$cl>,)+)) -> Self {
$e(($([<$cl:lower>],)+))
}
}
impl<TY, $($cl,)+ $(const [<$cl N>]: usize,)+>
From<[<T $len>]<$([TY;[<$cl N>]],)+>> for $e<($($cl,)+)>
where
$($e<$cl>: From<[TY;[<$cl N>]]>,)+
{
fn from(data: [<T $len>]<$([TY;[<$cl N>]],)+>) -> Self {
<($($e<$cl>,)+)>::from(data).into()
}
}
impl<TY, $($cl,)+ const NU: usize, $(const [<$cl N>]: usize,)+>
From<[<P $len>]<[TY;NU],$([<$cl N>],)+>> for $e<($($cl,)+)>
where
TY: Copy + Default,
$($e<$cl>: From<[TY;[<$cl N>]]>,)+
{
fn from(data: [<P $len>]<[TY;NU],$([<$cl N>],)+>) -> Self {
<($($e<$cl>,)+)>::from(data).into()
}
}
impl<$($cl,)+ const NU: usize, $(const [<$cl N>]: usize,)+>
[<From $e Bytes>]<NU> for [<P $len>]<($($cl,)+), $([<$cl N>],)+>
where
$( $cl: [<From $e Bytes>]<[<$cl N>]>, )+
{
fn [<from_ $e:lower _bytes>](bytes: [u8;NU]) -> Self {
let [<T $len>]($([<$cl:lower>],)+) = bytes.into();
[<P $len>](($([<$cl:lower>].[<$e:lower _bytes_into>](),)+))
}
}
}};
(Le => $len:literal: $($cl:ident),+ $(,)?) => { paste!{
}};
(Be => $len:literal: $($cl:ident),+ $(,)?) => { paste!{
}};
($len:literal: $($cl:ident),+ $(,)?) => {
endianness_tuple_impl!(Common: Le => $len: $($cl),+);
endianness_tuple_impl!(Common: Be => $len: $($cl),+);
};
}
endianness_tuple_impl!(1: A);
endianness_tuple_impl!(2: A,B);
endianness_tuple_impl!(3: A,B,C);
endianness_tuple_impl!(4: A,B,C,D);
endianness_tuple_impl!(5: A,B,C,D,E);
endianness_tuple_impl!(6: A,B,C,D,E,F);
endianness_tuple_impl!(7: A,B,C,D,E,F,G);
endianness_tuple_impl!(8: A,B,C,D,E,F,G,H);
endianness_tuple_impl!(9: A,B,C,D,E,F,G,H,I);
endianness_tuple_impl!(10: A,B,C,D,E,F,G,H,I,J);
endianness_tuple_impl!(11: A,B,C,D,E,F,G,H,I,J,K);
endianness_tuple_impl!(12: A,B,C,D,E,F,G,H,I,J,K,L);
endianness_tuple_impl!(13: A,B,C,D,E,F,G,H,I,J,K,L,M);
endianness_tuple_impl!(14: A,B,C,D,E,F,G,H,I,J,K,L,M,N);
endianness_tuple_impl!(15: A,B,C,D,E,F,G,H,I,J,K,L,M,N,O);
endianness_tuple_impl!(16: A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P);
endianness_tuple_impl!(17: A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q);
endianness_tuple_impl!(18: A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R);
endianness_tuple_impl!(19: A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S);
endianness_tuple_impl!(20: A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T);
endianness_tuple_impl!(21: A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U);
endianness_tuple_impl!(22: A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V);
endianness_tuple_impl!(23: A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W);
endianness_tuple_impl!(24: A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X);
endianness_tuple_impl!(25: A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y);
endianness_tuple_impl!(26: A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z);
macro_rules! endianness_for_wrappers_impl {
($e:ident @ $($ty:ty),+) => {paste!{ $(
impl<T: From<[<$ty:lower>]>> [<From $e Bytes>]<{ size_of::<[<$ty:lower>]>() }> for $ty<T> {
fn [<from_ $e:lower _bytes>](bytes: [u8; size_of::<[<$ty:lower>]>()]) -> Self {
Self([<$ty:lower>]::[<from_ $e:lower _bytes>](bytes).into())
}
}
)+ }};
($($ty:ty),+) => {
endianness_for_wrappers_impl!(Le @ $($ty),+);
endianness_for_wrappers_impl!(Be @ $($ty),+);
};
}
endianness_for_wrappers_impl!(U8, U16, U32, U64, U128, Usize);
#[cfg(test)]
mod tests {
use super::*;
const DATA: [u8; 31] = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE,
0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
0xEE,
];
const RESULT_LE_U8: u8 = 0x00;
const RESULT_LE_U16: u16 = 0x1100;
const RESULT_LE_U32: u32 = 0x33221100;
const RESULT_LE_U64: u64 = 0x7766554433221100;
const RESULT_LE_U128: u128 = 0xFFEEDDCCBBAA99887766554433221100;
const RESULT_BE_U8: u8 = 0x00;
const RESULT_BE_U16: u16 = 0x0011;
const RESULT_BE_U32: u32 = 0x00112233;
const RESULT_BE_U64: u64 = 0x0011223344556677;
const RESULT_BE_U128: u128 = 0x00112233445566778899AABBCCDDEEFF;
#[test]
fn le_bytes_into_integer_array() {
let data: [u8; 16] = DATA[..16].try_into().unwrap();
let result: [u8; 16] = data.le_bytes_into();
assert_eq!(data, result, "[u8;16]");
let result: [u16; 8] = data.le_bytes_into();
let sample = [
0x1100, 0x3322, 0x5544, 0x7766, 0x9988, 0xBBAA, 0xDDCC, 0xFFEE,
];
assert_eq!(sample, result, "[u16;8]");
let result: [u32; 4] = data.le_bytes_into();
let sample = [0x33221100, 0x77665544, 0xBBAA9988, 0xFFEEDDCC];
assert_eq!(sample, result, "[u32;4]");
let result: [u64; 2] = data.le_bytes_into();
let sample = [0x7766554433221100, 0xFFEEDDCCBBAA9988];
assert_eq!(sample, result, "[u64;2]");
let result: [u128; 1] = data.le_bytes_into();
let sample = [0xFFEEDDCCBBAA99887766554433221100];
assert_eq!(sample, result, "[u128;1]");
}
#[test]
fn be_bytes_into_integer_array() {
let data: [u8; 16] = DATA[..16].try_into().unwrap();
let result: [u8; 16] = data.be_bytes_into();
assert_eq!(data, result, "[u8;16]");
let result: [u16; 8] = data.be_bytes_into();
let sample = [
0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xAABB, 0xCCDD, 0xEEFF,
];
assert_eq!(sample, result, "[u16;8]");
let result: [u32; 4] = data.be_bytes_into();
let sample = [0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF];
assert_eq!(sample, result, "[u32;4]");
let result: [u64; 2] = data.be_bytes_into();
let sample = [0x0011223344556677, 0x8899AABBCCDDEEFF];
assert_eq!(sample, result, "[u64;2]");
let result: [u128; 1] = data.be_bytes_into();
let sample = [0x00112233445566778899AABBCCDDEEFF];
assert_eq!(sample, result, "[u128;1]");
}
#[test]
fn into_le_integer_array_wrapper() {
let data: [u8; 16] = DATA[..16].try_into().unwrap();
let result: Le<[u8; 16]> = data.into();
assert_eq!(data, result.0, "Le<[u8;16]>");
let result: Le<[u16; 8]> = data.into();
let sample = [
0x1100u16, 0x3322, 0x5544, 0x7766, 0x9988, 0xBBAA, 0xDDCC, 0xFFEE,
];
assert_eq!(sample, result.0, "Le<[u16;8]>");
let result: Le<[u32; 4]> = data.into();
let sample = [0x33221100, 0x77665544, 0xBBAA9988, 0xFFEEDDCC];
assert_eq!(sample, result.0, "Le<[u32;4]>");
let result: Le<[u64; 2]> = data.into();
let sample = [0x7766554433221100, 0xFFEEDDCCBBAA9988];
assert_eq!(sample, result.0, "Le<[u64;2]>");
let result: Le<[u128; 1]> = data.into();
let sample = [0xFFEEDDCCBBAA99887766554433221100];
assert_eq!(sample, result.0, "Le<[u128;1]>");
}
#[test]
fn into_mixed_integers() {
let data: [u8; 8] = DATA[..8].try_into().unwrap();
let (Be(a), Le(b), c) = T3::from(data).into();
let _: (u16, u32, [u8; 2]) = (a, b, c);
let sample = (0x0011, 0x55443322, [0x66, 0x77]);
assert_eq!(sample, (a, b, c), "mixed integers");
}
#[test]
fn into_mixed_arrays() {
let data: [u8; 8] = DATA[..8].try_into().unwrap();
let (Be(a), Le(b)) = P2::<_, 4, 4>(data).into();
let _: ([u16; 2], [u32; 1]) = (a, b);
let sample = ([0x0011, 0x2233], [0x77665544]);
assert_eq!(sample, (a, b), "mixed arrays");
}
#[test]
fn into_tupled_integers() {
let result: Le<(u8, u16, u32, u64, u128)> = T5::from(DATA).into();
let sample = (
0x00,
0x2211,
0x66554433,
0xEEDDCCBBAA998877,
0xEEDDCCBBAA99887766554433221100FF,
);
assert_eq!(sample, result.0, "u8 .. u128");
let result: Le<(usize,)> = T1::from(0x1234usize.to_le_bytes()).into();
assert_eq!(0x1234usize, result.0 .0, "usize");
}
#[test]
fn destructuring() {
if let Le((0x1100u16, data)) = T2::from(DATA).into() {
let _: [u8; 29] = data;
} else {
panic!();
}
}
#[test]
fn le_bytes_into_mixed_integers() {
let data: [u8; 8] = DATA[..8].try_into().unwrap();
let P3((a, b, c)) = data.le_bytes_into();
let _: (u16, u32, [u8; 2]) = (a, b, c);
let sample = (0x1100, 0x55443322, [0x66, 0x77]);
assert_eq!(sample, (a, b, c), "mixed integers");
}
macro_rules! integers {
($e:ident => $($ty:ty),+ $(,)?) => { paste!{ $(
#[test]
fn [<try_from_ $e:lower _bytes_ $ty>]() {
let Seq { head: result, .. } =
[<TryFrom $e Bytes>]::[<try_from_ $e:lower _bytes>](&DATA[..size_of::<$ty>()])
.unwrap();
assert_eq!([<RESULT_ $e:upper _ $ty:upper>], result);
}
#[test]
fn [<$e:lower _bytes_into_ $ty>]() {
let data: [u8; size_of::<$ty>()] =
DATA[..size_of::<$ty>()].try_into().unwrap();
let result: $ty = data.[<$e:lower _bytes_into>]();
assert_eq!([<RESULT_ $e:upper _ $ty:upper>], result);
}
#[test]
fn [<into_ $e:lower _wrapper_ $ty>]() {
let data: [u8; size_of::<$ty>()] =
DATA[..size_of::<$ty>()].try_into().unwrap();
let result: [<$e>]<$ty> = data.into();
assert_eq!([<RESULT_ $e:upper _ $ty:upper>], result.0);
}
#[test]
fn [<$e:lower _bytes_try_into_ $ty>]() {
let Seq { head: result, .. } =
DATA[..size_of::<$ty>()].[<$e:lower _bytes_try_into>]().unwrap();
assert_eq!([<RESULT_ $e:upper _ $ty:upper>], result);
}
)+ } }
}
integers!(Le => u8,u16,u32,u64,u128);
integers!(Be => u8,u16,u32,u64,u128);
}