1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
use crate::Endian; pub trait PackedSize where Self: Sized { /// Number of bytes this struct packs to/from const BYTES: usize; } /// Trait that enables endian aware conversion to/from bytes for packable types /// /// Mostly for primitives. Currently expected to accept/return arrays by proc macro. /// Benchmarking showed allocating a new 1 to 8 byte array during to/from bytes /// performed exactly the same as manual bit shifting or various other shenanigans. /// /// TODO: Above perf statement likely only holds true up to a certain size. Currently /// nested structs and packing/unpacking them to/from &mut [u8] is not supported. pub trait PackedBytes<B>: PackedSize { type Error; fn to_bytes<En: Endian>(&self) -> Result<B, Self::Error>; fn from_bytes<En: Endian>(bytes: B) -> Result<Self, Self::Error>; } /// Trait that enables packing and unpacking to/from byte slices /// /// This is the trait that the proc macro implements on structs. Supports arbitrary /// field alignment - i.e. fields needn't start or end on 8-bit byte boundaries. /// For example, 2 bools, a 10-bit number and a 4-bit number could be packed into /// a pair of bytes such that the 10-bit number straddles them and doesn't align with /// any byte boundaries /// /// Allowing arbitray field alignment should be zero-cost as the field definitions /// will all be constants. pub trait Packed: PackedSize { type Error; fn pack(&self, bytes: &mut [u8]) -> Result<(), Self::Error>; fn unpack(bytes: &[u8]) -> Result<Self, Self::Error>; }