virtio_driver/util/
endian.rs

1// Copyright (C) 2022 Red Hat, Inc. All rights reserved.
2//
3// Copyright 2017 The Chromium OS Authors. All rights reserved.
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE.crosvm file.
6
7//! Explicit endian types useful for embedding in structs or reinterpreting data.
8//!
9//! Each endian type is guarnteed to have the same size and alignment as a regular unsigned primiive
10//! of the equal size.
11//!
12//! # Examples
13//!
14//! ```ignore
15//! # use  data_model::*;
16//!   let b: Be32 = From::from(3);
17//!   let l: Le32 = From::from(3);
18//!
19//!   assert_eq!(b.to_native(), 3);
20//!   assert_eq!(l.to_native(), 3);
21//!   assert!(b == 3);
22//!   assert!(l == 3);
23//!
24//!   let b_trans: u32 = unsafe { std::mem::transmute(b) };
25//!   let l_trans: u32 = unsafe { std::mem::transmute(l) };
26//!
27//!   #[cfg(target_endian = "little")]
28//!   assert_eq!(l_trans, 3);
29//!   #[cfg(target_endian = "big")]
30//!   assert_eq!(b_trans, 3);
31//!
32//!   assert_ne!(b_trans, l_trans);
33//! ```
34
35use std::mem::{align_of, size_of};
36
37// Copy only const_assert from
38// https://github.com/nvzqz/static-assertions-rs/blob/v1.1.0/src/const_assert.rs
39#[macro_export]
40macro_rules! const_assert {
41    ($x:expr $(,)?) => {
42        #[allow(unknown_lints, clippy::eq_op)]
43        const _: [(); 0 - !{
44            const ASSERT: bool = $x;
45            ASSERT
46        } as usize] = [];
47    };
48}
49
50use crate::ByteValued;
51
52macro_rules! endian_type {
53    ($old_type:ident, $new_type:ident, $to_new:ident, $from_new:ident) => {
54        /// An integer type of with an explicit endianness.
55        ///
56        /// See module level documentation for examples.
57        #[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
58        pub struct $new_type($old_type);
59
60        impl $new_type {
61            fn _assert() {
62                const_assert!(align_of::<$new_type>() == align_of::<$old_type>());
63                const_assert!(size_of::<$new_type>() == size_of::<$old_type>());
64            }
65
66            /// Converts `self` to the native endianness.
67            pub fn to_native(self) -> $old_type {
68                $old_type::$from_new(self.0)
69            }
70        }
71
72        unsafe impl ByteValued for $new_type {}
73
74        impl PartialEq<$old_type> for $new_type {
75            fn eq(&self, other: &$old_type) -> bool {
76                self.0 == $old_type::$to_new(*other)
77            }
78        }
79
80        impl PartialEq<$new_type> for $old_type {
81            fn eq(&self, other: &$new_type) -> bool {
82                $old_type::$to_new(other.0) == *self
83            }
84        }
85
86        impl From<$new_type> for $old_type {
87            fn from(v: $new_type) -> $old_type {
88                $old_type::$from_new(v.0)
89            }
90        }
91
92        impl From<$old_type> for $new_type {
93            fn from(v: $old_type) -> $new_type {
94                $new_type($old_type::$to_new(v))
95            }
96        }
97    };
98}
99
100endian_type!(u16, Le16, to_le, from_le);
101endian_type!(i16, SLe16, to_le, from_le);
102endian_type!(u32, Le32, to_le, from_le);
103endian_type!(i32, SLe32, to_le, from_le);
104endian_type!(u64, Le64, to_le, from_le);
105endian_type!(i64, SLe64, to_le, from_le);
106endian_type!(usize, LeSize, to_le, from_le);
107endian_type!(isize, SLeSize, to_le, from_le);
108endian_type!(u16, Be16, to_be, from_be);
109endian_type!(i16, SBe16, to_be, from_be);
110endian_type!(u32, Be32, to_be, from_be);
111endian_type!(i32, SBe32, to_be, from_be);
112endian_type!(u64, Be64, to_be, from_be);
113endian_type!(i64, SBe64, to_be, from_be);
114endian_type!(usize, BeSize, to_be, from_be);
115endian_type!(isize, SBeSize, to_be, from_be);
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    use std::convert::From;
122    use std::mem::transmute;
123
124    #[cfg(target_endian = "little")]
125    const NATIVE_LITTLE: bool = true;
126    #[cfg(target_endian = "big")]
127    const NATIVE_LITTLE: bool = false;
128    const NATIVE_BIG: bool = !NATIVE_LITTLE;
129
130    macro_rules! endian_test {
131        ($old_type:ty, $new_type:ty, $test_name:ident, $native:expr) => {
132            mod $test_name {
133                use super::*;
134
135                #[allow(overflowing_literals)]
136                #[test]
137                fn equality() {
138                    let v = 0x0123456789ABCDEF as $old_type;
139                    let endian_v: $new_type = From::from(v);
140                    let endian_into: $old_type = endian_v.into();
141                    let endian_transmute: $old_type = unsafe { transmute(endian_v) };
142
143                    if $native {
144                        assert_eq!(endian_v, endian_transmute);
145                    } else {
146                        assert_eq!(endian_v, endian_transmute.swap_bytes());
147                    }
148
149                    assert_eq!(v, endian_into);
150                    assert!(v == endian_v);
151                    assert!(endian_v == v);
152                }
153            }
154        };
155    }
156
157    endian_test!(u16, Le16, test_le16, NATIVE_LITTLE);
158    endian_test!(i16, SLe16, test_sle16, NATIVE_LITTLE);
159    endian_test!(u32, Le32, test_le32, NATIVE_LITTLE);
160    endian_test!(i32, SLe32, test_sle32, NATIVE_LITTLE);
161    endian_test!(u64, Le64, test_le64, NATIVE_LITTLE);
162    endian_test!(i64, SLe64, test_sle64, NATIVE_LITTLE);
163    endian_test!(usize, LeSize, test_le_size, NATIVE_LITTLE);
164    endian_test!(isize, SLeSize, test_sle_size, NATIVE_LITTLE);
165    endian_test!(u16, Be16, test_be16, NATIVE_BIG);
166    endian_test!(i16, SBe16, test_sbe16, NATIVE_BIG);
167    endian_test!(u32, Be32, test_be32, NATIVE_BIG);
168    endian_test!(i32, SBe32, test_sbe32, NATIVE_BIG);
169    endian_test!(u64, Be64, test_be64, NATIVE_BIG);
170    endian_test!(i64, SBe64, test_sbe64, NATIVE_BIG);
171    endian_test!(usize, BeSize, test_be_size, NATIVE_BIG);
172    endian_test!(isize, SBeSize, test_sbe_size, NATIVE_BIG);
173}