#[cfg(not(feature = "std"))]
use alloc::{format, string::ToString, vec::Vec};
use crate::convert::{TryToUsize, u32_from};
use crate::datatype::{CompoundMember, Datatype, DatatypeByteOrder};
use crate::error::FormatError;
use crate::type_builders::{
make_f32_type, make_f64_type, make_i8_type, make_i16_type, make_i32_type, make_i64_type,
make_u8_type, make_u16_type, make_u32_type, make_u64_type,
};
pub trait CompoundField: Sized {
fn datatype() -> Result<Datatype, FormatError>;
fn encode_field(&self, output: &mut Vec<u8>);
fn decode_field(datatype: &Datatype, bytes: &[u8]) -> Result<Self, FormatError>;
}
pub trait CompoundType: Sized {
fn datatype() -> Result<Datatype, FormatError>;
fn encode(&self, output: &mut Vec<u8>);
fn decode(datatype: &Datatype, bytes: &[u8]) -> Result<Self, FormatError>;
}
fn require_bytes(bytes: &[u8], size: usize) -> Result<&[u8], FormatError> {
if bytes.len() != size {
return Err(FormatError::DataSizeMismatch {
expected: size,
actual: bytes.len(),
});
}
Ok(bytes)
}
fn integer_order(
datatype: &Datatype,
size: u32,
signed: bool,
name: &str,
) -> Result<DatatypeByteOrder, FormatError> {
match datatype {
Datatype::FixedPoint {
size: actual_size,
byte_order,
signed: actual_signed,
bit_offset: 0,
bit_precision,
} if *actual_size == size
&& *actual_signed == signed
&& u32::from(*bit_precision) == size * 8 =>
{
Ok(byte_order.clone())
}
_ => Err(FormatError::CompoundFieldTypeMismatch(name.to_string())),
}
}
fn float_order(
datatype: &Datatype,
size: u32,
name: &str,
) -> Result<DatatypeByteOrder, FormatError> {
let standard = match (datatype, size) {
(
Datatype::FloatingPoint {
size: 4,
byte_order,
bit_offset: 0,
bit_precision: 32,
exponent_location: 23,
exponent_size: 8,
mantissa_location: 0,
mantissa_size: 23,
exponent_bias: 127,
},
4,
) => Some(byte_order.clone()),
(
Datatype::FloatingPoint {
size: 8,
byte_order,
bit_offset: 0,
bit_precision: 64,
exponent_location: 52,
exponent_size: 11,
mantissa_location: 0,
mantissa_size: 52,
exponent_bias: 1023,
},
8,
) => Some(byte_order.clone()),
_ => None,
};
standard.ok_or_else(|| FormatError::CompoundFieldTypeMismatch(name.to_string()))
}
fn ordered<const N: usize>(bytes: &[u8], order: DatatypeByteOrder) -> Result<[u8; N], FormatError> {
let bytes = require_bytes(bytes, N)?;
let mut array = [0u8; N];
array.copy_from_slice(bytes);
match order {
DatatypeByteOrder::LittleEndian => Ok(array),
DatatypeByteOrder::BigEndian => {
array.reverse();
Ok(array)
}
DatatypeByteOrder::Vax => Err(FormatError::InvalidByteOrder(2)),
}
}
macro_rules! impl_integer_field {
($ty:ty, $make:ident, $size:expr, $signed:expr) => {
impl CompoundField for $ty {
fn datatype() -> Result<Datatype, FormatError> {
Ok($make())
}
fn encode_field(&self, output: &mut Vec<u8>) {
output.extend_from_slice(&self.to_le_bytes());
}
fn decode_field(datatype: &Datatype, bytes: &[u8]) -> Result<Self, FormatError> {
let order = integer_order(datatype, $size, $signed, "")?;
Ok(<$ty>::from_le_bytes(ordered(bytes, order)?))
}
}
};
}
impl_integer_field!(i8, make_i8_type, 1, true);
impl_integer_field!(i16, make_i16_type, 2, true);
impl_integer_field!(i32, make_i32_type, 4, true);
impl_integer_field!(i64, make_i64_type, 8, true);
impl_integer_field!(u8, make_u8_type, 1, false);
impl_integer_field!(u16, make_u16_type, 2, false);
impl_integer_field!(u32, make_u32_type, 4, false);
impl_integer_field!(u64, make_u64_type, 8, false);
macro_rules! impl_float_field {
($ty:ty, $make:ident, $size:expr) => {
impl CompoundField for $ty {
fn datatype() -> Result<Datatype, FormatError> {
Ok($make())
}
fn encode_field(&self, output: &mut Vec<u8>) {
output.extend_from_slice(&self.to_le_bytes());
}
fn decode_field(datatype: &Datatype, bytes: &[u8]) -> Result<Self, FormatError> {
let order = float_order(datatype, $size, "")?;
Ok(<$ty>::from_le_bytes(ordered(bytes, order)?))
}
}
};
}
impl_float_field!(f32, make_f32_type, 4);
impl_float_field!(f64, make_f64_type, 8);
fn compound_parts<'a>(
datatype: &'a Datatype,
bytes: &[u8],
) -> Result<(&'a [CompoundMember], u32), FormatError> {
match datatype {
Datatype::Compound { size, members } => {
require_bytes(bytes, size.to_usize()?)?;
Ok((members, *size))
}
_ => Err(FormatError::TypeMismatch {
expected: "Compound",
actual: "non-Compound",
}),
}
}
fn reported_compound_size(bytes: &[u8]) -> u32 {
u32::try_from(bytes.len()).unwrap_or(u32::MAX)
}
fn decode_named<T: CompoundField>(
members: &[CompoundMember],
bytes: &[u8],
name: &str,
) -> Result<T, FormatError> {
let member = members
.iter()
.find(|member| member.name == name)
.ok_or_else(|| FormatError::CompoundFieldMissing(name.to_string()))?;
let start =
usize::try_from(member.byte_offset).map_err(|_| FormatError::CompoundFieldOutOfBounds {
name: name.to_string(),
offset: member.byte_offset,
field_size: member.datatype.type_size(),
compound_size: reported_compound_size(bytes),
})?;
let end = start
.checked_add(member.datatype.type_size().to_usize()?)
.ok_or_else(|| FormatError::CompoundFieldOutOfBounds {
name: name.to_string(),
offset: member.byte_offset,
field_size: member.datatype.type_size(),
compound_size: reported_compound_size(bytes),
})?;
let field_bytes =
bytes
.get(start..end)
.ok_or_else(|| FormatError::CompoundFieldOutOfBounds {
name: name.to_string(),
offset: member.byte_offset,
field_size: member.datatype.type_size(),
compound_size: reported_compound_size(bytes),
})?;
T::decode_field(&member.datatype, field_bytes).map_err(|error| match error {
FormatError::CompoundFieldTypeMismatch(inner) => {
let path = if inner.is_empty() {
name.to_string()
} else {
format!("{}.{}", name, inner)
};
FormatError::CompoundFieldTypeMismatch(path)
}
other => other,
})
}
macro_rules! impl_compound_tuple {
($($type:ident:$index:tt),+) => {
impl<$($type: CompoundField),+> CompoundType for ($($type,)+) {
fn datatype() -> Result<Datatype, FormatError> {
let mut offset = 0u64;
let mut members = Vec::new();
$(
let datatype = $type::datatype()?;
members.push(CompoundMember {
name: stringify!($index).to_string(),
byte_offset: offset,
datatype: datatype.clone(),
});
offset += u64::from(datatype.type_size());
)+
Ok(Datatype::Compound {
size: u32_from(offset)?,
members,
})
}
fn encode(&self, output: &mut Vec<u8>) {
$(self.$index.encode_field(output);)+
}
fn decode(datatype: &Datatype, bytes: &[u8]) -> Result<Self, FormatError> {
let (members, _) = compound_parts(datatype, bytes)?;
Ok(($(decode_named::<$type>(members, bytes, stringify!($index))?,)+))
}
}
impl<$($type: CompoundField),+> CompoundField for ($($type,)+) {
fn datatype() -> Result<Datatype, FormatError> {
<Self as CompoundType>::datatype()
}
fn encode_field(&self, output: &mut Vec<u8>) {
<Self as CompoundType>::encode(self, output);
}
fn decode_field(
datatype: &Datatype,
bytes: &[u8],
) -> Result<Self, FormatError> {
<Self as CompoundType>::decode(datatype, bytes)
}
}
};
}
impl_compound_tuple!(A:0);
impl_compound_tuple!(A:0, B:1);
impl_compound_tuple!(A:0, B:1, C:2);
impl_compound_tuple!(A:0, B:1, C:2, D:3);
impl_compound_tuple!(A:0, B:1, C:2, D:3, E:4);
impl_compound_tuple!(A:0, B:1, C:2, D:3, E:4, F:5);
impl_compound_tuple!(A:0, B:1, C:2, D:3, E:4, F:5, G:6);
impl_compound_tuple!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7);
impl_compound_tuple!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7, I:8);
impl_compound_tuple!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7, I:8, J:9);
impl_compound_tuple!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7, I:8, J:9, K:10);
impl_compound_tuple!(A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7, I:8, J:9, K:10, L:11);