use crate::internal_prelude::v1::*;
use crate::{PackedStructSlice, PackingError, PackingResult, lib_get_mut_slice, lib_get_slice};
#[derive(Debug, Copy, Clone, PartialEq)]
enum StructLength {
Empty,
Dynamic,
Static(usize)
}
struct StructLengthBuilder {
lengths: [StructLength; 16],
i: usize
}
impl StructLengthBuilder {
fn new() -> Self {
StructLengthBuilder {
lengths: [StructLength::Empty; 16],
i: 0
}
}
fn add<S: PackedStructSlice>(&mut self, st: Option<&S>) -> PackingResult<StructLength> {
let len = match S::packed_bytes_size(st) {
Err(PackingError::InstanceRequiredForSize) => StructLength::Dynamic,
Err(e) => return Err(e),
Ok(s) => StructLength::Static(s)
};
let target = lib_get_mut_slice(&mut self.lengths, self.i)?;
*target = len;
self.i += 1;
Ok(len)
}
fn build(self, total_length: usize) -> PackingResult<StructLengths> {
let lengths = lib_get_slice(&self.lengths, ..self.i)?;
let dy = lengths.iter().filter(|l| l == &&StructLength::Dynamic).count();
if dy > 1 {
return Err(PackingError::MoreThanOneDynamicType);
}
let len_static: usize = lengths.iter().filter_map(|l| if let StructLength::Static(s) = l { Some(*s) } else { None }).sum();
let len_dy = if dy == 1 {
total_length - len_static
} else {
0
};
if len_static + len_dy != total_length {
return Err(PackingError::BufferSizeMismatch { expected: len_static + len_dy, actual: total_length });
}
let mut output_lengths = [0; 16];
for (i, l) in lengths.iter().enumerate() {
let output = lib_get_mut_slice(&mut output_lengths, i)?;
*output = match l {
StructLength::Empty => return Err(PackingError::InternalError),
StructLength::Dynamic => len_dy,
StructLength::Static(s) => *s
};
}
StructLengths::new(output_lengths, self.i)
}
}
struct StructLengths {
ranges: [Range<usize>; 16],
len: usize
}
impl StructLengths {
fn new(lengths: [usize; 16], len: usize) -> PackingResult<Self> {
let mut ranges = [0..0, 0..0, 0..0, 0..0, 0..0, 0..0, 0..0, 0..0, 0..0, 0..0, 0..0, 0..0 , 0..0, 0..0, 0..0, 0..0];
let mut n = 0;
for i in 0..len {
let l = lib_get_slice(&lengths, i)?;
let ranges_out = lib_get_mut_slice(&mut ranges, i)?;
*ranges_out = n..(n+l);
n += l;
}
Ok(StructLengths {
ranges,
len
})
}
fn get_ranges(&self) -> PackingResult<&[Range<usize>]> {
lib_get_slice(&self.ranges, ..self.len)
}
}
macro_rules! tuple_impls {
() => {};
(($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )*) => {
tuple_impls!([($idx, $typ);] $( ($nidx => $ntyp), )*);
tuple_impls!($( ($nidx => $ntyp), )*); };
([$(($accIdx: tt, $accTyp: ident);)+] ($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )*) => {
tuple_impls!([($idx, $typ); $(($accIdx, $accTyp); )*] $( ($nidx => $ntyp), ) *);
};
([($idx:tt, $typ:ident); $( ($nidx:tt, $ntyp:ident); )*]) => {
impl<$typ, $( $ntyp ),*> PackedStructSlice for ($typ, $( $ntyp ),*)
where
$typ: PackedStructSlice,
$( $ntyp: PackedStructSlice ),*
{
fn pack_to_slice(&self, output: &mut [u8]) -> PackingResult<()> {
let lengths = {
let mut builder = StructLengthBuilder::new();
builder.add::<$typ>(Some(&self.$idx))?;
$( builder.add::<$ntyp>(Some(&self.$nidx))?; )*
builder.build(output.len())?
};
let ranges = lengths.get_ranges()?;
self.$idx.pack_to_slice(lib_get_mut_slice(output, ranges.get($idx).ok_or(crate::PackingError::InternalError)?.clone())?)?;
$( self.$nidx.pack_to_slice(lib_get_mut_slice(output, ranges.get($nidx).ok_or(crate::PackingError::InternalError)?.clone())?)?; )*
Ok(())
}
fn unpack_from_slice(src: &[u8]) -> PackingResult<Self> {
let lengths = {
let mut builder = StructLengthBuilder::new();
builder.add::<$typ>(None)?;
$( builder.add::<$ntyp>(None)?; )*
builder.build(src.len())?
};
let ranges = lengths.get_ranges()?;
Ok(
(
$typ::unpack_from_slice(lib_get_slice(src, ranges.get($idx).ok_or(crate::PackingError::InternalError)?.clone())?)?,
$( $ntyp::unpack_from_slice(lib_get_slice(src, ranges.get($nidx).ok_or(crate::PackingError::InternalError)?.clone())?)? ),*
)
)
}
fn packed_bytes_size(opt_self: Option<&Self>) -> PackingResult<usize> {
let sizes = [
$typ::packed_bytes_size(opt_self.map(|s| &s.$idx))?,
$( $ntyp::packed_bytes_size(opt_self.map(|s| &s.$nidx))? ),*
];
Ok(sizes.iter().sum())
}
}
};
}
tuple_impls!(
(9 => J),
(8 => I),
(7 => H),
(6 => G),
(5 => F),
(4 => E),
(3 => D),
(2 => C),
(1 => B),
(0 => A),
);