ncomm_utils/
packing.rs

1//!
2//! Utility Packing and Unpacking Methods Necessary for Data
3//! Sent over Some Network.
4//!
5
6/// An error from attempting to pack data into a buffer or from
7/// attempting to unpack data from a slice.
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum PackingError {
10    /// The buffer to pack or unpack data from cannot be used as
11    /// the data will not fit in the buffer.
12    InvalidBufferSize,
13}
14
15/// Trait implemented by data to be sent over network boundaries.
16pub trait Packable: Sized {
17    /// Get the minimum necessary length of a buffer to pack this data
18    /// into.
19    fn len() -> usize;
20
21    /// Pack a given piece of data into a given buffer.
22    ///
23    /// Note: this format of pack was utilized to make this trait
24    /// compatible with both std and no_std targets (specifically
25    /// for no_std targets without `alloc`)
26    fn pack(self, buffer: &mut [u8]) -> Result<(), PackingError>;
27
28    /// Unpack a given piece of data from an array of bytes
29    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}