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