1 /*
2 * Copyright 2018 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #![allow(clippy::wrong_self_convention)]
17
18 use core::mem::size_of;
19
20 /// Trait for values that must be stored in little-endian byte order, but
21 /// might be represented in memory as big-endian. Every type that implements
22 /// EndianScalar is a valid FlatBuffers scalar value.
23 ///
24 /// The Rust stdlib does not provide a trait to represent scalars, so this trait
25 /// serves that purpose, too.
26 ///
27 /// Note that we do not use the num-traits crate for this, because it provides
28 /// "too much". For example, num-traits provides i128 support, but that is an
29 /// invalid FlatBuffers type.
30 pub trait EndianScalar: Sized + PartialEq + Copy + Clone {
to_little_endian(self) -> Self31 fn to_little_endian(self) -> Self;
from_little_endian(self) -> Self32 fn from_little_endian(self) -> Self;
33 }
34
35 /// Macro for implementing a no-op endian conversion. This is used for types
36 /// that are one byte wide.
37 macro_rules! impl_endian_scalar_noop {
38 ($ty:ident) => {
39 impl EndianScalar for $ty {
40 #[inline]
41 fn to_little_endian(self) -> Self {
42 self
43 }
44 #[inline]
45 fn from_little_endian(self) -> Self {
46 self
47 }
48 }
49 };
50 }
51
52 /// Macro for implementing an endian conversion using the stdlib `to_le` and
53 /// `from_le` functions. This is used for integer types. It is not used for
54 /// floats, because the `to_le` and `from_le` are not implemented for them in
55 /// the stdlib.
56 macro_rules! impl_endian_scalar_stdlib_le_conversion {
57 ($ty:ident) => {
58 impl EndianScalar for $ty {
59 #[inline]
60 fn to_little_endian(self) -> Self {
61 Self::to_le(self)
62 }
63 #[inline]
64 fn from_little_endian(self) -> Self {
65 Self::from_le(self)
66 }
67 }
68 };
69 }
70
71 impl_endian_scalar_noop!(bool);
72 impl_endian_scalar_noop!(u8);
73 impl_endian_scalar_noop!(i8);
74
75 impl_endian_scalar_stdlib_le_conversion!(u16);
76 impl_endian_scalar_stdlib_le_conversion!(u32);
77 impl_endian_scalar_stdlib_le_conversion!(u64);
78 impl_endian_scalar_stdlib_le_conversion!(i16);
79 impl_endian_scalar_stdlib_le_conversion!(i32);
80 impl_endian_scalar_stdlib_le_conversion!(i64);
81
82 impl EndianScalar for f32 {
83 /// Convert f32 from host endian-ness to little-endian.
84 #[inline]
to_little_endian(self) -> Self85 fn to_little_endian(self) -> Self {
86 #[cfg(target_endian = "little")]
87 {
88 self
89 }
90 #[cfg(not(target_endian = "little"))]
91 {
92 byte_swap_f32(self)
93 }
94 }
95 /// Convert f32 from little-endian to host endian-ness.
96 #[inline]
from_little_endian(self) -> Self97 fn from_little_endian(self) -> Self {
98 #[cfg(target_endian = "little")]
99 {
100 self
101 }
102 #[cfg(not(target_endian = "little"))]
103 {
104 byte_swap_f32(self)
105 }
106 }
107 }
108
109 impl EndianScalar for f64 {
110 /// Convert f64 from host endian-ness to little-endian.
111 #[inline]
to_little_endian(self) -> Self112 fn to_little_endian(self) -> Self {
113 #[cfg(target_endian = "little")]
114 {
115 self
116 }
117 #[cfg(not(target_endian = "little"))]
118 {
119 byte_swap_f64(self)
120 }
121 }
122 /// Convert f64 from little-endian to host endian-ness.
123 #[inline]
from_little_endian(self) -> Self124 fn from_little_endian(self) -> Self {
125 #[cfg(target_endian = "little")]
126 {
127 self
128 }
129 #[cfg(not(target_endian = "little"))]
130 {
131 byte_swap_f64(self)
132 }
133 }
134 }
135
136 /// Swaps the bytes of an f32.
137 #[allow(dead_code)]
138 #[inline]
byte_swap_f32(x: f32) -> f32139 pub fn byte_swap_f32(x: f32) -> f32 {
140 f32::from_bits(x.to_bits().swap_bytes())
141 }
142
143 /// Swaps the bytes of an f64.
144 #[allow(dead_code)]
145 #[inline]
byte_swap_f64(x: f64) -> f64146 pub fn byte_swap_f64(x: f64) -> f64 {
147 f64::from_bits(x.to_bits().swap_bytes())
148 }
149
150 /// Place an EndianScalar into the provided mutable byte slice. Performs
151 /// endian conversion, if necessary.
152 /// # Safety
153 /// Caller must ensure `s.len() > size_of::<T>()`
154 /// and `x` does not overlap with `s`.
155 #[inline]
emplace_scalar<T: EndianScalar>(s: &mut [u8], x: T)156 pub unsafe fn emplace_scalar<T: EndianScalar>(s: &mut [u8], x: T) {
157 let x_le = x.to_little_endian();
158 core::ptr::copy_nonoverlapping(
159 &x_le as *const T as *const u8,
160 s.as_mut_ptr() as *mut u8,
161 size_of::<T>(),
162 );
163 }
164
165 /// Read an EndianScalar from the provided byte slice at the specified location.
166 /// Performs endian conversion, if necessary.
167 /// # Safety
168 /// Caller must ensure `s.len() > loc + size_of::<T>()`.
169 #[inline]
read_scalar_at<T: EndianScalar>(s: &[u8], loc: usize) -> T170 pub unsafe fn read_scalar_at<T: EndianScalar>(s: &[u8], loc: usize) -> T {
171 read_scalar(&s[loc..])
172 }
173
174 /// Read an EndianScalar from the provided byte slice. Performs endian
175 /// conversion, if necessary.
176 /// # Safety
177 /// Caller must ensure `s.len() > size_of::<T>()`.
178 #[inline]
read_scalar<T: EndianScalar>(s: &[u8]) -> T179 pub unsafe fn read_scalar<T: EndianScalar>(s: &[u8]) -> T {
180 let mut mem = core::mem::MaybeUninit::<T>::uninit();
181 // Since [u8] has alignment 1, we copy it into T which may have higher alignment.
182 core::ptr::copy_nonoverlapping(s.as_ptr(), mem.as_mut_ptr() as *mut u8, size_of::<T>());
183 mem.assume_init().from_little_endian()
184 }
185