use core::borrow::{Borrow, BorrowMut}; use core::cmp::{Eq, Ord, PartialEq, PartialOrd}; use core::fmt::Debug; use core::hash::Hash; #[cfg(not(has_int_to_from_bytes))] use core::mem::transmute; pub trait NumBytes: Debug + AsRef<[u8]> + AsMut<[u8]> + PartialEq + Eq + PartialOrd + Ord + Hash + Borrow<[u8]> + BorrowMut<[u8]> { } impl NumBytes for T where T: Debug + AsRef<[u8]> + AsMut<[u8]> + PartialEq + Eq + PartialOrd + Ord + Hash + Borrow<[u8]> + BorrowMut<[u8]> + ?Sized { } pub trait ToBytes { type Bytes: NumBytes; /// Return the memory representation of this number as a byte array in big-endian byte order. /// /// # Examples /// /// ``` /// use num_traits::ToBytes; /// /// let bytes = ToBytes::to_be_bytes(&0x12345678u32); /// assert_eq!(bytes, [0x12, 0x34, 0x56, 0x78]); /// ``` fn to_be_bytes(&self) -> Self::Bytes; /// Return the memory representation of this number as a byte array in little-endian byte order. /// /// # Examples /// /// ``` /// use num_traits::ToBytes; /// /// let bytes = ToBytes::to_le_bytes(&0x12345678u32); /// assert_eq!(bytes, [0x78, 0x56, 0x34, 0x12]); /// ``` fn to_le_bytes(&self) -> Self::Bytes; /// Return the memory representation of this number as a byte array in native byte order. /// /// As the target platform's native endianness is used, /// portable code should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead. /// /// [`to_be_bytes`]: #method.to_be_bytes /// [`to_le_bytes`]: #method.to_le_bytes /// /// # Examples /// /// ``` /// use num_traits::ToBytes; /// /// #[cfg(target_endian = "big")] /// let expected = [0x12, 0x34, 0x56, 0x78]; /// /// #[cfg(target_endian = "little")] /// let expected = [0x78, 0x56, 0x34, 0x12]; /// /// let bytes = ToBytes::to_ne_bytes(&0x12345678u32); /// assert_eq!(bytes, expected) /// ``` fn to_ne_bytes(&self) -> Self::Bytes { #[cfg(target_endian = "big")] let bytes = self.to_be_bytes(); #[cfg(target_endian = "little")] let bytes = self.to_le_bytes(); bytes } } pub trait FromBytes: Sized { type Bytes: NumBytes + ?Sized; /// Create a number from its representation as a byte array in big endian. /// /// # Examples /// /// ``` /// use num_traits::FromBytes; /// /// let value: u32 = FromBytes::from_be_bytes(&[0x12, 0x34, 0x56, 0x78]); /// assert_eq!(value, 0x12345678); /// ``` fn from_be_bytes(bytes: &Self::Bytes) -> Self; /// Create a number from its representation as a byte array in little endian. /// /// # Examples /// /// ``` /// use num_traits::FromBytes; /// /// let value: u32 = FromBytes::from_le_bytes(&[0x78, 0x56, 0x34, 0x12]); /// assert_eq!(value, 0x12345678); /// ``` fn from_le_bytes(bytes: &Self::Bytes) -> Self; /// Create a number from its memory representation as a byte array in native endianness. /// /// As the target platform's native endianness is used, /// portable code likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as appropriate instead. /// /// [`from_be_bytes`]: #method.from_be_bytes /// [`from_le_bytes`]: #method.from_le_bytes /// /// # Examples /// /// ``` /// use num_traits::FromBytes; /// /// #[cfg(target_endian = "big")] /// let bytes = [0x12, 0x34, 0x56, 0x78]; /// /// #[cfg(target_endian = "little")] /// let bytes = [0x78, 0x56, 0x34, 0x12]; /// /// let value: u32 = FromBytes::from_ne_bytes(&bytes); /// assert_eq!(value, 0x12345678) /// ``` fn from_ne_bytes(bytes: &Self::Bytes) -> Self { #[cfg(target_endian = "big")] let this = Self::from_be_bytes(bytes); #[cfg(target_endian = "little")] let this = Self::from_le_bytes(bytes); this } } macro_rules! float_to_from_bytes_impl { ($T:ty, $L:expr) => { #[cfg(has_float_to_from_bytes)] impl ToBytes for $T { type Bytes = [u8; $L]; #[inline] fn to_be_bytes(&self) -> Self::Bytes { <$T>::to_be_bytes(*self) } #[inline] fn to_le_bytes(&self) -> Self::Bytes { <$T>::to_le_bytes(*self) } #[inline] fn to_ne_bytes(&self) -> Self::Bytes { <$T>::to_ne_bytes(*self) } } #[cfg(has_float_to_from_bytes)] impl FromBytes for $T { type Bytes = [u8; $L]; #[inline] fn from_be_bytes(bytes: &Self::Bytes) -> Self { <$T>::from_be_bytes(*bytes) } #[inline] fn from_le_bytes(bytes: &Self::Bytes) -> Self { <$T>::from_le_bytes(*bytes) } #[inline] fn from_ne_bytes(bytes: &Self::Bytes) -> Self { <$T>::from_ne_bytes(*bytes) } } #[cfg(not(has_float_to_from_bytes))] impl ToBytes for $T { type Bytes = [u8; $L]; #[inline] fn to_be_bytes(&self) -> Self::Bytes { ToBytes::to_be_bytes(&self.to_bits()) } #[inline] fn to_le_bytes(&self) -> Self::Bytes { ToBytes::to_le_bytes(&self.to_bits()) } #[inline] fn to_ne_bytes(&self) -> Self::Bytes { ToBytes::to_ne_bytes(&self.to_bits()) } } #[cfg(not(has_float_to_from_bytes))] impl FromBytes for $T { type Bytes = [u8; $L]; #[inline] fn from_be_bytes(bytes: &Self::Bytes) -> Self { Self::from_bits(FromBytes::from_be_bytes(bytes)) } #[inline] fn from_le_bytes(bytes: &Self::Bytes) -> Self { Self::from_bits(FromBytes::from_le_bytes(bytes)) } #[inline] fn from_ne_bytes(bytes: &Self::Bytes) -> Self { Self::from_bits(FromBytes::from_ne_bytes(bytes)) } } }; } macro_rules! int_to_from_bytes_impl { ($T:ty, $L:expr) => { #[cfg(has_int_to_from_bytes)] impl ToBytes for $T { type Bytes = [u8; $L]; #[inline] fn to_be_bytes(&self) -> Self::Bytes { <$T>::to_be_bytes(*self) } #[inline] fn to_le_bytes(&self) -> Self::Bytes { <$T>::to_le_bytes(*self) } #[inline] fn to_ne_bytes(&self) -> Self::Bytes { <$T>::to_ne_bytes(*self) } } #[cfg(has_int_to_from_bytes)] impl FromBytes for $T { type Bytes = [u8; $L]; #[inline] fn from_be_bytes(bytes: &Self::Bytes) -> Self { <$T>::from_be_bytes(*bytes) } #[inline] fn from_le_bytes(bytes: &Self::Bytes) -> Self { <$T>::from_le_bytes(*bytes) } #[inline] fn from_ne_bytes(bytes: &Self::Bytes) -> Self { <$T>::from_ne_bytes(*bytes) } } #[cfg(not(has_int_to_from_bytes))] impl ToBytes for $T { type Bytes = [u8; $L]; #[inline] fn to_be_bytes(&self) -> Self::Bytes { <$T as ToBytes>::to_ne_bytes(&<$T>::to_be(*self)) } #[inline] fn to_le_bytes(&self) -> Self::Bytes { <$T as ToBytes>::to_ne_bytes(&<$T>::to_le(*self)) } #[inline] fn to_ne_bytes(&self) -> Self::Bytes { unsafe { transmute(*self) } } } #[cfg(not(has_int_to_from_bytes))] impl FromBytes for $T { type Bytes = [u8; $L]; #[inline] fn from_be_bytes(bytes: &Self::Bytes) -> Self { Self::from_be(::from_ne_bytes(bytes)) } #[inline] fn from_le_bytes(bytes: &Self::Bytes) -> Self { Self::from_le(::from_ne_bytes(bytes)) } #[inline] fn from_ne_bytes(bytes: &Self::Bytes) -> Self { unsafe { transmute(*bytes) } } } }; } int_to_from_bytes_impl!(u8, 1); int_to_from_bytes_impl!(u16, 2); int_to_from_bytes_impl!(u32, 4); int_to_from_bytes_impl!(u64, 8); int_to_from_bytes_impl!(u128, 16); #[cfg(target_pointer_width = "64")] int_to_from_bytes_impl!(usize, 8); #[cfg(target_pointer_width = "32")] int_to_from_bytes_impl!(usize, 4); int_to_from_bytes_impl!(i8, 1); int_to_from_bytes_impl!(i16, 2); int_to_from_bytes_impl!(i32, 4); int_to_from_bytes_impl!(i64, 8); int_to_from_bytes_impl!(i128, 16); #[cfg(target_pointer_width = "64")] int_to_from_bytes_impl!(isize, 8); #[cfg(target_pointer_width = "32")] int_to_from_bytes_impl!(isize, 4); float_to_from_bytes_impl!(f32, 4); float_to_from_bytes_impl!(f64, 8); #[cfg(test)] mod tests { use super::*; macro_rules! check_to_from_bytes { ($( $ty:ty )+) => {$({ let n = 1; let be = <$ty as ToBytes>::to_be_bytes(&n); let le = <$ty as ToBytes>::to_le_bytes(&n); let ne = <$ty as ToBytes>::to_ne_bytes(&n); assert_eq!(*be.last().unwrap(), 1); assert_eq!(*le.first().unwrap(), 1); if cfg!(target_endian = "big") { assert_eq!(*ne.last().unwrap(), 1); } else { assert_eq!(*ne.first().unwrap(), 1); } assert_eq!(<$ty as FromBytes>::from_be_bytes(&be), n); assert_eq!(<$ty as FromBytes>::from_le_bytes(&le), n); if cfg!(target_endian = "big") { assert_eq!(<$ty as FromBytes>::from_ne_bytes(&be), n); } else { assert_eq!(<$ty as FromBytes>::from_ne_bytes(&le), n); } })+} } #[test] fn convert_between_int_and_bytes() { check_to_from_bytes!(u8 u16 u32 u64 u128 usize); check_to_from_bytes!(i8 i16 i32 i64 i128 isize); } #[test] fn convert_between_float_and_bytes() { macro_rules! check_to_from_bytes { ($( $ty:ty )+) => {$( let n: $ty = 3.14; let be = <$ty as ToBytes>::to_be_bytes(&n); let le = <$ty as ToBytes>::to_le_bytes(&n); let ne = <$ty as ToBytes>::to_ne_bytes(&n); assert_eq!(<$ty as FromBytes>::from_be_bytes(&be), n); assert_eq!(<$ty as FromBytes>::from_le_bytes(&le), n); if cfg!(target_endian = "big") { assert_eq!(ne, be); assert_eq!(<$ty as FromBytes>::from_ne_bytes(&be), n); } else { assert_eq!(ne, le); assert_eq!(<$ty as FromBytes>::from_ne_bytes(&le), n); } )+} } check_to_from_bytes!(f32 f64); } }