use {
crate::{
TypeMeta,
config::{ConfigCore, ZeroCopy},
error::{ReadResult, WriteResult},
io::{Reader, Writer},
schema::{SchemaRead, SchemaWrite},
},
core::mem::MaybeUninit,
zerocopy::byteorder::{ByteOrder, F32, F64, I16, I32, I64, I128, U16, U32, U64, U128},
};
macro_rules! impl_byteorder {
($($ty:ident),* $(,)?) => {$(
unsafe impl<O: ByteOrder + 'static, C: ConfigCore> ZeroCopy<C> for $ty<O> {}
unsafe impl<O: ByteOrder, C: ConfigCore> SchemaWrite<C> for $ty<O> {
type Src = Self;
const TYPE_META: TypeMeta = TypeMeta::Static {
size: size_of::<Self>(),
zero_copy: true,
};
#[inline]
fn size_of(_src: &Self::Src) -> WriteResult<usize> {
Ok(size_of::<Self>())
}
#[inline]
fn write(mut writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
writer.write(&src.to_bytes())?;
Ok(())
}
}
unsafe impl<'de, O: ByteOrder, C: ConfigCore> SchemaRead<'de, C> for $ty<O> {
type Dst = Self;
const TYPE_META: TypeMeta = TypeMeta::Static {
size: size_of::<Self>(),
zero_copy: true,
};
#[inline]
fn read(mut reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
dst.write(Self::from_bytes(reader.take_array()?));
Ok(())
}
}
)*};
}
impl_byteorder!(U16, U32, U64, U128, I16, I32, I64, I128, F32, F64);
#[cfg(test)]
mod tests {
use {
crate::{
SchemaRead, SchemaWrite, ZeroCopy, deserialize, proptest_config::proptest_cfg,
serialize,
},
proptest::prelude::*,
zerocopy::byteorder::{BE, F64, I128, LE, U16, U64},
};
#[derive(SchemaWrite, SchemaRead, Debug, PartialEq)]
#[wincode(internal, assert_zero_copy)]
#[repr(C)]
struct Unaligned {
tag: u8,
amount: U64<LE>,
port: U16<BE>,
delta: I128<LE>,
}
#[test]
fn test_byteorder_roundtrip() {
proptest!(proptest_cfg(), |(a: u64, b: u16, c: i128, d: f64)| {
let value = (U64::<LE>::new(a), U16::<BE>::new(b), I128::<BE>::new(c), F64::<LE>::new(d));
let serialized = serialize(&value).unwrap();
prop_assert_eq!(serialized.len(), 8 + 2 + 16 + 8);
let deserialized: (U64<LE>, U16<BE>, I128<BE>, F64<LE>) = deserialize(&serialized).unwrap();
prop_assert_eq!(value.0, deserialized.0);
prop_assert_eq!(value.1, deserialized.1);
prop_assert_eq!(value.2, deserialized.2);
prop_assert_eq!(value.3.get().to_bits(), deserialized.3.get().to_bits());
});
}
#[test]
fn test_byteorder_is_its_bytes() {
proptest!(proptest_cfg(), |(value: u16)| {
prop_assert_eq!(serialize(&U16::<BE>::new(value)).unwrap(), value.to_be_bytes());
prop_assert_eq!(serialize(&U16::<LE>::new(value)).unwrap(), value.to_le_bytes());
});
}
#[cfg(target_endian = "little")]
#[test]
fn test_byteorder_le_matches_native() {
proptest!(proptest_cfg(), |(value: u64)| {
prop_assert_eq!(serialize(&U64::<LE>::new(value)).unwrap(), serialize(&value).unwrap());
});
}
#[test]
fn test_byteorder_struct_zero_copy_unaligned() {
proptest!(proptest_cfg(), |(tag: u8, amount: u64, port: u16, delta: i128)| {
let value = Unaligned {
tag,
amount: U64::new(amount),
port: U16::new(port),
delta: I128::new(delta),
};
let mut bytes = vec![0];
bytes.extend(serialize(&value).unwrap());
prop_assert_eq!(Unaligned::from_bytes(&bytes[1..]).unwrap(), &value);
let amount: &U64<LE> = deserialize(&bytes[2..]).unwrap();
prop_assert_eq!(amount.get(), value.amount.get());
});
}
}