• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use core::{convert::TryInto, 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 /// Allow access to a slice of  of `Encoding<T>` as a slice of bytes.
as_byte_slice<E: Encoding<T>, T>(x: &[E]) -> &[u8]15 pub fn as_byte_slice<E: Encoding<T>, T>(x: &[E]) -> &[u8] {
16     unsafe {
17         core::slice::from_raw_parts(x.as_ptr() as *const u8, x.len() * core::mem::size_of::<E>())
18     }
19 }
20 
21 /// Work around the inability to implement `AsRef` for arrays of `Encoding`s
22 /// due to the coherence rules.
23 pub trait ArrayEncoding<T> {
as_byte_array(&self) -> &T24     fn as_byte_array(&self) -> &T;
25 }
26 
27 /// Work around the inability to implement `from` for arrays of `Encoding`s
28 /// due to the coherence rules.
29 pub trait FromByteArray<T> {
from_byte_array(a: &T) -> Self30     fn from_byte_array(a: &T) -> Self;
31 }
32 
33 macro_rules! define_endian {
34     ($endian:ident) => {
35         #[repr(transparent)]
36         pub struct $endian<T>(T);
37 
38         impl<T> $endian<T> {
39             #[deprecated]
40             pub fn into_raw_value(self) -> T {
41                 self.0
42             }
43         }
44 
45         impl<T> Copy for $endian<T> where T: Copy {}
46 
47         impl<T> Clone for $endian<T>
48         where
49             T: Clone,
50         {
51             fn clone(&self) -> Self {
52                 Self(self.0.clone())
53             }
54         }
55     };
56 }
57 
58 macro_rules! impl_from_byte_array {
59     ($endian:ident, $base:ident, $elems:expr) => {
60         impl FromByteArray<[u8; $elems * core::mem::size_of::<$base>()]>
61             for [$endian<$base>; $elems]
62         {
63             fn from_byte_array(a: &[u8; $elems * core::mem::size_of::<$base>()]) -> Self {
64                 unsafe { core::mem::transmute_copy(a) }
65             }
66         }
67     };
68 }
69 
70 macro_rules! impl_array_encoding {
71     ($endian:ident, $base:ident, $elems:expr) => {
72         impl ArrayEncoding<[u8; $elems * core::mem::size_of::<$base>()]>
73             for [$endian<$base>; $elems]
74         {
75             fn as_byte_array(&self) -> &[u8; $elems * core::mem::size_of::<$base>()] {
76                 as_byte_slice(self).try_into().unwrap()
77             }
78         }
79 
80         impl_from_byte_array!($endian, $base, $elems);
81     };
82 }
83 
84 macro_rules! impl_endian {
85     ($endian:ident, $base:ident, $to_endian:ident, $from_endian:ident, $size:expr) => {
86         impl Encoding<$base> for $endian<$base> {
87             const ZERO: Self = Self(0);
88         }
89 
90         impl From<[u8; $size]> for $endian<$base> {
91             #[inline]
92             fn from(bytes: [u8; $size]) -> Self {
93                 Self($base::from_ne_bytes(bytes))
94             }
95         }
96 
97         impl From<$endian<$base>> for [u8; $size] {
98             #[inline]
99             fn from(encoded: $endian<$base>) -> Self {
100                 $base::to_ne_bytes(encoded.0)
101             }
102         }
103 
104         impl From<$base> for $endian<$base> {
105             #[inline]
106             fn from(value: $base) -> Self {
107                 Self($base::$to_endian(value))
108             }
109         }
110 
111         impl From<Wrapping<$base>> for $endian<$base> {
112             #[inline]
113             fn from(Wrapping(value): Wrapping<$base>) -> Self {
114                 Self($base::$to_endian(value))
115             }
116         }
117 
118         impl From<$endian<$base>> for $base {
119             #[inline]
120             fn from($endian(value): $endian<$base>) -> Self {
121                 $base::$from_endian(value)
122             }
123         }
124 
125         impl_array_encoding!($endian, $base, 1);
126         impl_array_encoding!($endian, $base, 2);
127         impl_array_encoding!($endian, $base, 3);
128         impl_array_encoding!($endian, $base, 4);
129         impl_from_byte_array!($endian, $base, 8);
130     };
131 }
132 
133 define_endian!(BigEndian);
134 define_endian!(LittleEndian);
135 impl_endian!(BigEndian, u32, to_be, from_be, 4);
136 impl_endian!(BigEndian, u64, to_be, from_be, 8);
137 impl_endian!(LittleEndian, u32, to_le, from_le, 4);
138 impl_endian!(LittleEndian, u64, to_le, from_le, 8);
139 
140 #[cfg(test)]
141 mod tests {
142     use super::*;
143 
144     #[test]
test_big_endian()145     fn test_big_endian() {
146         let x = BigEndian::from(1u32);
147         assert_eq!(u32::from(x), 1);
148     }
149 }
150