primitives/utils/codec/
prims.rs1use std::mem::MaybeUninit;
8
9use super::InPlaceCodec;
10use crate::errors::PrimitiveError;
11
12macro_rules! impl_inplace_codec_le {
13 ($($ty:ty),* $(,)?) => {$(
14 unsafe impl InPlaceCodec for $ty {
18 const ENCODED_SIZE: usize = std::mem::size_of::<$ty>();
19
20 fn write_le_bytes(&self, out: &mut [MaybeUninit<u8>]) {
21 for (slot, &b) in out.iter_mut().zip(self.to_le_bytes().iter()) {
22 slot.write(b);
23 }
24 }
25
26 fn read_le_bytes(bytes: &[u8]) -> Result<Self, PrimitiveError> {
27 let bytes = bytes.try_into().map_err(|_| {
28 PrimitiveError::InvalidSize(Self::ENCODED_SIZE, bytes.len())
29 })?;
30 Ok(<$ty>::from_le_bytes(bytes))
31 }
32 }
33 )*};
34}
35
36impl_inplace_codec_le!(u8, u16, u32, u64);
37
38unsafe impl InPlaceCodec for bool {
41 const ENCODED_SIZE: usize = 1;
42
43 fn write_le_bytes(&self, out: &mut [MaybeUninit<u8>]) {
44 out[0].write(*self as u8);
45 }
46
47 fn read_le_bytes(bytes: &[u8]) -> Result<Self, PrimitiveError> {
48 match bytes[0] {
49 0 => Ok(false),
50 1 => Ok(true),
51 other => Err(PrimitiveError::DeserializationFailed(format!(
52 "non-canonical bool byte: {other}"
53 ))),
54 }
55 }
56}
57
58unsafe impl InPlaceCodec for usize {
62 const ENCODED_SIZE: usize = 8;
63
64 fn write_le_bytes(&self, out: &mut [MaybeUninit<u8>]) {
65 for (slot, &b) in out.iter_mut().zip((*self as u64).to_le_bytes().iter()) {
66 slot.write(b);
67 }
68 }
69
70 fn read_le_bytes(bytes: &[u8]) -> Result<Self, PrimitiveError> {
71 let bytes = bytes
72 .try_into()
73 .map_err(|_| PrimitiveError::InvalidSize(Self::ENCODED_SIZE, bytes.len()))?;
74 let value = u64::from_le_bytes(bytes);
75 usize::try_from(value).map_err(|_| {
76 PrimitiveError::DeserializationFailed(format!("usize out of range: {value}"))
77 })
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::InPlaceCodec;
84
85 fn roundtrip<T: InPlaceCodec + PartialEq + std::fmt::Debug>(v: T) {
86 let bytes = v.to_inplace_bytes();
87 assert_eq!(bytes.len(), T::ENCODED_SIZE);
88 assert_eq!(T::from_inplace_bytes(&bytes).unwrap(), v);
89 }
90
91 #[test]
92 fn prim_inplace_roundtrips() {
93 roundtrip(true);
94 roundtrip(false);
95 roundtrip(0xABu8);
96 roundtrip(0x1234u16);
97 roundtrip(0xDEAD_BEEFu32);
98 roundtrip(0x0123_4567_89AB_CDEFu64);
99 roundtrip(1234567usize);
100 }
101
102 #[test]
103 fn bool_rejects_non_canonical() {
104 assert!(bool::from_inplace_bytes(&[2]).is_err());
105 }
106}