1 /// A trait for working with structs as little-endian byte arrays. Automatically 2 /// implemented for all built-in signed/unsigned integers. 3 pub trait LeBytes: Sized { 4 /// Write the memory representation of `self` as a byte array in 5 /// little-endian byte order into the provided buffer. 6 #[allow(clippy::wrong_self_convention)] to_le_bytes(self, buf: &mut [u8]) -> Option<usize>7 fn to_le_bytes(self, buf: &mut [u8]) -> Option<usize>; 8 9 /// Parse `self` from a byte array in little-endian byte order. 10 /// Returns None upon overflow. from_le_bytes(buf: &[u8]) -> Option<Self>11 fn from_le_bytes(buf: &[u8]) -> Option<Self>; 12 } 13 14 macro_rules! impl_to_le_bytes { 15 ($($num:ty)*) => { 16 $( 17 impl LeBytes for $num { 18 fn to_le_bytes(self, buf: &mut [u8]) -> Option<usize> { 19 let len = core::mem::size_of::<$num>(); 20 if buf.len() < len { 21 return None 22 } 23 buf[..len].copy_from_slice(&<$num>::to_le_bytes(self)); 24 Some(len) 25 } 26 27 fn from_le_bytes(buf: &[u8]) -> Option<Self> { 28 let len = core::mem::size_of::<$num>(); 29 30 let buf = if buf.len() > len { 31 let (extra, buf) = buf.split_at(buf.len() - len); 32 if extra.iter().any(|&b| b != 0) { 33 return None 34 } 35 buf 36 } else { 37 buf 38 }; 39 40 let mut res: Self = 0; 41 for b in buf.iter().copied().rev() { 42 let b: Self = b as Self; 43 // `res <<= 8` causes the compiler to complain in the `u8` case 44 res <<= 4; 45 res <<= 4; 46 res |= b; 47 } 48 49 Some(res) 50 } 51 } 52 )* 53 }; 54 } 55 56 impl_to_le_bytes!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); 57 58 #[cfg(test)] 59 mod tests { 60 use super::*; 61 62 #[test] basic()63 fn basic() { 64 assert_eq!( 65 0x12345678, 66 LeBytes::from_le_bytes(&[0x78, 0x56, 0x34, 0x12]).unwrap() 67 ) 68 } 69 70 #[test] small()71 fn small() { 72 assert_eq!( 73 0x123456, 74 LeBytes::from_le_bytes(&[0x56, 0x34, 0x12]).unwrap() 75 ) 76 } 77 78 #[test] too_big()79 fn too_big() { 80 assert_eq!( 81 0x1234_u16, 82 LeBytes::from_le_bytes(&[0xde, 0xad, 0xbe, 0xef]).unwrap_or(0x1234) 83 ) 84 } 85 } 86