use core::ops::Add;
use core::ops::Mul;
use core::ops::Sub;
pub use hybrid_array;
#[doc(hidden)]
pub use hybrid_array::Array;
use hybrid_array::ArraySize;
use hybrid_array::AssocArraySize;
#[doc(hidden)]
pub use hybrid_array::sizes;
use hybrid_array::sizes::U0;
use hybrid_array::sizes::U1;
use hybrid_array::typenum::Prod;
use hybrid_array::typenum::Sum;
use super::SizeOf;
use crate::array_to_core;
use crate::chunks::chunks;
#[macro_export]
macro_rules! split_bytes {
(
let bytes [
$($name:ident $(: $ty:ty)? ),* $(,)?
] = $data:expr $(;)?
) => {
$crate::split_array!{
let split [
$($name),*
] = $data;
}
$(
let $name $( : $ty )? = $crate::bytes::decoding::FromByteArray::from_bytes($name) ;
)*
};
}
pub trait FromByteArray {
type Size: ArraySize;
fn from_bytes(array: Array<u8, Self::Size>) -> Self;
}
impl<Size, T> FromByteArray for Array<T, Size>
where
Size: ArraySize,
T: FromByteArray,
Prod<T::Size, Size>: ArraySize,
T::Size: Mul<Size>,
{
type Size = Prod<T::Size, Size>;
fn from_bytes(array: Array<u8, Self::Size>) -> Self {
chunks(array).map(|sub| FromByteArray::from_bytes(sub))
}
}
impl<const N: usize, T> FromByteArray for [T; N]
where
T: FromByteArray,
[T; N]: AssocArraySize,
<[T; N] as AssocArraySize>::Size: ArraySize<ArrayType<T> = [T; N]>,
Prod<T::Size, SizeOf<T, N>>: ArraySize,
T::Size: Mul<SizeOf<T, N>>,
{
type Size = Prod<T::Size, SizeOf<T, N>>;
fn from_bytes(array: Array<u8, Self::Size>) -> Self {
array_to_core(Array::from_bytes(array))
}
}
impl FromByteArray for u8 {
type Size = U1;
fn from_bytes(array: Array<u8, Self::Size>) -> Self {
let [n] = array.0;
n
}
}
impl FromByteArray for i8 {
type Size = U1;
fn from_bytes(array: Array<u8, Self::Size>) -> Self {
let [n] = array.0;
n as i8
}
}
impl FromByteArray for () {
type Size = U0;
fn from_bytes(array: Array<u8, Self::Size>) -> Self {
let [] = array.0;
}
}
impl<T1> FromByteArray for (T1,)
where
T1: FromByteArray,
{
type Size = T1::Size;
fn from_bytes(array: Array<u8, Self::Size>) -> Self {
(T1::from_bytes(array),)
}
}
impl<T1, T2> FromByteArray for (T1, T2)
where
T1: FromByteArray,
T2: FromByteArray,
T1::Size: Add<T2::Size>,
Sum<T1::Size, T2::Size>: ArraySize + Sub<T1::Size, Output = T2::Size>,
{
type Size = Sum<T1::Size, T2::Size>;
fn from_bytes(array: Array<u8, Self::Size>) -> Self {
let (sub1, sub2) = array.split();
(T1::from_bytes(sub1), T2::from_bytes(sub2))
}
}
pub fn parse_from_array<T>(array: Array<u8, T::Size>) -> T
where
T: FromByteArray,
{
T::from_bytes(array)
}
#[cfg(test)]
mod test {
use hybrid_array::sizes::U2;
use hybrid_array::sizes::U4;
use super::*;
use crate::array_from_core;
use crate::bytes::BigEndian;
use crate::chunks::chunks_from_core;
use crate::chunks::flatten;
struct Foo(u16);
impl FromByteArray for Foo {
type Size = U2;
fn from_bytes(array: Array<u8, Self::Size>) -> Self {
Foo(u16::from_be_bytes(array.0))
}
}
struct Bar(u32);
impl FromByteArray for Bar {
type Size = U4;
fn from_bytes(array: Array<u8, Self::Size>) -> Self {
Bar(u32::from_be_bytes(array.0))
}
}
#[test]
fn test_parse_from_array_u8() {
let input = 42_u8;
let data = array_from_core([input]);
let parsed: u8 = parse_from_array(data);
assert_eq!(parsed, input);
}
#[test]
fn test_parse_from_array_id() {
let input = [1, 2, 3, 4];
let data = array_from_core(input);
let parsed: Array<u8, U4> = parse_from_array(data);
assert_eq!(parsed, Array(input));
}
#[test]
fn test_parse_from_array_array() {
let input = [1, 2, 3, 4];
let data = array_from_core(input);
let parsed: [u8; 4] = parse_from_array(data);
assert_eq!(parsed, input);
}
#[test]
fn test_parse_from_array_array_of_arrays() {
let input = [[1, 2], [3, 4]];
let data = flatten(chunks_from_core(input));
let parsed: [[u8; 2]; 2] = parse_from_array(data);
assert_eq!(parsed, input);
}
#[test]
fn test_parse_from_array_tuple() {
let input = [1, 2];
let data = array_from_core(input);
let parsed: (u8, u8) = parse_from_array(data);
assert_eq!(parsed, (1, 2));
}
#[test]
fn test_parse_from_array_custom_type() {
let input = [0, 42];
let data = array_from_core(input);
let parsed: Foo = parse_from_array(data);
assert_eq!(parsed.0, 42);
}
#[test]
fn test_parse_from_array_custom_tuple() {
let input = [0, 42, 0, 0, 0x12, 0x34];
let data = array_from_core(input);
let parsed: (Foo, Bar) = parse_from_array(data);
assert_eq!(parsed.0.0, 42);
assert_eq!(parsed.1.0, 0x1234);
}
#[test]
fn test_split_bytes() {
let data = [
0, 42, 0x0, 0x0, 0x12, 0x34, 0, b'a', b'b', b'c', ];
split_bytes! {
let bytes [
foo,
bar: BigEndian<u32>,
_pad: u8,
alpha: [u8; 3],
] = array_from_core(data);
}
let foo: Foo = foo;
assert_eq!(foo.0, 42);
assert_eq!(bar.0, 0x1234);
assert_eq!(alpha, *b"abc");
}
}