use std::mem::MaybeUninit;
use super::InPlaceCodec;
use crate::errors::PrimitiveError;
macro_rules! impl_inplace_codec_le {
($($ty:ty),* $(,)?) => {$(
unsafe impl InPlaceCodec for $ty {
const ENCODED_SIZE: usize = std::mem::size_of::<$ty>();
fn write_le_bytes(&self, out: &mut [MaybeUninit<u8>]) {
for (slot, &b) in out.iter_mut().zip(self.to_le_bytes().iter()) {
slot.write(b);
}
}
fn read_le_bytes(bytes: &[u8]) -> Result<Self, PrimitiveError> {
let bytes = bytes.try_into().map_err(|_| {
PrimitiveError::InvalidSize(Self::ENCODED_SIZE, bytes.len())
})?;
Ok(<$ty>::from_le_bytes(bytes))
}
}
)*};
}
impl_inplace_codec_le!(u8, u16, u32, u64);
unsafe impl InPlaceCodec for bool {
const ENCODED_SIZE: usize = 1;
fn write_le_bytes(&self, out: &mut [MaybeUninit<u8>]) {
out[0].write(*self as u8);
}
fn read_le_bytes(bytes: &[u8]) -> Result<Self, PrimitiveError> {
match bytes[0] {
0 => Ok(false),
1 => Ok(true),
other => Err(PrimitiveError::DeserializationFailed(format!(
"non-canonical bool byte: {other}"
))),
}
}
}
unsafe impl InPlaceCodec for usize {
const ENCODED_SIZE: usize = 8;
fn write_le_bytes(&self, out: &mut [MaybeUninit<u8>]) {
for (slot, &b) in out.iter_mut().zip((*self as u64).to_le_bytes().iter()) {
slot.write(b);
}
}
fn read_le_bytes(bytes: &[u8]) -> Result<Self, PrimitiveError> {
let bytes = bytes
.try_into()
.map_err(|_| PrimitiveError::InvalidSize(Self::ENCODED_SIZE, bytes.len()))?;
let value = u64::from_le_bytes(bytes);
usize::try_from(value).map_err(|_| {
PrimitiveError::DeserializationFailed(format!("usize out of range: {value}"))
})
}
}
#[cfg(test)]
mod tests {
use super::InPlaceCodec;
fn roundtrip<T: InPlaceCodec + PartialEq + std::fmt::Debug>(v: T) {
let bytes = v.to_inplace_bytes();
assert_eq!(bytes.len(), T::ENCODED_SIZE);
assert_eq!(T::from_inplace_bytes(&bytes).unwrap(), v);
}
#[test]
fn prim_inplace_roundtrips() {
roundtrip(true);
roundtrip(false);
roundtrip(0xABu8);
roundtrip(0x1234u16);
roundtrip(0xDEAD_BEEFu32);
roundtrip(0x0123_4567_89AB_CDEFu64);
roundtrip(1234567usize);
}
#[test]
fn bool_rejects_non_canonical() {
assert!(bool::from_inplace_bytes(&[2]).is_err());
}
}