• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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