1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE-BSD-3-Clause file.
4 //
5 // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
6 
7 //! Explicit endian types useful for embedding in structs or reinterpreting data.
8 //!
9 //! Each endian type is guaarnteed to have the same size and alignment as a regular unsigned
10 //! primitive of the equal size.
11 //!
12 //! # Examples
13 //!
14 //! ```
15 //! # use vm_memory::{Be32, Le32};
16 //! #
17 //! let b: Be32 = From::from(3);
18 //! let l: Le32 = From::from(3);
19 //!
20 //! assert_eq!(b.to_native(), 3);
21 //! assert_eq!(l.to_native(), 3);
22 //! assert!(b == 3);
23 //! assert!(l == 3);
24 //!
25 //! let b_trans: u32 = unsafe { std::mem::transmute(b) };
26 //! let l_trans: u32 = unsafe { std::mem::transmute(l) };
27 //!
28 //! #[cfg(target_endian = "little")]
29 //! assert_eq!(l_trans, 3);
30 //! #[cfg(target_endian = "big")]
31 //! assert_eq!(b_trans, 3);
32 //!
33 //! assert_ne!(b_trans, l_trans);
34 //! ```
35 
36 use std::mem::{align_of, size_of};
37 
38 use crate::bytes::ByteValued;
39 
40 macro_rules! const_assert {
41     ($condition:expr) => {
42         let _ = [(); 0 - !$condition as usize];
43     };
44 }
45 
46 macro_rules! endian_type {
47     ($old_type:ident, $new_type:ident, $to_new:ident, $from_new:ident) => {
48         /// An unsigned integer type of with an explicit endianness.
49         ///
50         /// See module level documentation for examples.
51         #[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
52         pub struct $new_type($old_type);
53 
54         impl $new_type {
55             fn _assert() {
56                 const_assert!(align_of::<$new_type>() == align_of::<$old_type>());
57                 const_assert!(size_of::<$new_type>() == size_of::<$old_type>());
58             }
59 
60             /// Converts `self` to the native endianness.
61             pub fn to_native(self) -> $old_type {
62                 $old_type::$from_new(self.0)
63             }
64         }
65 
66         // SAFETY: Safe because we are using this for implementing ByteValued for endian types
67         // which are POD.
68         unsafe impl ByteValued for $new_type {}
69 
70         impl PartialEq<$old_type> for $new_type {
71             fn eq(&self, other: &$old_type) -> bool {
72                 self.0 == $old_type::$to_new(*other)
73             }
74         }
75 
76         impl PartialEq<$new_type> for $old_type {
77             fn eq(&self, other: &$new_type) -> bool {
78                 $old_type::$to_new(other.0) == *self
79             }
80         }
81 
82         impl From<$new_type> for $old_type {
83             fn from(v: $new_type) -> $old_type {
84                 v.to_native()
85             }
86         }
87 
88         impl From<$old_type> for $new_type {
89             fn from(v: $old_type) -> $new_type {
90                 $new_type($old_type::$to_new(v))
91             }
92         }
93     };
94 }
95 
96 endian_type!(u16, Le16, to_le, from_le);
97 endian_type!(u32, Le32, to_le, from_le);
98 endian_type!(u64, Le64, to_le, from_le);
99 endian_type!(usize, LeSize, to_le, from_le);
100 endian_type!(u16, Be16, to_be, from_be);
101 endian_type!(u32, Be32, to_be, from_be);
102 endian_type!(u64, Be64, to_be, from_be);
103 endian_type!(usize, BeSize, to_be, from_be);
104 
105 #[cfg(test)]
106 mod tests {
107     #![allow(clippy::undocumented_unsafe_blocks)]
108     use super::*;
109 
110     use std::convert::From;
111     use std::mem::transmute;
112 
113     #[cfg(target_endian = "little")]
114     const NATIVE_LITTLE: bool = true;
115     #[cfg(target_endian = "big")]
116     const NATIVE_LITTLE: bool = false;
117     const NATIVE_BIG: bool = !NATIVE_LITTLE;
118 
119     macro_rules! endian_test {
120         ($old_type:ty, $new_type:ty, $test_name:ident, $native:expr) => {
121             mod $test_name {
122                 use super::*;
123 
124                 #[allow(overflowing_literals)]
125                 #[test]
126                 fn test_endian_type() {
127                     <$new_type>::_assert();
128 
129                     let v = 0x0123_4567_89AB_CDEF as $old_type;
130                     let endian_v: $new_type = From::from(v);
131                     let endian_into: $old_type = endian_v.into();
132                     let endian_transmute: $old_type = unsafe { transmute(endian_v) };
133 
134                     if $native {
135                         assert_eq!(endian_v, endian_transmute);
136                     } else {
137                         assert_eq!(endian_v, endian_transmute.swap_bytes());
138                     }
139 
140                     assert_eq!(endian_into, v);
141                     assert_eq!(endian_v.to_native(), v);
142 
143                     assert!(v == endian_v);
144                     assert!(endian_v == v);
145                 }
146             }
147         };
148     }
149 
150     endian_test!(u16, Le16, test_le16, NATIVE_LITTLE);
151     endian_test!(u32, Le32, test_le32, NATIVE_LITTLE);
152     endian_test!(u64, Le64, test_le64, NATIVE_LITTLE);
153     endian_test!(usize, LeSize, test_le_size, NATIVE_LITTLE);
154     endian_test!(u16, Be16, test_be16, NATIVE_BIG);
155     endian_test!(u32, Be32, test_be32, NATIVE_BIG);
156     endian_test!(u64, Be64, test_be64, NATIVE_BIG);
157     endian_test!(usize, BeSize, test_be_size, NATIVE_BIG);
158 }
159