1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum PackingError {
10 InvalidBufferSize,
13}
14
15pub trait Packable: Sized {
17 fn len() -> usize;
20
21 fn pack(self, buffer: &mut [u8]) -> Result<(), PackingError>;
27
28 fn unpack(data: &[u8]) -> Result<Self, PackingError>;
30}
31
32#[cfg(feature = "little-endian")]
33macro_rules! packable_primitive {
34 ($primitive_name: ident, $length: literal) => {
35 impl Packable for $primitive_name {
36 fn len() -> usize {
37 $length as usize
38 }
39
40 fn pack(self, buffer: &mut [u8]) -> Result<(), PackingError> {
41 if buffer.len() < Self::len() {
42 return Err(PackingError::InvalidBufferSize);
43 }
44
45 buffer[..Self::len()].copy_from_slice(&self.to_le_bytes()[..]);
46 Ok(())
47 }
48
49 fn unpack(data: &[u8]) -> Result<Self, PackingError> {
50 if data.len() < Self::len() {
51 return Err(PackingError::InvalidBufferSize);
52 }
53
54 Ok(Self::from_le_bytes(data[..Self::len()].try_into().unwrap()))
55 }
56 }
57 };
58}
59
60#[cfg(not(feature = "little-endian"))]
61macro_rules! packable_primitive {
62 ($primitive_name: ident, $length: literal) => {
63 impl Packable for $primitive_name {
64 fn len() -> usize {
65 $length as usize
66 }
67
68 fn pack(self, buffer: &mut [u8]) -> Result<(), PackingError> {
69 if buffer.len() < Self::len() {
70 return Err(PackingError::InvalidBufferSize);
71 }
72
73 buffer[..Self::len()].copy_from_slice(&self.to_be_bytes()[..]);
74 Ok(())
75 }
76
77 fn unpack(data: &[u8]) -> Result<Self, PackingError> {
78 if data.len() < Self::len() {
79 return Err(PackingError::InvalidBufferSize);
80 }
81
82 Ok(Self::from_be_bytes(data[..Self::len()].try_into().unwrap()))
83 }
84 }
85 };
86}
87
88packable_primitive!(u8, 1);
89packable_primitive!(u16, 2);
90packable_primitive!(u32, 4);
91packable_primitive!(u64, 8);
92packable_primitive!(u128, 16);
93packable_primitive!(usize, 8);
94packable_primitive!(i8, 1);
95packable_primitive!(i16, 2);
96packable_primitive!(i32, 4);
97packable_primitive!(i64, 8);
98packable_primitive!(i128, 16);
99packable_primitive!(isize, 8);
100packable_primitive!(f32, 4);
101packable_primitive!(f64, 8);
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 macro_rules! test_primitive_packing {
108 ($primitive: ident, $buffer_length: literal, $value: literal, $test_name: ident) => {
109 #[test]
110 fn $test_name() {
111 let mut buffer = [0u8; $buffer_length as usize];
112 assert!($value.pack(&mut buffer).is_ok());
113 println!("buffer: {:?}", buffer);
114 assert_eq!($value, $primitive::unpack(&buffer).unwrap());
115 }
116 };
117 }
118
119 test_primitive_packing!(u8, 1, 129u8, test_u8_packing);
120 test_primitive_packing!(u16, 2, 129u16, test_u16_packing);
121 test_primitive_packing!(u32, 4, 129u32, test_u32_packing);
122 test_primitive_packing!(u64, 8, 129u64, test_u64_packing);
123 test_primitive_packing!(u128, 16, 129u128, test_u128_packing);
124 test_primitive_packing!(usize, 16, 129usize, test_usize_packing);
125 test_primitive_packing!(i8, 1, 15i8, test_i8_packing);
126 test_primitive_packing!(i16, 2, 129i16, test_i16_packing);
127 test_primitive_packing!(i32, 4, 129i32, test_i32_packing);
128 test_primitive_packing!(i64, 8, 129i64, test_i64_packing);
129 test_primitive_packing!(i128, 16, 129i128, test_i128_packing);
130 test_primitive_packing!(isize, 16, 129isize, test_isize_packing);
131 test_primitive_packing!(f32, 4, 2.01f32, test_f32_packing);
132 test_primitive_packing!(f64, 8, 2.01f64, test_f64_packing);
133}