vm_memory/
endian.rs

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
36use std::mem::{align_of, size_of};
37
38use crate::bytes::ByteValued;
39
40macro_rules! const_assert {
41    ($condition:expr) => {
42        let _ = [(); 0 - !$condition as usize];
43    };
44}
45
46macro_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        #[repr(transparent)]
53        pub struct $new_type($old_type);
54
55        impl $new_type {
56            fn _assert() {
57                const_assert!(align_of::<$new_type>() == align_of::<$old_type>());
58                const_assert!(size_of::<$new_type>() == size_of::<$old_type>());
59            }
60
61            /// Converts `self` to the native endianness.
62            pub fn to_native(self) -> $old_type {
63                $old_type::$from_new(self.0)
64            }
65        }
66
67        // SAFETY: Safe because we are using this for implementing ByteValued for endian types
68        // which are POD.
69        unsafe impl ByteValued for $new_type {}
70
71        impl PartialEq<$old_type> for $new_type {
72            fn eq(&self, other: &$old_type) -> bool {
73                self.0 == $old_type::$to_new(*other)
74            }
75        }
76
77        impl PartialEq<$new_type> for $old_type {
78            fn eq(&self, other: &$new_type) -> bool {
79                $old_type::$to_new(other.0) == *self
80            }
81        }
82
83        impl From<$new_type> for $old_type {
84            fn from(v: $new_type) -> $old_type {
85                v.to_native()
86            }
87        }
88
89        impl From<$old_type> for $new_type {
90            fn from(v: $old_type) -> $new_type {
91                $new_type($old_type::$to_new(v))
92            }
93        }
94    };
95}
96
97endian_type!(u16, Le16, to_le, from_le);
98endian_type!(u32, Le32, to_le, from_le);
99endian_type!(u64, Le64, to_le, from_le);
100endian_type!(usize, LeSize, to_le, from_le);
101endian_type!(u16, Be16, to_be, from_be);
102endian_type!(u32, Be32, to_be, from_be);
103endian_type!(u64, Be64, to_be, from_be);
104endian_type!(usize, BeSize, to_be, from_be);
105
106#[cfg(test)]
107mod tests {
108    #![allow(clippy::undocumented_unsafe_blocks)]
109    use super::*;
110
111    use std::convert::From;
112    use std::mem::transmute;
113
114    #[cfg(target_endian = "little")]
115    const NATIVE_LITTLE: bool = true;
116    #[cfg(target_endian = "big")]
117    const NATIVE_LITTLE: bool = false;
118    const NATIVE_BIG: bool = !NATIVE_LITTLE;
119
120    macro_rules! endian_test {
121        ($old_type:ty, $new_type:ty, $test_name:ident, $native:expr) => {
122            mod $test_name {
123                use super::*;
124
125                #[allow(overflowing_literals)]
126                #[test]
127                fn test_endian_type() {
128                    <$new_type>::_assert();
129
130                    let v = 0x0123_4567_89AB_CDEF as $old_type;
131                    let endian_v: $new_type = From::from(v);
132                    let endian_into: $old_type = endian_v.into();
133                    let endian_transmute: $old_type = unsafe { transmute(endian_v) };
134
135                    if $native {
136                        assert_eq!(endian_v, endian_transmute);
137                    } else {
138                        assert_eq!(endian_v, endian_transmute.swap_bytes());
139                    }
140
141                    assert_eq!(endian_into, v);
142                    assert_eq!(endian_v.to_native(), v);
143
144                    assert!(v == endian_v);
145                    assert!(endian_v == v);
146                }
147            }
148        };
149    }
150
151    endian_test!(u16, Le16, test_le16, NATIVE_LITTLE);
152    endian_test!(u32, Le32, test_le32, NATIVE_LITTLE);
153    endian_test!(u64, Le64, test_le64, NATIVE_LITTLE);
154    endian_test!(usize, LeSize, test_le_size, NATIVE_LITTLE);
155    endian_test!(u16, Be16, test_be16, NATIVE_BIG);
156    endian_test!(u32, Be32, test_be32, NATIVE_BIG);
157    endian_test!(u64, Be64, test_be64, NATIVE_BIG);
158    endian_test!(usize, BeSize, test_be_size, NATIVE_BIG);
159}