| // Copyright 2017 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE-BSD-3-Clause file. |
| // |
| // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause |
| |
| //! Explicit endian types useful for embedding in structs or reinterpreting data. |
| //! |
| //! Each endian type is guaarnteed to have the same size and alignment as a regular unsigned |
| //! primitive of the equal size. |
| //! |
| //! # Examples |
| //! |
| //! ``` |
| //! # use vm_memory::{Be32, Le32}; |
| //! # |
| //! let b: Be32 = From::from(3); |
| //! let l: Le32 = From::from(3); |
| //! |
| //! assert_eq!(b.to_native(), 3); |
| //! assert_eq!(l.to_native(), 3); |
| //! assert!(b == 3); |
| //! assert!(l == 3); |
| //! |
| //! let b_trans: u32 = unsafe { std::mem::transmute(b) }; |
| //! let l_trans: u32 = unsafe { std::mem::transmute(l) }; |
| //! |
| //! #[cfg(target_endian = "little")] |
| //! assert_eq!(l_trans, 3); |
| //! #[cfg(target_endian = "big")] |
| //! assert_eq!(b_trans, 3); |
| //! |
| //! assert_ne!(b_trans, l_trans); |
| //! ``` |
| |
| use std::mem::{align_of, size_of}; |
| |
| use crate::bytes::ByteValued; |
| |
| macro_rules! const_assert { |
| ($condition:expr) => { |
| let _ = [(); 0 - !$condition as usize]; |
| }; |
| } |
| |
| macro_rules! endian_type { |
| ($old_type:ident, $new_type:ident, $to_new:ident, $from_new:ident) => { |
| /// An unsigned integer type of with an explicit endianness. |
| /// |
| /// See module level documentation for examples. |
| #[derive(Copy, Clone, Eq, PartialEq, Debug, Default)] |
| pub struct $new_type($old_type); |
| |
| impl $new_type { |
| fn _assert() { |
| const_assert!(align_of::<$new_type>() == align_of::<$old_type>()); |
| const_assert!(size_of::<$new_type>() == size_of::<$old_type>()); |
| } |
| |
| /// Converts `self` to the native endianness. |
| pub fn to_native(self) -> $old_type { |
| $old_type::$from_new(self.0) |
| } |
| } |
| |
| // SAFETY: Safe because we are using this for implementing ByteValued for endian types |
| // which are POD. |
| unsafe impl ByteValued for $new_type {} |
| |
| impl PartialEq<$old_type> for $new_type { |
| fn eq(&self, other: &$old_type) -> bool { |
| self.0 == $old_type::$to_new(*other) |
| } |
| } |
| |
| impl PartialEq<$new_type> for $old_type { |
| fn eq(&self, other: &$new_type) -> bool { |
| $old_type::$to_new(other.0) == *self |
| } |
| } |
| |
| impl From<$new_type> for $old_type { |
| fn from(v: $new_type) -> $old_type { |
| v.to_native() |
| } |
| } |
| |
| impl From<$old_type> for $new_type { |
| fn from(v: $old_type) -> $new_type { |
| $new_type($old_type::$to_new(v)) |
| } |
| } |
| }; |
| } |
| |
| endian_type!(u16, Le16, to_le, from_le); |
| endian_type!(u32, Le32, to_le, from_le); |
| endian_type!(u64, Le64, to_le, from_le); |
| endian_type!(usize, LeSize, to_le, from_le); |
| endian_type!(u16, Be16, to_be, from_be); |
| endian_type!(u32, Be32, to_be, from_be); |
| endian_type!(u64, Be64, to_be, from_be); |
| endian_type!(usize, BeSize, to_be, from_be); |
| |
| #[cfg(test)] |
| mod tests { |
| #![allow(clippy::undocumented_unsafe_blocks)] |
| use super::*; |
| |
| use std::convert::From; |
| use std::mem::transmute; |
| |
| #[cfg(target_endian = "little")] |
| const NATIVE_LITTLE: bool = true; |
| #[cfg(target_endian = "big")] |
| const NATIVE_LITTLE: bool = false; |
| const NATIVE_BIG: bool = !NATIVE_LITTLE; |
| |
| macro_rules! endian_test { |
| ($old_type:ty, $new_type:ty, $test_name:ident, $native:expr) => { |
| mod $test_name { |
| use super::*; |
| |
| #[allow(overflowing_literals)] |
| #[test] |
| fn test_endian_type() { |
| <$new_type>::_assert(); |
| |
| let v = 0x0123_4567_89AB_CDEF as $old_type; |
| let endian_v: $new_type = From::from(v); |
| let endian_into: $old_type = endian_v.into(); |
| let endian_transmute: $old_type = unsafe { transmute(endian_v) }; |
| |
| if $native { |
| assert_eq!(endian_v, endian_transmute); |
| } else { |
| assert_eq!(endian_v, endian_transmute.swap_bytes()); |
| } |
| |
| assert_eq!(endian_into, v); |
| assert_eq!(endian_v.to_native(), v); |
| |
| assert!(v == endian_v); |
| assert!(endian_v == v); |
| } |
| } |
| }; |
| } |
| |
| endian_test!(u16, Le16, test_le16, NATIVE_LITTLE); |
| endian_test!(u32, Le32, test_le32, NATIVE_LITTLE); |
| endian_test!(u64, Le64, test_le64, NATIVE_LITTLE); |
| endian_test!(usize, LeSize, test_le_size, NATIVE_LITTLE); |
| endian_test!(u16, Be16, test_be16, NATIVE_BIG); |
| endian_test!(u32, Be32, test_be32, NATIVE_BIG); |
| endian_test!(u64, Be64, test_be64, NATIVE_BIG); |
| endian_test!(usize, BeSize, test_be_size, NATIVE_BIG); |
| } |