• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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