#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PackingError {
InvalidBufferSize,
}
pub trait Packable: Sized {
fn len() -> usize;
fn pack(self, buffer: &mut [u8]) -> Result<(), PackingError>;
fn unpack(data: &[u8]) -> Result<Self, PackingError>;
}
#[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<Self, PackingError> {
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<Self, PackingError> {
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);
}