diff --git a/build.rs b/build.rs index fd60866..3e425e3 100644 --- a/build.rs +++ b/build.rs @@ -8,6 +8,19 @@ fn main() { } else if env::var_os("CARGO_FEATURE_I128").is_some() { panic!("i128 support was not detected!"); } + + if probe(r#" + fn main() { + let bytes = 0x1234567890123456u64.to_ne_bytes(); + + assert_eq!(bytes, if cfg!(target_endian = "big") { + [0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56] + } else { + [0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12] + }); + }"#) { + println!("cargo:rustc-cfg=int_to_from_bytes"); + } } /// Test if a code snippet can be compiled diff --git a/src/int.rs b/src/int.rs index 26d5a9a..fb2a3d4 100644 --- a/src/int.rs +++ b/src/int.rs @@ -1,6 +1,7 @@ use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; use bounds::Bounded; +use ops::bytes::IntToFromBytes; use ops::checked::*; use ops::saturating::Saturating; use {Num, NumCast}; @@ -51,6 +52,7 @@ pub trait PrimInt: + CheckedMul + CheckedDiv + Saturating + + IntToFromBytes { /// Returns the number of ones in the binary representation of `self`. /// diff --git a/src/lib.rs b/src/lib.rs index 172e714..d6bef05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,6 +33,7 @@ pub use float::FloatConst; pub use cast::{cast, AsPrimitive, FromPrimitive, NumCast, ToPrimitive}; pub use identities::{one, zero, One, Zero}; pub use int::PrimInt; +pub use ops::bytes::IntToFromBytes; pub use 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 0000000..9ceddb5 --- /dev/null +++ b/src/ops/bytes.rs @@ -0,0 +1,201 @@ +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 IntToFromBytes { + type Bytes: Debug + + AsRef<[u8]> + + AsMut<[u8]> + + PartialEq + + Eq + + PartialOrd + + Ord + + Hash + + Borrow<[u8]> + + BorrowMut<[u8]> + + Default; + + /// Return the memory representation of this integer as a byte array in big-endian byte order. + /// + /// # Examples + /// + /// ``` + /// use num_traits::IntToFromBytes; + /// + /// let bytes = 0x12345678u32.to_be_bytes(); + /// assert_eq!(bytes, [0x12, 0x34, 0x56, 0x78]); + /// ``` + fn to_be_bytes(self) -> Self::Bytes; + + /// Return the memory representation of this integer as a byte array in little-endian byte order. + /// + /// # Examples + /// + /// ``` + /// use num_traits::IntToFromBytes; + /// + /// let bytes = 0x12345678u32.to_le_bytes(); + /// assert_eq!(bytes, [0x78, 0x56, 0x34, 0x12]); + /// ``` + fn to_le_bytes(self) -> Self::Bytes; + + /// Return the memory representation of this integer 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::IntToFromBytes; + /// + /// let bytes = 0x12345678u32.to_ne_bytes(); + /// assert_eq!(bytes, if cfg!(target_endian = "big") { + /// [0x12, 0x34, 0x56, 0x78] + /// } else { + /// [0x78, 0x56, 0x34, 0x12] + /// }); + /// ``` + fn to_ne_bytes(self) -> Self::Bytes; + + /// Create an integer value from its representation as a byte array in big endian. + /// + /// # Examples + /// + /// ``` + /// use num_traits::IntToFromBytes; + /// + /// let value = u32::from_be_bytes([0x12, 0x34, 0x56, 0x78]); + /// assert_eq!(value, 0x12345678); + /// ``` + fn from_be_bytes(bytes: Self::Bytes) -> Self; + + /// Create an integer value from its representation as a byte array in little endian. + /// + /// # Examples + /// + /// ``` + /// use num_traits::IntToFromBytes; + /// + /// let value = u32::from_le_bytes([0x78, 0x56, 0x34, 0x12]); + /// assert_eq!(value, 0x12345678); + /// ``` + fn from_le_bytes(bytes: Self::Bytes) -> Self; + + /// Create an integer value 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::IntToFromBytes; + /// + /// let value = u32::from_ne_bytes(if cfg!(target_endian = "big") { + /// [0x12, 0x34, 0x56, 0x78] + /// } else { + /// [0x78, 0x56, 0x34, 0x12] + /// }); + /// assert_eq!(value, 0x12345678); + /// ``` + fn from_ne_bytes(bytes: Self::Bytes) -> Self; +} + +macro_rules! int_to_from_bytes_impl { + ($T:ty, $L:expr) => { + #[cfg(feature = "int_to_from_bytes")] + impl IntToFromBytes 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 = "int_to_from_bytes"))] + impl IntToFromBytes for $T { + type Bytes = [u8; $L]; + + #[inline] + fn to_be_bytes(self) -> Self::Bytes { + <$T>::to_ne_bytes(<$T>::to_be(self)) + } + + #[inline] + fn to_le_bytes(self) -> Self::Bytes { + <$T>::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(Self::from_ne_bytes(bytes)) + } + + #[inline] + fn from_le_bytes(bytes: Self::Bytes) -> Self { + Self::from_le(Self::from_ne_bytes(bytes)) + } + + #[inline] + fn from_ne_bytes(bytes: Self::Bytes) -> Self { + unsafe { transmute(bytes) } + } + } + }; +} + +// int_to_from_bytes_impl!(type, signed, unsigned); +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(has_i128)] +int_to_from_bytes_impl!(u128, 16); +int_to_from_bytes_impl!(usize, 8); +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(has_i128)] +int_to_from_bytes_impl!(i128, 16); +int_to_from_bytes_impl!(isize, 8); diff --git a/src/ops/mod.rs b/src/ops/mod.rs index fd1695d..b39dd4a 100644 --- a/src/ops/mod.rs +++ b/src/ops/mod.rs @@ -3,3 +3,4 @@ pub mod inv; pub mod mul_add; pub mod saturating; pub mod wrapping; +pub mod bytes; \ No newline at end of file