From 2f2a463a52c76b940d3d1d7f49bcec411e042b58 Mon Sep 17 00:00:00 2001 From: Kaido Kert Date: Fri, 21 Oct 2022 17:46:19 -0700 Subject: [PATCH 1/2] Convert between int and bytes Co-authored-by: Flier Lu --- build.rs | 2 + src/lib.rs | 1 + src/ops/bytes.rs | 384 +++++++++++++++++++++++++++++++++++++++++++++++ src/ops/mod.rs | 1 + 4 files changed, 388 insertions(+) create mode 100644 src/ops/bytes.rs diff --git a/build.rs b/build.rs index 639d0582..9327815d 100644 --- a/build.rs +++ b/build.rs @@ -15,6 +15,8 @@ fn main() { if env::var_os("CARGO_FEATURE_STD").is_some() { ac.emit_expression_cfg("1f64.copysign(-1f64)", "has_copysign"); } + ac.emit_expression_cfg("1u32.to_ne_bytes()", "has_int_to_from_bytes"); + ac.emit_expression_cfg("3.14f64.to_ne_bytes()", "has_float_to_from_bytes"); autocfg::rerun_path("build.rs"); } diff --git a/src/lib.rs b/src/lib.rs index f6562d2e..02211ca2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,7 @@ pub use crate::float::FloatConst; pub use crate::cast::{cast, AsPrimitive, FromPrimitive, NumCast, ToPrimitive}; pub use crate::identities::{one, zero, One, Zero}; pub use crate::int::PrimInt; +pub use crate::ops::bytes::ToFromBytes; pub use crate::ops::checked::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub, }; diff --git a/src/ops/bytes.rs b/src/ops/bytes.rs new file mode 100644 index 00000000..16ecfded --- /dev/null +++ b/src/ops/bytes.rs @@ -0,0 +1,384 @@ +use core::borrow::{Borrow, BorrowMut}; +use core::cmp::{Eq, Ord, PartialEq, PartialOrd}; +use core::fmt::Debug; +use core::hash::Hash; +use core::mem::transmute; + +pub trait ToFromBytes { + type Bytes: Debug + + AsRef<[u8]> + + AsMut<[u8]> + + PartialEq + + Eq + + PartialOrd + + Ord + + Hash + + Borrow<[u8]> + + BorrowMut<[u8]> + + Default; + + /// Return the memory representation of this number as a byte array in big-endian byte order. + /// + /// # Examples + /// + /// ``` + /// use num_traits::ToFromBytes; + /// + /// # #[cfg(has_int_to_from_bytes)] + /// # fn main() { + /// let bytes = 0x12345678u32.to_be_bytes(); + /// assert_eq!(bytes, [0x12, 0x34, 0x56, 0x78]); + /// # } + /// # #[cfg(not(has_int_to_from_bytes))] + /// # fn main() {} + /// ``` + 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::ToFromBytes; + /// + /// # #[cfg(has_int_to_from_bytes)] + /// # fn main() { + /// let bytes = 0x12345678u32.to_le_bytes(); + /// assert_eq!(bytes, [0x78, 0x56, 0x34, 0x12]); + /// # } + /// # #[cfg(not(has_int_to_from_bytes))] + /// # fn main() {} + /// ``` + 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::ToFromBytes; + /// + /// # #[cfg(has_int_to_from_bytes)] + /// # fn main() { + /// #[cfg(target_endian = "big")] + /// let expected = [0x12, 0x34, 0x56, 0x78]; + /// + /// #[cfg(not(target_endian = "big"))] + /// let expected = [0x78, 0x56, 0x34, 0x12]; + /// + /// let bytes = 0x12345678u32.to_ne_bytes(); + /// assert_eq!(bytes, expected) + /// # } + /// # #[cfg(not(has_int_to_from_bytes))] + /// # fn main() {} + /// ``` + fn to_ne_bytes(&self) -> Self::Bytes; + + /// Create a number from its representation as a byte array in big endian. + /// + /// # Examples + /// + /// ``` + /// use num_traits::ToFromBytes; + /// + /// let value = ::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::ToFromBytes; + /// + /// let value = ::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::ToFromBytes; + /// + /// # #[cfg(has_int_to_from_bytes)] + /// # fn main() { + /// #[cfg(target_endian = "big")] + /// let bytes = [0x12, 0x34, 0x56, 0x78]; + /// + /// #[cfg(not(target_endian = "big"))] + /// let bytes = [0x78, 0x56, 0x34, 0x12]; + /// + /// let value = ::from_ne_bytes(&bytes); + /// assert_eq!(value, 0x12345678) + /// # } + /// # #[cfg(not(has_int_to_from_bytes))] + /// # fn main() {} + /// ``` + fn from_ne_bytes(bytes: &Self::Bytes) -> Self; +} + +macro_rules! float_to_from_bytes_impl { + ($T:ty, $I:ty, $L:expr) => { + #[cfg(feature = "has_float_to_from_bytes")] + impl ToFromBytes 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) + } + + #[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(all( + not(feature = "has_float_to_from_bytes"), + feature = "has_int_to_from_bytes" + ))] + impl ToFromBytes for $T { + type Bytes = [u8; $L]; + + #[inline] + fn to_be_bytes(&self) -> Self::Bytes { + <$I as ToFromBytes>::from_ne_bytes(&self.to_ne_bytes()).to_be_bytes() + } + + #[inline] + fn to_le_bytes(&self) -> Self::Bytes { + <$I as ToFromBytes>::from_ne_bytes(&self.to_ne_bytes()).to_le_bytes() + } + + #[inline] + fn to_ne_bytes(&self) -> Self::Bytes { + unsafe { transmute(*self) } + } + + #[inline] + fn from_be_bytes(bytes: &Self::Bytes) -> Self { + ::from_ne_bytes( + &<$I as ToFromBytes>::from_be_bytes(bytes).to_ne_bytes(), + ) + } + + #[inline] + fn from_le_bytes(bytes: &Self::Bytes) -> Self { + ::from_ne_bytes( + &<$I as ToFromBytes>::from_le_bytes(bytes).to_ne_bytes(), + ) + } + + #[inline] + fn from_ne_bytes(bytes: &Self::Bytes) -> Self { + unsafe { transmute(*bytes) } + } + } + }; +} + +macro_rules! int_to_from_bytes_impl { + ($T:ty, $L:expr) => { + #[cfg(feature = "has_int_to_from_bytes")] + impl ToFromBytes 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) + } + + #[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(feature = "has_int_to_from_bytes"))] + impl ToFromBytes for $T { + type Bytes = [u8; $L]; + + #[inline] + fn to_be_bytes(&self) -> Self::Bytes { + <$T as ToFromBytes>::to_ne_bytes(&<$T>::to_be(*self)) + } + + #[inline] + fn to_le_bytes(&self) -> Self::Bytes { + <$T as ToFromBytes>::to_ne_bytes(&<$T>::to_le(*self)) + } + + #[inline] + fn to_ne_bytes(&self) -> Self::Bytes { + unsafe { transmute(*self) } + } + + #[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); +#[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); +#[cfg(target_pointer_width = "64")] +int_to_from_bytes_impl!(isize, 8); +#[cfg(target_pointer_width = "32")] +int_to_from_bytes_impl!(isize, 4); + +#[cfg(has_i128)] +int_to_from_bytes_impl!(u128, 16); +#[cfg(has_i128)] +int_to_from_bytes_impl!(i128, 16); + +float_to_from_bytes_impl!(f32, u32, 4); +float_to_from_bytes_impl!(f64, u64, 8); + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! check_to_from_bytes { + ($( $ty:ty )+) => {$({ + let n = 1; + let be = <$ty as ToFromBytes>::to_be_bytes(&n); + let le = <$ty as ToFromBytes>::to_le_bytes(&n); + let ne = <$ty as ToFromBytes>::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 ToFromBytes>::from_be_bytes(&be), n); + assert_eq!(<$ty as ToFromBytes>::from_le_bytes(&le), n); + if cfg!(target_endian = "big") { + assert_eq!(<$ty as ToFromBytes>::from_ne_bytes(&be), n); + } else { + assert_eq!(<$ty as ToFromBytes>::from_ne_bytes(&le), n); + } + })+} + } + + #[test] + fn convert_between_int_and_bytes() { + check_to_from_bytes!(u8 u16 u32 u64 usize); + check_to_from_bytes!(i8 i16 i32 i64 isize); + } + + #[cfg(has_i128)] + #[test] + fn convert_between_int_and_bytes_128() { + check_to_from_bytes!(i128 u128); + } + + #[cfg(feature = "has_float_to_from_bytes")] + #[test] + fn convert_between_float_and_bytes() { + macro_rules! check_to_from_bytes { + ($( $ty:ty )+) => {$( + let n: $ty = 3.14; + + let be = <$ty as ToFromBytes>::to_be_bytes(&n); + let le = <$ty as ToFromBytes>::to_le_bytes(&n); + let ne = <$ty as ToFromBytes>::to_ne_bytes(&n); + + assert_eq!(<$ty as ToFromBytes>::from_be_bytes(&be), n); + assert_eq!(<$ty as ToFromBytes>::from_le_bytes(&le), n); + if cfg!(target_endian = "big") { + assert_eq!(ne, be); + assert_eq!(<$ty as ToFromBytes>::from_ne_bytes(&be), n); + } else { + assert_eq!(ne, le); + assert_eq!(<$ty as ToFromBytes>::from_ne_bytes(&le), n); + } + )+} + } + + check_to_from_bytes!(f32 f64); + } +} diff --git a/src/ops/mod.rs b/src/ops/mod.rs index 585879f6..2128d86a 100644 --- a/src/ops/mod.rs +++ b/src/ops/mod.rs @@ -1,3 +1,4 @@ +pub mod bytes; pub mod checked; pub mod euclid; pub mod inv; From a922693c62aac661520ebcee8867515283565893 Mon Sep 17 00:00:00 2001 From: Kaido Kert Date: Tue, 25 Oct 2022 00:11:50 -0700 Subject: [PATCH 2/2] Simplify float to/from_bytes and enable tests --- src/ops/bytes.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/ops/bytes.rs b/src/ops/bytes.rs index 16ecfded..e7c5e3f0 100644 --- a/src/ops/bytes.rs +++ b/src/ops/bytes.rs @@ -171,45 +171,38 @@ macro_rules! float_to_from_bytes_impl { } } - #[cfg(all( - not(feature = "has_float_to_from_bytes"), - feature = "has_int_to_from_bytes" - ))] + #[cfg(not(feature = "has_float_to_from_bytes"))] impl ToFromBytes for $T { type Bytes = [u8; $L]; #[inline] fn to_be_bytes(&self) -> Self::Bytes { - <$I as ToFromBytes>::from_ne_bytes(&self.to_ne_bytes()).to_be_bytes() + <$I as ToFromBytes>::to_be_bytes(&self.to_bits()) } #[inline] fn to_le_bytes(&self) -> Self::Bytes { - <$I as ToFromBytes>::from_ne_bytes(&self.to_ne_bytes()).to_le_bytes() + <$I as ToFromBytes>::to_le_bytes(&self.to_bits()) } #[inline] fn to_ne_bytes(&self) -> Self::Bytes { - unsafe { transmute(*self) } + <$I as ToFromBytes>::to_ne_bytes(&self.to_bits()) } #[inline] fn from_be_bytes(bytes: &Self::Bytes) -> Self { - ::from_ne_bytes( - &<$I as ToFromBytes>::from_be_bytes(bytes).to_ne_bytes(), - ) + Self::from_bits(<$I as ToFromBytes>::from_be_bytes(&bytes)) } #[inline] fn from_le_bytes(bytes: &Self::Bytes) -> Self { - ::from_ne_bytes( - &<$I as ToFromBytes>::from_le_bytes(bytes).to_ne_bytes(), - ) + Self::from_bits(<$I as ToFromBytes>::from_le_bytes(&bytes)) } #[inline] fn from_ne_bytes(bytes: &Self::Bytes) -> Self { - unsafe { transmute(*bytes) } + Self::from_bits(<$I as ToFromBytes>::from_ne_bytes(&bytes)) } } }; @@ -356,7 +349,6 @@ mod tests { check_to_from_bytes!(i128 u128); } - #[cfg(feature = "has_float_to_from_bytes")] #[test] fn convert_between_float_and_bytes() { macro_rules! check_to_from_bytes {