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