//! Types for compile-time and run-time endianness. use crate::pod::Pod; use core::fmt::{self, Debug}; use core::marker::PhantomData; /// A trait for using an endianness specification. /// /// Provides methods for converting between the specified endianness and /// the native endianness of the target machine. /// /// This trait does not require that the endianness is known at compile time. pub trait Endian: Debug + Default + Clone + Copy + PartialEq + Eq + 'static { /// Construct a specification for the endianness of some values. /// /// Returns `None` if the type does not support specifying the given endianness. fn from_big_endian(big_endian: bool) -> Option; /// Construct a specification for the endianness of some values. /// /// Returns `None` if the type does not support specifying the given endianness. fn from_little_endian(little_endian: bool) -> Option { Self::from_big_endian(!little_endian) } /// Return true for big endian byte order. fn is_big_endian(self) -> bool; /// Return true for little endian byte order. #[inline] fn is_little_endian(self) -> bool { !self.is_big_endian() } /// Converts an unsigned 16 bit integer to native endian. #[inline] fn read_u16(self, n: u16) -> u16 { if self.is_big_endian() { u16::from_be(n) } else { u16::from_le(n) } } /// Converts an unsigned 32 bit integer to native endian. #[inline] fn read_u32(self, n: u32) -> u32 { if self.is_big_endian() { u32::from_be(n) } else { u32::from_le(n) } } /// Converts an unsigned 64 bit integer to native endian. #[inline] fn read_u64(self, n: u64) -> u64 { if self.is_big_endian() { u64::from_be(n) } else { u64::from_le(n) } } /// Converts a signed 16 bit integer to native endian. #[inline] fn read_i16(self, n: i16) -> i16 { if self.is_big_endian() { i16::from_be(n) } else { i16::from_le(n) } } /// Converts a signed 32 bit integer to native endian. #[inline] fn read_i32(self, n: i32) -> i32 { if self.is_big_endian() { i32::from_be(n) } else { i32::from_le(n) } } /// Converts a signed 64 bit integer to native endian. #[inline] fn read_i64(self, n: i64) -> i64 { if self.is_big_endian() { i64::from_be(n) } else { i64::from_le(n) } } /// Converts an unaligned unsigned 16 bit integer to native endian. #[inline] fn read_u16_bytes(self, n: [u8; 2]) -> u16 { if self.is_big_endian() { u16::from_be_bytes(n) } else { u16::from_le_bytes(n) } } /// Converts an unaligned unsigned 32 bit integer to native endian. #[inline] fn read_u32_bytes(self, n: [u8; 4]) -> u32 { if self.is_big_endian() { u32::from_be_bytes(n) } else { u32::from_le_bytes(n) } } /// Converts an unaligned unsigned 64 bit integer to native endian. #[inline] fn read_u64_bytes(self, n: [u8; 8]) -> u64 { if self.is_big_endian() { u64::from_be_bytes(n) } else { u64::from_le_bytes(n) } } /// Converts an unaligned signed 16 bit integer to native endian. #[inline] fn read_i16_bytes(self, n: [u8; 2]) -> i16 { if self.is_big_endian() { i16::from_be_bytes(n) } else { i16::from_le_bytes(n) } } /// Converts an unaligned signed 32 bit integer to native endian. #[inline] fn read_i32_bytes(self, n: [u8; 4]) -> i32 { if self.is_big_endian() { i32::from_be_bytes(n) } else { i32::from_le_bytes(n) } } /// Converts an unaligned signed 64 bit integer to native endian. #[inline] fn read_i64_bytes(self, n: [u8; 8]) -> i64 { if self.is_big_endian() { i64::from_be_bytes(n) } else { i64::from_le_bytes(n) } } /// Converts an unsigned 16 bit integer from native endian. #[inline] fn write_u16(self, n: u16) -> u16 { if self.is_big_endian() { u16::to_be(n) } else { u16::to_le(n) } } /// Converts an unsigned 32 bit integer from native endian. #[inline] fn write_u32(self, n: u32) -> u32 { if self.is_big_endian() { u32::to_be(n) } else { u32::to_le(n) } } /// Converts an unsigned 64 bit integer from native endian. #[inline] fn write_u64(self, n: u64) -> u64 { if self.is_big_endian() { u64::to_be(n) } else { u64::to_le(n) } } /// Converts a signed 16 bit integer from native endian. #[inline] fn write_i16(self, n: i16) -> i16 { if self.is_big_endian() { i16::to_be(n) } else { i16::to_le(n) } } /// Converts a signed 32 bit integer from native endian. #[inline] fn write_i32(self, n: i32) -> i32 { if self.is_big_endian() { i32::to_be(n) } else { i32::to_le(n) } } /// Converts a signed 64 bit integer from native endian. #[inline] fn write_i64(self, n: i64) -> i64 { if self.is_big_endian() { i64::to_be(n) } else { i64::to_le(n) } } /// Converts an unaligned unsigned 16 bit integer from native endian. #[inline] fn write_u16_bytes(self, n: u16) -> [u8; 2] { if self.is_big_endian() { u16::to_be_bytes(n) } else { u16::to_le_bytes(n) } } /// Converts an unaligned unsigned 32 bit integer from native endian. #[inline] fn write_u32_bytes(self, n: u32) -> [u8; 4] { if self.is_big_endian() { u32::to_be_bytes(n) } else { u32::to_le_bytes(n) } } /// Converts an unaligned unsigned 64 bit integer from native endian. #[inline] fn write_u64_bytes(self, n: u64) -> [u8; 8] { if self.is_big_endian() { u64::to_be_bytes(n) } else { u64::to_le_bytes(n) } } /// Converts an unaligned signed 16 bit integer from native endian. #[inline] fn write_i16_bytes(self, n: i16) -> [u8; 2] { if self.is_big_endian() { i16::to_be_bytes(n) } else { i16::to_le_bytes(n) } } /// Converts an unaligned signed 32 bit integer from native endian. #[inline] fn write_i32_bytes(self, n: i32) -> [u8; 4] { if self.is_big_endian() { i32::to_be_bytes(n) } else { i32::to_le_bytes(n) } } /// Converts an unaligned signed 64 bit integer from native endian. #[inline] fn write_i64_bytes(self, n: i64) -> [u8; 8] { if self.is_big_endian() { i64::to_be_bytes(n) } else { i64::to_le_bytes(n) } } } /// An endianness that is selectable at run-time. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Endianness { /// Little endian byte order. Little, /// Big endian byte order. Big, } impl Default for Endianness { #[cfg(target_endian = "little")] #[inline] fn default() -> Endianness { Endianness::Little } #[cfg(target_endian = "big")] #[inline] fn default() -> Endianness { Endianness::Big } } impl Endian for Endianness { #[inline] fn from_big_endian(big_endian: bool) -> Option { Some(if big_endian { Endianness::Big } else { Endianness::Little }) } #[inline] fn is_big_endian(self) -> bool { self != Endianness::Little } } /// Compile-time little endian byte order. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct LittleEndian; impl Default for LittleEndian { #[inline] fn default() -> LittleEndian { LittleEndian } } impl Endian for LittleEndian { #[inline] fn from_big_endian(big_endian: bool) -> Option { if big_endian { None } else { Some(LittleEndian) } } #[inline] fn is_big_endian(self) -> bool { false } } /// Compile-time big endian byte order. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct BigEndian; impl Default for BigEndian { #[inline] fn default() -> BigEndian { BigEndian } } impl Endian for BigEndian { #[inline] fn from_big_endian(big_endian: bool) -> Option { if big_endian { Some(BigEndian) } else { None } } #[inline] fn is_big_endian(self) -> bool { true } } /// The native endianness for the target platform. #[cfg(target_endian = "little")] pub type NativeEndian = LittleEndian; #[cfg(target_endian = "little")] #[allow(non_upper_case_globals)] #[doc(hidden)] pub const NativeEndian: LittleEndian = LittleEndian; /// The native endianness for the target platform. #[cfg(target_endian = "big")] pub type NativeEndian = BigEndian; #[cfg(target_endian = "big")] #[allow(non_upper_case_globals)] #[doc(hidden)] pub const NativeEndian: BigEndian = BigEndian; macro_rules! unsafe_impl_endian_pod { ($($struct_name:ident),+ $(,)?) => { $( unsafe impl Pod for $struct_name { } )+ } } #[cfg(not(feature = "unaligned"))] mod aligned { use super::{fmt, Endian, PhantomData, Pod}; /// A `u16` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct U16(u16, PhantomData); impl U16 { /// Construct a new value given bytes that already have the required endianness. pub fn from_bytes(n: [u8; 2]) -> Self { Self(u16::from_ne_bytes(n), PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: u16) -> Self { Self(e.write_u16(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> u16 { e.read_u16(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: u16) { self.0 = e.write_u16(n); } } /// A `u32` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct U32(u32, PhantomData); impl U32 { /// Construct a new value given bytes that already have the required endianness. pub fn from_bytes(n: [u8; 4]) -> Self { Self(u32::from_ne_bytes(n), PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: u32) -> Self { Self(e.write_u32(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> u32 { e.read_u32(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: u32) { self.0 = e.write_u32(n); } } /// A `u64` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct U64(u64, PhantomData); impl U64 { /// Construct a new value given bytes that already have the required endianness. pub fn from_bytes(n: [u8; 8]) -> Self { Self(u64::from_ne_bytes(n), PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: u64) -> Self { Self(e.write_u64(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> u64 { e.read_u64(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: u64) { self.0 = e.write_u64(n); } } /// An `i16` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct I16(i16, PhantomData); impl I16 { /// Construct a new value given bytes that already have the required endianness. pub fn from_bytes(n: [u8; 2]) -> Self { Self(i16::from_ne_bytes(n), PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: i16) -> Self { Self(e.write_i16(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> i16 { e.read_i16(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: i16) { self.0 = e.write_i16(n); } } /// An `i32` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct I32(i32, PhantomData); impl I32 { /// Construct a new value given bytes that already have the required endianness. pub fn from_bytes(n: [u8; 4]) -> Self { Self(i32::from_ne_bytes(n), PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: i32) -> Self { Self(e.write_i32(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> i32 { e.read_i32(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: i32) { self.0 = e.write_i32(n); } } /// An `i64` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct I64(i64, PhantomData); impl I64 { /// Construct a new value given bytes that already have the required endianness. pub fn from_bytes(n: [u8; 8]) -> Self { Self(i64::from_ne_bytes(n), PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: i64) -> Self { Self(e.write_i64(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> i64 { e.read_i64(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: i64) { self.0 = e.write_i64(n); } } impl fmt::Debug for U16 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "U16({:x})", self.0) } } impl fmt::Debug for U32 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "U32({:x})", self.0) } } impl fmt::Debug for U64 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "U64({:x})", self.0) } } impl fmt::Debug for I16 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "I16({:x})", self.0) } } impl fmt::Debug for I32 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "I32({:x})", self.0) } } impl fmt::Debug for I64 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "I64({:x})", self.0) } } unsafe_impl_endian_pod!(U16, U32, U64, I16, I32, I64); } #[cfg(not(feature = "unaligned"))] pub use aligned::*; /// A `u16` value with an externally specified endianness of type `E`. #[cfg(feature = "unaligned")] pub type U16 = U16Bytes; /// A `u32` value with an externally specified endianness of type `E`. #[cfg(feature = "unaligned")] pub type U32 = U32Bytes; /// A `u64` value with an externally specified endianness of type `E`. #[cfg(feature = "unaligned")] pub type U64 = U64Bytes; /// An `i16` value with an externally specified endianness of type `E`. #[cfg(feature = "unaligned")] pub type I16 = I16Bytes; /// An `i32` value with an externally specified endianness of type `E`. #[cfg(feature = "unaligned")] pub type I32 = I32Bytes; /// An `i64` value with an externally specified endianness of type `E`. #[cfg(feature = "unaligned")] pub type I64 = I64Bytes; /// An unaligned `u16` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct U16Bytes([u8; 2], PhantomData); impl U16Bytes { /// Construct a new value given bytes that already have the required endianness. pub fn from_bytes(n: [u8; 2]) -> Self { Self(n, PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: u16) -> Self { Self(e.write_u16_bytes(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> u16 { e.read_u16_bytes(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: u16) { self.0 = e.write_u16_bytes(n); } } /// An unaligned `u32` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct U32Bytes([u8; 4], PhantomData); impl U32Bytes { /// Construct a new value given bytes that already have the required endianness. pub fn from_bytes(n: [u8; 4]) -> Self { Self(n, PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: u32) -> Self { Self(e.write_u32_bytes(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> u32 { e.read_u32_bytes(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: u32) { self.0 = e.write_u32_bytes(n); } } /// An unaligned `u64` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct U64Bytes([u8; 8], PhantomData); impl U64Bytes { /// Construct a new value given bytes that already have the required endianness. pub fn from_bytes(n: [u8; 8]) -> Self { Self(n, PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: u64) -> Self { Self(e.write_u64_bytes(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> u64 { e.read_u64_bytes(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: u64) { self.0 = e.write_u64_bytes(n); } } /// An unaligned `i16` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct I16Bytes([u8; 2], PhantomData); impl I16Bytes { /// Construct a new value given bytes that already have the required endianness. pub fn from_bytes(n: [u8; 2]) -> Self { Self(n, PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: i16) -> Self { Self(e.write_i16_bytes(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> i16 { e.read_i16_bytes(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: i16) { self.0 = e.write_i16_bytes(n); } } /// An unaligned `i32` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct I32Bytes([u8; 4], PhantomData); impl I32Bytes { /// Construct a new value given bytes that already have the required endianness. pub fn from_bytes(n: [u8; 4]) -> Self { Self(n, PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: i32) -> Self { Self(e.write_i32_bytes(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> i32 { e.read_i32_bytes(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: i32) { self.0 = e.write_i32_bytes(n); } } /// An unaligned `i64` value with an externally specified endianness of type `E`. #[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct I64Bytes([u8; 8], PhantomData); impl I64Bytes { /// Construct a new value given bytes that already have the required endianness. pub fn from_bytes(n: [u8; 8]) -> Self { Self(n, PhantomData) } /// Construct a new value given a native endian value. pub fn new(e: E, n: i64) -> Self { Self(e.write_i64_bytes(n), PhantomData) } /// Return the value as a native endian value. pub fn get(self, e: E) -> i64 { e.read_i64_bytes(self.0) } /// Set the value given a native endian value. pub fn set(&mut self, e: E, n: i64) { self.0 = e.write_i64_bytes(n); } } impl fmt::Debug for U16Bytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "U16({:x}, {:x})", self.0[0], self.0[1],) } } impl fmt::Debug for U32Bytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "U32({:x}, {:x}, {:x}, {:x})", self.0[0], self.0[1], self.0[2], self.0[3], ) } } impl fmt::Debug for U64Bytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "U64({:x}, {:x}, {:x}, {:x}, {:x}, {:x}, {:x}, {:x})", self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7], ) } } impl fmt::Debug for I16Bytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "I16({:x}, {:x})", self.0[0], self.0[1],) } } impl fmt::Debug for I32Bytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "I32({:x}, {:x}, {:x}, {:x})", self.0[0], self.0[1], self.0[2], self.0[3], ) } } impl fmt::Debug for I64Bytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "I64({:x}, {:x}, {:x}, {:x}, {:x}, {:x}, {:x}, {:x})", self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7], ) } } unsafe_impl_endian_pod!(U16Bytes, U32Bytes, U64Bytes, I16Bytes, I32Bytes, I64Bytes);