• 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 mod private {
21     /// Types that are trivially transmutable are those where any combination of bits
22     /// represents a valid value of that type
23     ///
24     /// For example integral types are TriviallyTransmutable as all bit patterns are valid,
25     /// however, `bool` is not trivially transmutable as only `0` and `1` are valid
26     pub trait TriviallyTransmutable {}
27 
28     impl TriviallyTransmutable for i8 {}
29     impl TriviallyTransmutable for i16 {}
30     impl TriviallyTransmutable for i32 {}
31     impl TriviallyTransmutable for i64 {}
32     impl TriviallyTransmutable for u8 {}
33     impl TriviallyTransmutable for u16 {}
34     impl TriviallyTransmutable for u32 {}
35     impl TriviallyTransmutable for u64 {}
36 }
37 
38 /// Trait for values that must be stored in little-endian byte order, but
39 /// might be represented in memory as big-endian. Every type that implements
40 /// EndianScalar is a valid FlatBuffers scalar value.
41 ///
42 /// The Rust stdlib does not provide a trait to represent scalars, so this trait
43 /// serves that purpose, too.
44 ///
45 /// Note that we do not use the num-traits crate for this, because it provides
46 /// "too much". For example, num-traits provides i128 support, but that is an
47 /// invalid FlatBuffers type.
48 pub trait EndianScalar: Sized + PartialEq + Copy + Clone {
49     type Scalar: private::TriviallyTransmutable;
50 
to_little_endian(self) -> Self::Scalar51     fn to_little_endian(self) -> Self::Scalar;
52 
from_little_endian(v: Self::Scalar) -> Self53     fn from_little_endian(v: Self::Scalar) -> Self;
54 }
55 
56 /// Macro for implementing an endian conversion using the stdlib `to_le` and
57 /// `from_le` functions. This is used for integer types. It is not used for
58 /// floats, because the `to_le` and `from_le` are not implemented for them in
59 /// the stdlib.
60 macro_rules! impl_endian_scalar {
61     ($ty:ident) => {
62         impl EndianScalar for $ty {
63             type Scalar = Self;
64 
65             #[inline]
66             fn to_little_endian(self) -> Self::Scalar {
67                 Self::to_le(self)
68             }
69             #[inline]
70             fn from_little_endian(v: Self::Scalar) -> Self {
71                 Self::from_le(v)
72             }
73         }
74     };
75 }
76 
77 impl_endian_scalar!(u8);
78 impl_endian_scalar!(i8);
79 impl_endian_scalar!(u16);
80 impl_endian_scalar!(u32);
81 impl_endian_scalar!(u64);
82 impl_endian_scalar!(i16);
83 impl_endian_scalar!(i32);
84 impl_endian_scalar!(i64);
85 
86 impl EndianScalar for bool {
87     type Scalar = u8;
88 
to_little_endian(self) -> Self::Scalar89     fn to_little_endian(self) -> Self::Scalar {
90         self as u8
91     }
92 
from_little_endian(v: Self::Scalar) -> Self93     fn from_little_endian(v: Self::Scalar) -> Self {
94         v != 0
95     }
96 }
97 
98 impl EndianScalar for f32 {
99     type Scalar = u32;
100     /// Convert f32 from host endian-ness to little-endian.
101     #[inline]
to_little_endian(self) -> u32102     fn to_little_endian(self) -> u32 {
103         // Floats and Ints have the same endianness on all supported platforms.
104         // <https://doc.rust-lang.org/std/primitive.f32.html#method.from_bits>
105         self.to_bits().to_le()
106     }
107     /// Convert f32 from little-endian to host endian-ness.
108     #[inline]
from_little_endian(v: u32) -> Self109     fn from_little_endian(v: u32) -> Self {
110         // Floats and Ints have the same endianness on all supported platforms.
111         // <https://doc.rust-lang.org/std/primitive.f32.html#method.from_bits>
112         f32::from_bits(u32::from_le(v))
113     }
114 }
115 
116 impl EndianScalar for f64 {
117     type Scalar = u64;
118 
119     /// Convert f64 from host endian-ness to little-endian.
120     #[inline]
to_little_endian(self) -> u64121     fn to_little_endian(self) -> u64 {
122         // Floats and Ints have the same endianness on all supported platforms.
123         // <https://doc.rust-lang.org/std/primitive.f64.html#method.from_bits>
124         self.to_bits().to_le()
125     }
126     /// Convert f64 from little-endian to host endian-ness.
127     #[inline]
from_little_endian(v: u64) -> Self128     fn from_little_endian(v: u64) -> Self {
129         // Floats and Ints have the same endianness on all supported platforms.
130         // <https://doc.rust-lang.org/std/primitive.f64.html#method.from_bits>
131         f64::from_bits(u64::from_le(v))
132     }
133 }
134 
135 /// Place an EndianScalar into the provided mutable byte slice. Performs
136 /// endian conversion, if necessary.
137 /// # Safety
138 /// Caller must ensure `s.len() >= size_of::<T>()`
139 #[inline]
emplace_scalar<T: EndianScalar>(s: &mut [u8], x: T)140 pub unsafe fn emplace_scalar<T: EndianScalar>(s: &mut [u8], x: T) {
141     let size = size_of::<T::Scalar>();
142     debug_assert!(
143         s.len() >= size,
144         "insufficient capacity for emplace_scalar, needed {} got {}",
145         size,
146         s.len()
147     );
148 
149     let x_le = x.to_little_endian();
150     core::ptr::copy_nonoverlapping(
151         &x_le as *const T::Scalar as *const u8,
152         s.as_mut_ptr() as *mut u8,
153         size,
154     );
155 }
156 
157 /// Read an EndianScalar from the provided byte slice at the specified location.
158 /// Performs endian conversion, if necessary.
159 /// # Safety
160 /// Caller must ensure `s.len() >= loc + size_of::<T>()`.
161 #[inline]
read_scalar_at<T: EndianScalar>(s: &[u8], loc: usize) -> T162 pub unsafe fn read_scalar_at<T: EndianScalar>(s: &[u8], loc: usize) -> T {
163     read_scalar(&s[loc..])
164 }
165 
166 /// Read an EndianScalar from the provided byte slice. Performs endian
167 /// conversion, if necessary.
168 /// # Safety
169 /// Caller must ensure `s.len() > size_of::<T>()`.
170 #[inline]
read_scalar<T: EndianScalar>(s: &[u8]) -> T171 pub unsafe fn read_scalar<T: EndianScalar>(s: &[u8]) -> T {
172     let size = size_of::<T::Scalar>();
173     debug_assert!(
174         s.len() >= size,
175         "insufficient capacity for emplace_scalar, needed {} got {}",
176         size,
177         s.len()
178     );
179 
180     let mut mem = core::mem::MaybeUninit::<T::Scalar>::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);
183     T::from_little_endian(mem.assume_init())
184 }
185