• 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 /// 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                 // TODO: When we can require Rust 1.47.0 or later we could avoid
77                 // `as` and `unsafe` here using
78                 // `as_byte_slice(self).try_into().unwrap()`.
79                 let as_bytes_ptr =
80                     self.as_ptr() as *const [u8; $elems * core::mem::size_of::<$base>()];
81                 unsafe { &*as_bytes_ptr }
82             }
83         }
84 
85         impl_from_byte_array!($endian, $base, $elems);
86     };
87 }
88 
89 macro_rules! impl_endian {
90     ($endian:ident, $base:ident, $to_endian:ident, $from_endian:ident, $size:expr) => {
91         impl Encoding<$base> for $endian<$base> {
92             const ZERO: Self = Self(0);
93         }
94 
95         impl From<[u8; $size]> for $endian<$base> {
96             #[inline]
97             fn from(bytes: [u8; $size]) -> Self {
98                 Self($base::from_ne_bytes(bytes))
99             }
100         }
101 
102         impl From<$endian<$base>> for [u8; $size] {
103             #[inline]
104             fn from(encoded: $endian<$base>) -> Self {
105                 $base::to_ne_bytes(encoded.0)
106             }
107         }
108 
109         impl From<$base> for $endian<$base> {
110             #[inline]
111             fn from(value: $base) -> Self {
112                 Self($base::$to_endian(value))
113             }
114         }
115 
116         impl From<Wrapping<$base>> for $endian<$base> {
117             #[inline]
118             fn from(Wrapping(value): Wrapping<$base>) -> Self {
119                 Self($base::$to_endian(value))
120             }
121         }
122 
123         impl From<$endian<$base>> for $base {
124             #[inline]
125             fn from($endian(value): $endian<$base>) -> Self {
126                 $base::$from_endian(value)
127             }
128         }
129 
130         impl_array_encoding!($endian, $base, 1);
131         impl_array_encoding!($endian, $base, 2);
132         impl_array_encoding!($endian, $base, 3);
133         impl_array_encoding!($endian, $base, 4);
134         impl_array_encoding!($endian, $base, 8);
135     };
136 }
137 
138 define_endian!(BigEndian);
139 define_endian!(LittleEndian);
140 impl_endian!(BigEndian, u32, to_be, from_be, 4);
141 impl_endian!(BigEndian, u64, to_be, from_be, 8);
142 impl_endian!(LittleEndian, u32, to_le, from_le, 4);
143 impl_endian!(LittleEndian, u64, to_le, from_le, 8);
144 
145 #[cfg(test)]
146 mod tests {
147     use super::*;
148 
149     #[test]
test_big_endian()150     fn test_big_endian() {
151         let x = BigEndian::from(1u32);
152         assert_eq!(u32::from(x), 1);
153     }
154 }
155