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 #[repr(transparent)] 29 pub struct $endian<T>(T); 30 31 impl<T> Copy for $endian<T> where T: Copy {} 32 33 impl<T> Clone for $endian<T> 34 where 35 T: Clone, 36 { 37 #[inline] 38 fn clone(&self) -> Self { 39 Self(self.0.clone()) 40 } 41 } 42 43 impl<T> core::ops::BitXorAssign for $endian<T> 44 where 45 T: core::ops::BitXorAssign, 46 { 47 #[inline(always)] 48 fn bitxor_assign(&mut self, a: Self) { 49 self.0 ^= a.0; 50 } 51 } 52 }; 53 } 54 55 macro_rules! impl_from_byte_array { 56 ($endian:ident, $base:ident, $elems:expr) => { 57 impl FromByteArray<[u8; $elems * core::mem::size_of::<$base>()]> 58 for [$endian<$base>; $elems] 59 { 60 #[inline] 61 fn from_byte_array(a: &[u8; $elems * core::mem::size_of::<$base>()]) -> Self { 62 unsafe { core::mem::transmute_copy(a) } 63 } 64 } 65 }; 66 } 67 68 macro_rules! impl_array_encoding { 69 ($endian:ident, $base:ident, $elems:expr) => { 70 impl ArrayEncoding<[u8; $elems * core::mem::size_of::<$base>()]> 71 for [$endian<$base>; $elems] 72 { 73 #[inline] 74 fn as_byte_array(&self) -> &[u8; $elems * core::mem::size_of::<$base>()] { 75 let as_bytes_ptr = 76 self.as_ptr() as *const [u8; $elems * core::mem::size_of::<$base>()]; 77 unsafe { &*as_bytes_ptr } 78 } 79 } 80 81 impl_from_byte_array!($endian, $base, $elems); 82 }; 83 } 84 85 macro_rules! impl_endian { 86 ($endian:ident, $base:ident, $to_endian:ident, $from_endian:ident, $size:expr) => { 87 impl Encoding<$base> for $endian<$base> { 88 const ZERO: Self = Self(0); 89 } 90 91 impl From<[u8; $size]> for $endian<$base> { 92 #[inline] 93 fn from(bytes: [u8; $size]) -> Self { 94 Self($base::from_ne_bytes(bytes)) 95 } 96 } 97 98 impl From<$endian<$base>> for [u8; $size] { 99 #[inline] 100 fn from(encoded: $endian<$base>) -> Self { 101 $base::to_ne_bytes(encoded.0) 102 } 103 } 104 105 impl From<$base> for $endian<$base> { 106 #[inline] 107 fn from(value: $base) -> Self { 108 Self($base::$to_endian(value)) 109 } 110 } 111 112 impl From<Wrapping<$base>> for $endian<$base> { 113 #[inline] 114 fn from(Wrapping(value): Wrapping<$base>) -> Self { 115 Self($base::$to_endian(value)) 116 } 117 } 118 119 impl From<$endian<$base>> for $base { 120 #[inline] 121 fn from($endian(value): $endian<$base>) -> Self { 122 $base::$from_endian(value) 123 } 124 } 125 126 impl_array_encoding!($endian, $base, 1); 127 impl_array_encoding!($endian, $base, 2); 128 impl_array_encoding!($endian, $base, 3); 129 impl_array_encoding!($endian, $base, 4); 130 impl_array_encoding!($endian, $base, 8); 131 }; 132 } 133 134 define_endian!(BigEndian); 135 define_endian!(LittleEndian); 136 impl_endian!(BigEndian, u32, to_be, from_be, 4); 137 impl_endian!(BigEndian, u64, to_be, from_be, 8); 138 impl_endian!(LittleEndian, u32, to_le, from_le, 4); 139 impl_endian!(LittleEndian, u64, to_le, from_le, 8); 140 141 #[cfg(test)] 142 mod tests { 143 use super::*; 144 145 #[test] test_big_endian()146 fn test_big_endian() { 147 let x = BigEndian::from(1u32); 148 assert_eq!(u32::from(x), 1); 149 } 150 } 151