1 use core::num::Wrapping; 2 3 /// An `Encoding` of a type `T` can be converted to/from its byte 4 /// representation without any byte swapping or other computation. 5 /// 6 /// The `Self: Copy` constraint addresses `clippy::declare_interior_mutable_const`. 7 pub trait Encoding<T>: From<T> + Into<T> 8 where 9 Self: Copy, 10 { 11 const ZERO: Self; 12 } 13 14 /// Work around the inability to implement `AsRef` for arrays of `Encoding`s 15 /// due to the coherence rules. 16 pub trait ArrayEncoding<T> { as_byte_array(&self) -> &T17 fn as_byte_array(&self) -> &T; 18 } 19 20 /// Work around the inability to implement `from` for arrays of `Encoding`s 21 /// due to the coherence rules. 22 pub trait FromByteArray<T> { from_byte_array(a: &T) -> Self23 fn from_byte_array(a: &T) -> Self; 24 } 25 26 macro_rules! define_endian { 27 ($endian:ident) => { 28 #[derive(Clone, Copy)] 29 #[repr(transparent)] 30 pub struct $endian<T>(T); 31 32 impl<T> core::ops::BitXorAssign for $endian<T> 33 where 34 T: core::ops::BitXorAssign, 35 { 36 #[inline(always)] 37 fn bitxor_assign(&mut self, a: Self) { 38 self.0 ^= a.0; 39 } 40 } 41 }; 42 } 43 44 macro_rules! impl_from_byte_array { 45 ($endian:ident, $base:ident, $elems:expr) => { 46 impl FromByteArray<[u8; $elems * core::mem::size_of::<$base>()]> 47 for [$endian<$base>; $elems] 48 { 49 #[inline] 50 fn from_byte_array(a: &[u8; $elems * core::mem::size_of::<$base>()]) -> Self { 51 unsafe { core::mem::transmute_copy(a) } 52 } 53 } 54 }; 55 } 56 57 macro_rules! impl_array_encoding { 58 ($endian:ident, $base:ident, $elems:expr) => { 59 impl ArrayEncoding<[u8; $elems * core::mem::size_of::<$base>()]> 60 for [$endian<$base>; $elems] 61 { 62 #[inline] 63 fn as_byte_array(&self) -> &[u8; $elems * core::mem::size_of::<$base>()] { 64 let as_bytes_ptr = 65 self.as_ptr() as *const [u8; $elems * core::mem::size_of::<$base>()]; 66 unsafe { &*as_bytes_ptr } 67 } 68 } 69 70 impl_from_byte_array!($endian, $base, $elems); 71 }; 72 } 73 74 macro_rules! impl_endian { 75 ($endian:ident, $base:ident, $to_endian:ident, $from_endian:ident, $size:expr) => { 76 impl Encoding<$base> for $endian<$base> { 77 const ZERO: Self = Self(0); 78 } 79 80 impl From<[u8; $size]> for $endian<$base> { 81 #[inline] 82 fn from(bytes: [u8; $size]) -> Self { 83 Self($base::from_ne_bytes(bytes)) 84 } 85 } 86 87 impl From<$endian<$base>> for [u8; $size] { 88 #[inline] 89 fn from(encoded: $endian<$base>) -> Self { 90 $base::to_ne_bytes(encoded.0) 91 } 92 } 93 94 impl From<$base> for $endian<$base> { 95 #[inline] 96 fn from(value: $base) -> Self { 97 Self($base::$to_endian(value)) 98 } 99 } 100 101 impl From<Wrapping<$base>> for $endian<$base> { 102 #[inline] 103 fn from(Wrapping(value): Wrapping<$base>) -> Self { 104 Self($base::$to_endian(value)) 105 } 106 } 107 108 impl From<$endian<$base>> for $base { 109 #[inline] 110 fn from($endian(value): $endian<$base>) -> Self { 111 $base::$from_endian(value) 112 } 113 } 114 115 impl_array_encoding!($endian, $base, 1); 116 impl_array_encoding!($endian, $base, 2); 117 impl_array_encoding!($endian, $base, 3); 118 impl_array_encoding!($endian, $base, 4); 119 impl_array_encoding!($endian, $base, 8); 120 }; 121 } 122 123 define_endian!(BigEndian); 124 define_endian!(LittleEndian); 125 impl_endian!(BigEndian, u32, to_be, from_be, 4); 126 impl_endian!(BigEndian, u64, to_be, from_be, 8); 127 impl_endian!(LittleEndian, u32, to_le, from_le, 4); 128 impl_endian!(LittleEndian, u64, to_le, from_le, 8); 129 130 #[cfg(test)] 131 mod tests { 132 use super::*; 133 134 #[test] test_big_endian()135 fn test_big_endian() { 136 let x = BigEndian::from(1u32); 137 assert_eq!(u32::from(x), 1); 138 } 139 } 140