/* * Copyright 2018 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use std::mem::size_of; /// Trait for values that must be stored in little-endian byte order, but /// might be represented in memory as big-endian. Every type that implements /// EndianScalar is a valid FlatBuffers scalar value. /// /// The Rust stdlib does not provide a trait to represent scalars, so this trait /// serves that purpose, too. /// /// Note that we do not use the num-traits crate for this, because it provides /// "too much". For example, num-traits provides i128 support, but that is an /// invalid FlatBuffers type. pub trait EndianScalar: Sized + PartialEq + Copy + Clone { fn to_little_endian(self) -> Self; fn from_little_endian(self) -> Self; } /// Macro for implementing a no-op endian conversion. This is used for types /// that are one byte wide. macro_rules! impl_endian_scalar_noop { ($ty:ident) => { impl EndianScalar for $ty { #[inline] fn to_little_endian(self) -> Self { self } #[inline] fn from_little_endian(self) -> Self { self } } }; } /// Macro for implementing an endian conversion using the stdlib `to_le` and /// `from_le` functions. This is used for integer types. It is not used for /// floats, because the `to_le` and `from_le` are not implemented for them in /// the stdlib. macro_rules! impl_endian_scalar_stdlib_le_conversion { ($ty:ident) => { impl EndianScalar for $ty { #[inline] fn to_little_endian(self) -> Self { Self::to_le(self) } #[inline] fn from_little_endian(self) -> Self { Self::from_le(self) } } }; } impl_endian_scalar_noop!(bool); impl_endian_scalar_noop!(u8); impl_endian_scalar_noop!(i8); impl_endian_scalar_stdlib_le_conversion!(u16); impl_endian_scalar_stdlib_le_conversion!(u32); impl_endian_scalar_stdlib_le_conversion!(u64); impl_endian_scalar_stdlib_le_conversion!(i16); impl_endian_scalar_stdlib_le_conversion!(i32); impl_endian_scalar_stdlib_le_conversion!(i64); impl EndianScalar for f32 { /// Convert f32 from host endian-ness to little-endian. #[inline] fn to_little_endian(self) -> Self { #[cfg(target_endian = "little")] { self } #[cfg(not(target_endian = "little"))] { byte_swap_f32(self) } } /// Convert f32 from little-endian to host endian-ness. #[inline] fn from_little_endian(self) -> Self { #[cfg(target_endian = "little")] { self } #[cfg(not(target_endian = "little"))] { byte_swap_f32(self) } } } impl EndianScalar for f64 { /// Convert f64 from host endian-ness to little-endian. #[inline] fn to_little_endian(self) -> Self { #[cfg(target_endian = "little")] { self } #[cfg(not(target_endian = "little"))] { byte_swap_f64(self) } } /// Convert f64 from little-endian to host endian-ness. #[inline] fn from_little_endian(self) -> Self { #[cfg(target_endian = "little")] { self } #[cfg(not(target_endian = "little"))] { byte_swap_f64(self) } } } /// Swaps the bytes of an f32. #[allow(dead_code)] #[inline] pub fn byte_swap_f32(x: f32) -> f32 { f32::from_bits(x.to_bits().swap_bytes()) } /// Swaps the bytes of an f64. #[allow(dead_code)] #[inline] pub fn byte_swap_f64(x: f64) -> f64 { f64::from_bits(x.to_bits().swap_bytes()) } /// Place an EndianScalar into the provided mutable byte slice. Performs /// endian conversion, if necessary. #[inline] pub fn emplace_scalar(s: &mut [u8], x: T) { let sz = size_of::(); let mut_ptr = (&mut s[..sz]).as_mut_ptr() as *mut T; let val = x.to_little_endian(); unsafe { *mut_ptr = val; } } /// Read an EndianScalar from the provided byte slice at the specified location. /// Performs endian conversion, if necessary. #[inline] pub fn read_scalar_at(s: &[u8], loc: usize) -> T { let buf = &s[loc..loc + size_of::()]; read_scalar(buf) } /// Read an EndianScalar from the provided byte slice. Performs endian /// conversion, if necessary. #[inline] pub fn read_scalar(s: &[u8]) -> T { let sz = size_of::(); let p = (&s[..sz]).as_ptr() as *const T; let x = unsafe { *p }; x.from_little_endian() }