blob: 5f50cf1f36e05dd5dd948870da4f840f855a5dca [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001/*
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 */
James Kuszmaul8e62b022022-03-22 09:33:25 -070016#![allow(clippy::wrong_self_convention)]
Austin Schuhe89fa2d2019-08-14 20:24:23 -070017
James Kuszmaul8e62b022022-03-22 09:33:25 -070018use core::mem::size_of;
Austin Schuhe89fa2d2019-08-14 20:24:23 -070019
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.
30pub trait EndianScalar: Sized + PartialEq + Copy + Clone {
31 fn to_little_endian(self) -> Self;
32 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.
37macro_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.
56macro_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
71impl_endian_scalar_noop!(bool);
72impl_endian_scalar_noop!(u8);
73impl_endian_scalar_noop!(i8);
74
75impl_endian_scalar_stdlib_le_conversion!(u16);
76impl_endian_scalar_stdlib_le_conversion!(u32);
77impl_endian_scalar_stdlib_le_conversion!(u64);
78impl_endian_scalar_stdlib_le_conversion!(i16);
79impl_endian_scalar_stdlib_le_conversion!(i32);
80impl_endian_scalar_stdlib_le_conversion!(i64);
81
82impl EndianScalar for f32 {
83 /// Convert f32 from host endian-ness to little-endian.
84 #[inline]
85 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]
97 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
109impl EndianScalar for f64 {
110 /// Convert f64 from host endian-ness to little-endian.
111 #[inline]
112 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]
124 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]
139pub 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]
146pub 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.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700152/// # Safety
153/// Caller must ensure `s.len() > size_of::<T>()`
154/// and `x` does not overlap with `s`.
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700155#[inline]
James Kuszmaul8e62b022022-03-22 09:33:25 -0700156pub 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 );
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700163}
164
165/// Read an EndianScalar from the provided byte slice at the specified location.
166/// Performs endian conversion, if necessary.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700167/// # Safety
168/// Caller must ensure `s.len() > loc + size_of::<T>()`.
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700169#[inline]
James Kuszmaul8e62b022022-03-22 09:33:25 -0700170pub unsafe fn read_scalar_at<T: EndianScalar>(s: &[u8], loc: usize) -> T {
171 read_scalar(&s[loc..])
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700172}
173
174/// Read an EndianScalar from the provided byte slice. Performs endian
175/// conversion, if necessary.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700176/// # Safety
177/// Caller must ensure `s.len() > size_of::<T>()`.
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700178#[inline]
James Kuszmaul8e62b022022-03-22 09:33:25 -0700179pub 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()
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700184}