From eefd214309fc3f8a0b5415184e833948bd5bbb1e Mon Sep 17 00:00:00 2001 From: N8BWert Date: Sat, 5 Oct 2024 18:26:01 -0400 Subject: [PATCH] generate Packable implementations for primitive numbers + default to big-endian packing --- ncomm-utils/Cargo.toml | 3 +- ncomm-utils/src/packing.rs | 103 +++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/ncomm-utils/Cargo.toml b/ncomm-utils/Cargo.toml index 13b4f5b..da4d08b 100644 --- a/ncomm-utils/Cargo.toml +++ b/ncomm-utils/Cargo.toml @@ -16,4 +16,5 @@ repository.workspace = true default = ["std"] nostd = [] alloc = [] -std = [] \ No newline at end of file +std = [] +little-endian = [] \ No newline at end of file diff --git a/ncomm-utils/src/packing.rs b/ncomm-utils/src/packing.rs index 0db37f3..12babf2 100644 --- a/ncomm-utils/src/packing.rs +++ b/ncomm-utils/src/packing.rs @@ -28,3 +28,106 @@ pub trait Packable: Sized { /// Unpack a given piece of data from an array of bytes fn unpack(data: &[u8]) -> Result; } + +#[cfg(feature = "little-endian")] +macro_rules! packable_primitive { + ($primitive_name: ident, $length: literal) => { + impl Packable for $primitive_name { + fn len() -> usize { + $length as usize + } + + fn pack(self, buffer: &mut [u8]) -> Result<(), PackingError> { + if buffer.len() < Self::len() { + return Err(PackingError::InvalidBufferSize); + } + + buffer[..Self::len()].copy_from_slice(&self.to_le_bytes()[..]); + Ok(()) + } + + fn unpack(data: &[u8]) -> Result { + if data.len() < Self::len() { + return Err(PackingError::InvalidBufferSize); + } + + Ok(Self::from_le_bytes(data[..Self::len()].try_into().unwrap())) + } + } + }; +} + +#[cfg(not(feature = "little-endian"))] +macro_rules! packable_primitive { + ($primitive_name: ident, $length: literal) => { + impl Packable for $primitive_name { + fn len() -> usize { + $length as usize + } + + fn pack(self, buffer: &mut [u8]) -> Result<(), PackingError> { + if buffer.len() < Self::len() { + return Err(PackingError::InvalidBufferSize); + } + + buffer[..Self::len()].copy_from_slice(&self.to_be_bytes()[..]); + Ok(()) + } + + fn unpack(data: &[u8]) -> Result { + if data.len() < Self::len() { + return Err(PackingError::InvalidBufferSize); + } + + Ok(Self::from_be_bytes(data[..Self::len()].try_into().unwrap())) + } + } + }; +} + +packable_primitive!(u8, 1); +packable_primitive!(u16, 2); +packable_primitive!(u32, 4); +packable_primitive!(u64, 8); +packable_primitive!(u128, 16); +packable_primitive!(usize, 8); +packable_primitive!(i8, 1); +packable_primitive!(i16, 2); +packable_primitive!(i32, 4); +packable_primitive!(i64, 8); +packable_primitive!(i128, 16); +packable_primitive!(isize, 8); +packable_primitive!(f32, 4); +packable_primitive!(f64, 8); + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! test_primitive_packing { + ($primitive: ident, $buffer_length: literal, $value: literal, $test_name: ident) => { + #[test] + fn $test_name() { + let mut buffer = [0u8; $buffer_length as usize]; + assert!($value.pack(&mut buffer).is_ok()); + println!("buffer: {:?}", buffer); + assert_eq!($value, $primitive::unpack(&buffer).unwrap()); + } + }; + } + + test_primitive_packing!(u8, 1, 129u8, test_u8_packing); + test_primitive_packing!(u16, 2, 129u16, test_u16_packing); + test_primitive_packing!(u32, 4, 129u32, test_u32_packing); + test_primitive_packing!(u64, 8, 129u64, test_u64_packing); + test_primitive_packing!(u128, 16, 129u128, test_u128_packing); + test_primitive_packing!(usize, 16, 129usize, test_usize_packing); + test_primitive_packing!(i8, 1, 15i8, test_i8_packing); + test_primitive_packing!(i16, 2, 129i16, test_i16_packing); + test_primitive_packing!(i32, 4, 129i32, test_i32_packing); + test_primitive_packing!(i64, 8, 129i64, test_i64_packing); + test_primitive_packing!(i128, 16, 129i128, test_i128_packing); + test_primitive_packing!(isize, 16, 129isize, test_isize_packing); + test_primitive_packing!(f32, 4, 2.01f32, test_f32_packing); + test_primitive_packing!(f64, 8, 2.01f64, test_f64_packing); +} \ No newline at end of file