From d3213b15d8e16d7394042796a75d76b5ed4d2442 Mon Sep 17 00:00:00 2001 From: Nico Biernat Date: Sun, 15 Mar 2026 01:44:14 +0100 Subject: [PATCH 1/2] Suppress unused warning --- tests/size_test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/size_test.rs b/tests/size_test.rs index 2647445..ffbc5af 100644 --- a/tests/size_test.rs +++ b/tests/size_test.rs @@ -34,6 +34,7 @@ fn basic_structs() { } #[derive(Size)] + #[allow(unused)] struct Tuple(X, Y); #[derive(Size)] From 3f4d04ec96734bf1a1615d85e7e9c8e87b85f1c6 Mon Sep 17 00:00:00 2001 From: Nico Biernat Date: Sun, 15 Mar 2026 01:45:34 +0100 Subject: [PATCH 2/2] Use macros for implementing Pack, Unpack and Size for primitive types and tuples --- Cargo.toml | 1 + src/pack.rs | 128 ++++++++++++++++---------------------------------- src/size.rs | 101 +++++++++++++++++---------------------- src/unpack.rs | 126 +++++++++++++++++-------------------------------- 4 files changed, 127 insertions(+), 229 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1238e50..ec931cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ license = "MIT" [dependencies] lightpack-derive = { version = "0.2.9", path = "lightpack-derive" } byteorder = { workspace = true } +pastey = "0.2" [workspace] members = [ diff --git a/src/pack.rs b/src/pack.rs index b398059..dc54a03 100644 --- a/src/pack.rs +++ b/src/pack.rs @@ -1,6 +1,7 @@ //! The [`Pack`] trait and primitive implementations. use byteorder::ByteOrder; +use pastey::paste; use crate::Size; @@ -10,31 +11,24 @@ pub trait Pack: Size { fn pack(&self, buffer: &mut [u8]) where B: ByteOrder; } -// TODO: Abstract over this with a macro +macro_rules! impl_pack_number { + ($t:ty) => { + impl Pack for $t { + fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { + paste!{ B::[](buffer, *self) } + } + } + }; +} impl Pack for u8 { fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { buffer[0] = *self; } } - -impl Pack for u16 { - fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { - B::write_u16(buffer, *self); - } -} - -impl Pack for u32 { - fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { - B::write_u32(buffer, *self); - } -} - -impl Pack for u64 { - fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { - B::write_u64(buffer, *self); - } -} +impl_pack_number!(u16); +impl_pack_number!(u32); +impl_pack_number!(u64); impl Pack for i8 { fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { @@ -42,35 +36,12 @@ impl Pack for i8 { } } -impl Pack for i16 { - fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { - B::write_i16(buffer, *self); - } -} - -impl Pack for i32 { - fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { - B::write_i32(buffer, *self); - } -} - -impl Pack for i64 { - fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { - B::write_i64(buffer, *self); - } -} - -impl Pack for f32 { - fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { - B::write_f32(buffer, *self) - } -} +impl_pack_number!(i16); +impl_pack_number!(i32); +impl_pack_number!(i64); -impl Pack for f64 { - fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { - B::write_f64(buffer, *self) - } -} +impl_pack_number!(f32); +impl_pack_number!(f64); impl Pack for bool { fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { @@ -84,49 +55,30 @@ impl Pack for char { } } -// TODO: Abstract over the tuple size with a macro - -impl Pack for () { - fn pack(&self, _buffer: &mut [u8]) where B: ByteOrder { - // Do nothing - } -} - -impl Pack for (T0,) where T0: Pack { - fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { - self.0.pack::(buffer); - } -} - -impl Pack for (T0, T1) where T0: Pack, T1: Pack { - fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { - self.0.pack::(buffer); - let buffer = &mut buffer[T0::SIZE..]; - self.1.pack::(buffer); - } -} - -impl Pack for (T0, T1, T2) where T0: Pack, T1: Pack, T2: Pack { - fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { - self.0.pack::(buffer); - let buffer = &mut buffer[T0::SIZE..]; - self.1.pack::(buffer); - let buffer = &mut buffer[T1::SIZE..]; - self.2.pack::(buffer); - } +macro_rules! impl_pack_tuple { + ($($idx:tt),*) => { + paste! { + impl<$([],)*> Pack for ($([],)*) + where + $([]: Pack,)* + { + #[allow(unused)] // because we generate one more "let buffer = ..." statement than we need + fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { + $( + self.$idx.pack::(buffer); + let buffer = &mut buffer[[]::SIZE..]; + )* + } + } + } + }; } -impl Pack for (T0, T1, T2, T3) where T0: Pack, T1: Pack, T2: Pack, T3: Pack { - fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { - self.0.pack::(buffer); - let buffer = &mut buffer[T0::SIZE..]; - self.1.pack::(buffer); - let buffer = &mut buffer[T1::SIZE..]; - self.2.pack::(buffer); - let buffer = &mut buffer[T2::SIZE..]; - self.3.pack::(buffer); - } -} +impl_pack_tuple!(); +impl_pack_tuple!(0); +impl_pack_tuple!(0, 1); +impl_pack_tuple!(0, 1, 2); +impl_pack_tuple!(0, 1, 2, 3); impl Pack for &T where T: Pack { fn pack(&self, buffer: &mut [u8]) where B: ByteOrder { diff --git a/src/size.rs b/src/size.rs index 525ca9d..3151247 100644 --- a/src/size.rs +++ b/src/size.rs @@ -1,80 +1,67 @@ //! The [`Size`] trait and primitive implementations. +use core::mem::size_of; +use pastey::paste; + /// Types that have an encoded size. pub trait Size { /// The type's encoded size in bytes. const SIZE: usize; } -impl Size for u8 { - const SIZE: usize = 1; -} - -impl Size for u16 { - const SIZE: usize = 2; +macro_rules! impl_size_primitive { + ($t:ty) => { + impl Size for $t { + const SIZE: usize = size_of::<$t>(); + } + }; } -impl Size for u32 { - const SIZE: usize = 4; -} - -impl Size for u64 { - const SIZE: usize = 8; -} +impl_size_primitive!(u8); +impl_size_primitive!(u16); +impl_size_primitive!(u32); +impl_size_primitive!(u64); -impl Size for i8 { - const SIZE: usize = 1; -} +impl_size_primitive!(i8); +impl_size_primitive!(i16); +impl_size_primitive!(i32); +impl_size_primitive!(i64); -impl Size for i16 { - const SIZE: usize = 2; -} +impl_size_primitive!(f32); +impl_size_primitive!(f64); -impl Size for i32 { - const SIZE: usize = 4; -} +impl_size_primitive!(bool); -impl Size for i64 { - const SIZE: usize = 8; -} +impl_size_primitive!(char); -impl Size for f32 { - const SIZE: usize = 4; -} +impl_size_primitive!(()); -impl Size for f64 { - const SIZE: usize = 8; +macro_rules! sum { + ($e:expr, $($es:expr),+) => { + $e + sum!($($es),+) + }; + ($e:expr) => { + $e + } } -impl Size for bool { - const SIZE: usize = 1; +macro_rules! impl_size_tuple { + ($($idx:tt),+) => { + paste! { + impl<$([],)*> Size for ($([],)+) + where + $([]: Size,)+ + { + const SIZE: usize = sum!($( []::SIZE ),*); + } + } + }; } -impl Size for char { - const SIZE: usize = 4; -} - -impl Size for () { - const SIZE: usize = 0; -} - -// TODO: Abstract over the tuple size with a macro - -impl Size for (T1,) where T1: Size { - const SIZE: usize = T1::SIZE; -} - -impl Size for (T1, T2) where T1: Size, T2: Size { - const SIZE: usize = T1::SIZE + T2::SIZE; -} - -impl Size for (T1, T2, T3) where T1: Size, T2: Size, T3: Size { - const SIZE: usize = T1::SIZE + T2::SIZE + T3::SIZE; -} - -impl Size for (T1, T2, T3, T4) where T1: Size, T2: Size, T3: Size, T4: Size { - const SIZE: usize = T1::SIZE + T2::SIZE + T3::SIZE + T4::SIZE; -} +impl_size_tuple!(0); +impl_size_tuple!(0, 1); +impl_size_tuple!(0, 1, 2); +impl_size_tuple!(0, 1, 2, 3); impl Size for &T where T: Size { const SIZE: usize = T::SIZE; diff --git a/src/unpack.rs b/src/unpack.rs index 6fda525..0deb190 100644 --- a/src/unpack.rs +++ b/src/unpack.rs @@ -3,6 +3,7 @@ use core::mem::{MaybeUninit, self}; use byteorder::ByteOrder; +use pastey::paste; use crate::Size; @@ -42,29 +43,25 @@ pub trait Unpack: Size { } } -impl Unpack for u8 { - fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - Ok(buffer[0]) - } -} - -impl Unpack for u16 { - fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - Ok(B::read_u16(buffer)) - } +macro_rules! impl_unpack_number { + ($t:ty) => { + impl Unpack for $t { + fn unpack(buffer: &[u8]) -> Result where B: ByteOrder, Self: Sized { + paste!{ Ok(B::[](buffer)) } + } + } + }; } -impl Unpack for u32 { +impl Unpack for u8 { fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - Ok(B::read_u32(buffer)) + Ok(buffer[0]) } } -impl Unpack for u64 { - fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - Ok(B::read_u64(buffer)) - } -} +impl_unpack_number!(u16); +impl_unpack_number!(u32); +impl_unpack_number!(u64); impl Unpack for i8 { fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { @@ -72,35 +69,12 @@ impl Unpack for i8 { } } -impl Unpack for i16 { - fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - Ok(B::read_i16(buffer)) - } -} - -impl Unpack for i32 { - fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - Ok(B::read_i32(buffer)) - } -} - -impl Unpack for i64 { - fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - Ok(B::read_i64(buffer)) - } -} - -impl Unpack for f32 { - fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - Ok(B::read_f32(buffer)) - } -} +impl_unpack_number!(i16); +impl_unpack_number!(i32); +impl_unpack_number!(i64); -impl Unpack for f64 { - fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - Ok(B::read_f64(buffer)) - } -} +impl_unpack_number!(f32); +impl_unpack_number!(f64); impl Unpack for bool { fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { @@ -115,7 +89,25 @@ impl Unpack for char { } } -// TODO: Abstract over the tuple size with a macro +macro_rules! impl_unpack_tuple { + ($($idx:tt),*) => { + paste! { + impl<$([],)*> Unpack for ($([],)*) + where + $([]: Unpack,)* + { + #[allow(unused)] // because we generate one more "let buffer = ..." statement than we need + fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { + $( + let [] = []::unpack::(buffer)?; + let buffer = &buffer[[]::SIZE..]; + )* + Ok(($([],)*)) + } + } + } + }; +} impl Unpack for () { fn unpack(_buffer: &[u8]) -> Result where B: ByteOrder { @@ -123,44 +115,10 @@ impl Unpack for () { } } -impl Unpack for (T0,) where T0: Unpack { - fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - Ok((T0::unpack::(buffer)?,)) - } -} - -impl Unpack for (T0, T1) where T0: Unpack, T1: Unpack { - fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - let x0 = T0::unpack::(buffer)?; - let buffer = &buffer[T0::SIZE..]; - let x1 = T1::unpack::(buffer)?; - Ok((x0, x1)) - } -} - -impl Unpack for (T0, T1, T2) where T0: Unpack, T1: Unpack, T2: Unpack { - fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - let x0 = T0::unpack::(buffer)?; - let buffer = &buffer[T0::SIZE..]; - let x1 = T1::unpack::(buffer)?; - let buffer = &buffer[T1::SIZE..]; - let x2 = T2::unpack::(buffer)?; - Ok((x0, x1, x2)) - } -} - -impl Unpack for (T0, T1, T2, T3) where T0: Unpack, T1: Unpack, T2: Unpack, T3: Unpack { - fn unpack(buffer: &[u8]) -> Result where B: ByteOrder { - let x0 = T0::unpack::(buffer)?; - let buffer = &buffer[T0::SIZE..]; - let x1 = T1::unpack::(buffer)?; - let buffer = &buffer[T1::SIZE..]; - let x2 = T2::unpack::(buffer)?; - let buffer = &buffer[T2::SIZE..]; - let x3 = T3::unpack::(buffer)?; - Ok((x0, x1, x2, x3)) - } -} +impl_unpack_tuple!(0); +impl_unpack_tuple!(0, 1); +impl_unpack_tuple!(0, 1, 2); +impl_unpack_tuple!(0, 1, 2, 3); impl Unpack for Option where T: Unpack { fn unpack(buffer: &[u8]) -> Result where B: ByteOrder {