From 87c529c43a41e0c04a468cd2b301013df629b040 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sat, 14 Jun 2014 22:53:55 -0700 Subject: [PATCH] Add a ByteOrder trait for abstracting over endian conversions The `Bitwise::swap_bytes` method was also moved into the `ByteOrder` trait. This was because it works on the byte level rather than the bit level. --- src/libcore/mem.rs | 336 +++++++++++++++++++++++---------- src/libcore/num/int_macros.rs | 11 -- src/libcore/num/mod.rs | 72 +++---- src/libcore/num/uint_macros.rs | 11 -- 4 files changed, 263 insertions(+), 167 deletions(-) diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 237efcd0096..6b08a32944c 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -13,6 +13,7 @@ //! This module contains functions for querying the size and alignment of //! types, initializing and manipulating memory. +use clone::Clone; use ptr; use intrinsics; use intrinsics::{bswap16, bswap32, bswap64}; @@ -169,151 +170,238 @@ pub unsafe fn move_val_init(dst: &mut T, src: T) { ptr::write(dst, src) } -/// Convert an u16 to little endian from the target's endianness. -/// -/// On little endian, this is a no-op. On big endian, the bytes are swapped. -#[cfg(target_endian = "little")] #[inline] pub fn to_le16(x: u16) -> u16 { x } +/// A type that can have its bytes re-ordered. +pub trait ByteOrder: Clone { + /// Reverses the byte order of the value. + /// + /// # Example + /// + /// ```rust + /// use std::mem::ByteOrder; + /// + /// let n = 0x0123456789ABCDEFu64; + /// let m = 0xEFCDAB8967452301u64; + /// + /// assert_eq!(n.swap_bytes(), m); + /// ``` + fn swap_bytes(&self) -> Self; + + /// Convert a value from big endian to the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are swapped. + /// + /// # Example + /// + /// ```rust + /// use std::mem::ByteOrder; + /// + /// let n = 0x0123456789ABCDEFu64; + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(ByteOrder::from_big_endian(n), n) + /// } else { + /// assert_eq!(ByteOrder::from_big_endian(n), n.swap_bytes()) + /// } + /// ``` + #[inline] + fn from_big_endian(x: Self) -> Self { + if cfg!(target_endian = "big") { x } else { x.swap_bytes() } + } + + /// Convert a value from little endian to the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are swapped. + /// + /// # Example + /// + /// ```rust + /// use std::mem::ByteOrder; + /// + /// let n = 0x0123456789ABCDEFu64; + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(ByteOrder::from_little_endian(n), n) + /// } else { + /// assert_eq!(ByteOrder::from_little_endian(n), n.swap_bytes()) + /// } + /// ``` + #[inline] + fn from_little_endian(x: Self) -> Self { + if cfg!(target_endian = "little") { x } else { x.swap_bytes() } + } + + /// Convert the value to big endian from the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are swapped. + /// + /// # Example + /// + /// ```rust + /// use std::mem::ByteOrder; + /// + /// let n = 0x0123456789ABCDEFu64; + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(n.to_big_endian(), n) + /// } else { + /// assert_eq!(n.to_big_endian(), n.swap_bytes()) + /// } + /// ``` + #[inline] + fn to_big_endian(&self) -> Self { + if cfg!(target_endian = "big") { self.clone() } else { self.swap_bytes() } + } + + /// Convert the value to little endian from the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are swapped. + /// + /// # Example + /// + /// ```rust + /// use std::mem::ByteOrder; + /// + /// let n = 0x0123456789ABCDEFu64; + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(n.to_little_endian(), n) + /// } else { + /// assert_eq!(n.to_little_endian(), n.swap_bytes()) + /// } + /// ``` + #[inline] + fn to_little_endian(&self) -> Self { + if cfg!(target_endian = "little") { self.clone() } else { self.swap_bytes() } + } +} + +impl ByteOrder for u8 { + #[inline] + fn swap_bytes(&self) -> u8 { + *self // swapping a single byte does nothing + } +} + +impl ByteOrder for u16 { + #[inline] + fn swap_bytes(&self) -> u16 { + unsafe { intrinsics::bswap16(*self) } + } +} + +impl ByteOrder for u32 { + #[inline] + fn swap_bytes(&self) -> u32 { + unsafe { intrinsics::bswap32(*self) } + } +} + +impl ByteOrder for u64 { + #[inline] + fn swap_bytes(&self) -> u64 { + unsafe { intrinsics::bswap64(*self) } + } +} + +#[cfg(target_word_size = "32")] +impl ByteOrder for uint { + #[inline] + fn swap_bytes(&self) -> uint { + (*self as u32).swap_bytes() as uint + } +} + +#[cfg(target_word_size = "64")] +impl ByteOrder for uint { + #[inline] + fn swap_bytes(&self) -> uint { + (*self as u64).swap_bytes() as uint + } +} /// Convert an u16 to little endian from the target's endianness. /// /// On little endian, this is a no-op. On big endian, the bytes are swapped. -#[cfg(target_endian = "big")] #[inline] #[stable] -pub fn to_le16(x: u16) -> u16 { unsafe { bswap16(x) } } +#[inline] +#[stable] +pub fn to_le16(x: u16) -> u16 { x.to_little_endian() } /// Convert an u32 to little endian from the target's endianness. /// /// On little endian, this is a no-op. On big endian, the bytes are swapped. -#[cfg(target_endian = "little")] #[inline] #[stable] -pub fn to_le32(x: u32) -> u32 { x } - -/// Convert an u32 to little endian from the target's endianness. -/// -/// On little endian, this is a no-op. On big endian, the bytes are swapped. -#[cfg(target_endian = "big")] #[inline] #[stable] -pub fn to_le32(x: u32) -> u32 { unsafe { bswap32(x) } } +#[inline] +#[stable] +pub fn to_le32(x: u32) -> u32 { x.to_little_endian() } /// Convert an u64 to little endian from the target's endianness. /// /// On little endian, this is a no-op. On big endian, the bytes are swapped. -#[cfg(target_endian = "little")] #[inline] #[stable] -pub fn to_le64(x: u64) -> u64 { x } - -/// Convert an u64 to little endian from the target's endianness. -/// -/// On little endian, this is a no-op. On big endian, the bytes are swapped. -#[cfg(target_endian = "big")] #[inline] #[stable] -pub fn to_le64(x: u64) -> u64 { unsafe { bswap64(x) } } - +#[inline] +#[stable] +pub fn to_le64(x: u64) -> u64 { x.to_little_endian() } /// Convert an u16 to big endian from the target's endianness. /// /// On big endian, this is a no-op. On little endian, the bytes are swapped. -#[cfg(target_endian = "little")] #[inline] #[stable] -pub fn to_be16(x: u16) -> u16 { unsafe { bswap16(x) } } - -/// Convert an u16 to big endian from the target's endianness. -/// -/// On big endian, this is a no-op. On little endian, the bytes are swapped. -#[cfg(target_endian = "big")] #[inline] #[stable] -pub fn to_be16(x: u16) -> u16 { x } +#[inline] +#[stable] +pub fn to_be16(x: u16) -> u16 { x.to_big_endian() } /// Convert an u32 to big endian from the target's endianness. /// /// On big endian, this is a no-op. On little endian, the bytes are swapped. -#[cfg(target_endian = "little")] #[inline] #[stable] -pub fn to_be32(x: u32) -> u32 { unsafe { bswap32(x) } } - -/// Convert an u32 to big endian from the target's endianness. -/// -/// On big endian, this is a no-op. On little endian, the bytes are swapped. -#[cfg(target_endian = "big")] #[inline] #[stable] -pub fn to_be32(x: u32) -> u32 { x } +#[inline] +#[stable] +pub fn to_be32(x: u32) -> u32 { x.to_big_endian() } /// Convert an u64 to big endian from the target's endianness. /// /// On big endian, this is a no-op. On little endian, the bytes are swapped. -#[cfg(target_endian = "little")] #[inline] #[stable] -pub fn to_be64(x: u64) -> u64 { unsafe { bswap64(x) } } - -/// Convert an u64 to big endian from the target's endianness. -/// -/// On big endian, this is a no-op. On little endian, the bytes are swapped. -#[cfg(target_endian = "big")] #[inline] #[stable] -pub fn to_be64(x: u64) -> u64 { x } - +#[inline] +#[stable] +pub fn to_be64(x: u64) -> u64 { x.to_big_endian() } /// Convert an u16 from little endian to the target's endianness. /// /// On little endian, this is a no-op. On big endian, the bytes are swapped. -#[cfg(target_endian = "little")] #[inline] #[stable] -pub fn from_le16(x: u16) -> u16 { x } - -/// Convert an u16 from little endian to the target's endianness. -/// -/// On little endian, this is a no-op. On big endian, the bytes are swapped. -#[cfg(target_endian = "big")] #[inline] #[stable] -pub fn from_le16(x: u16) -> u16 { unsafe { bswap16(x) } } +#[inline] +#[stable] +pub fn from_le16(x: u16) -> u16 { ByteOrder::from_little_endian(x) } /// Convert an u32 from little endian to the target's endianness. /// /// On little endian, this is a no-op. On big endian, the bytes are swapped. -#[cfg(target_endian = "little")] #[inline] #[stable] -pub fn from_le32(x: u32) -> u32 { x } - -/// Convert an u32 from little endian to the target's endianness. -/// -/// On little endian, this is a no-op. On big endian, the bytes are swapped. -#[cfg(target_endian = "big")] #[inline] #[stable] -pub fn from_le32(x: u32) -> u32 { unsafe { bswap32(x) } } +#[inline] +#[stable] +pub fn from_le32(x: u32) -> u32 { ByteOrder::from_little_endian(x) } /// Convert an u64 from little endian to the target's endianness. /// /// On little endian, this is a no-op. On big endian, the bytes are swapped. -#[cfg(target_endian = "little")] #[inline] #[stable] -pub fn from_le64(x: u64) -> u64 { x } - -/// Convert an u64 from little endian to the target's endianness. -/// -/// On little endian, this is a no-op. On big endian, the bytes are swapped. -#[cfg(target_endian = "big")] #[inline] #[stable] -pub fn from_le64(x: u64) -> u64 { unsafe { bswap64(x) } } - +#[inline] +#[stable] +pub fn from_le64(x: u64) -> u64 { ByteOrder::from_little_endian(x) } /// Convert an u16 from big endian to the target's endianness. /// /// On big endian, this is a no-op. On little endian, the bytes are swapped. -#[cfg(target_endian = "little")] #[inline] #[stable] -pub fn from_be16(x: u16) -> u16 { unsafe { bswap16(x) } } - -/// Convert an u16 from big endian to the target's endianness. -/// -/// On big endian, this is a no-op. On little endian, the bytes are swapped. -#[cfg(target_endian = "big")] #[inline] #[stable] -pub fn from_be16(x: u16) -> u16 { x } +#[inline] +#[stable] +pub fn from_be16(x: u16) -> u16 { ByteOrder::from_big_endian(x) } /// Convert an u32 from big endian to the target's endianness. /// /// On big endian, this is a no-op. On little endian, the bytes are swapped. -#[cfg(target_endian = "little")] #[inline] #[stable] -pub fn from_be32(x: u32) -> u32 { unsafe { bswap32(x) } } - -/// Convert an u32 from big endian to the target's endianness. -/// -/// On big endian, this is a no-op. On little endian, the bytes are swapped. -#[cfg(target_endian = "big")] #[inline] #[stable] -pub fn from_be32(x: u32) -> u32 { x } +#[inline] +#[stable] +pub fn from_be32(x: u32) -> u32 { ByteOrder::from_big_endian(x) } /// Convert an u64 from big endian to the target's endianness. /// /// On big endian, this is a no-op. On little endian, the bytes are swapped. -#[cfg(target_endian = "little")] #[inline] #[stable] -pub fn from_be64(x: u64) -> u64 { unsafe { bswap64(x) } } - -/// Convert an u64 from big endian to the target's endianness. -/// -/// On big endian, this is a no-op. On little endian, the bytes are swapped. -#[cfg(target_endian = "big")] #[inline] #[stable] -pub fn from_be64(x: u64) -> u64 { x } +#[inline] +#[stable] +pub fn from_be64(x: u64) -> u64 { ByteOrder::from_big_endian(x) } /** * Swap the values at two mutable locations of the same type, without @@ -558,6 +646,60 @@ mod tests { assert!(Vec::from_slice([76u8]) == transmute("L".to_string())); } } + + macro_rules! test_byte_order { + ($T:ident) => { + mod $T { + use mem::ByteOrder; + + static A: $T = 0b0101100; + static B: $T = 0b0100001; + static C: $T = 0b1111001; + + static _0: $T = 0; + static _1: $T = !0; + + #[test] + fn test_swap_bytes() { + assert_eq!(A.swap_bytes().swap_bytes(), A); + assert_eq!(B.swap_bytes().swap_bytes(), B); + assert_eq!(C.swap_bytes().swap_bytes(), C); + + // Swapping these should make no difference + assert_eq!(_0.swap_bytes(), _0); + assert_eq!(_1.swap_bytes(), _1); + } + + #[test] + fn test_little_endian() { + assert_eq!(ByteOrder::from_little_endian(A.to_little_endian()), A); + assert_eq!(ByteOrder::from_little_endian(B.to_little_endian()), B); + assert_eq!(ByteOrder::from_little_endian(C.to_little_endian()), C); + assert_eq!(ByteOrder::from_little_endian(_0), _0); + assert_eq!(ByteOrder::from_little_endian(_1), _1); + assert_eq!(_0.to_little_endian(), _0); + assert_eq!(_1.to_little_endian(), _1); + } + + #[test] + fn test_big_endian() { + assert_eq!(ByteOrder::from_big_endian(A.to_big_endian()), A); + assert_eq!(ByteOrder::from_big_endian(B.to_big_endian()), B); + assert_eq!(ByteOrder::from_big_endian(C.to_big_endian()), C); + assert_eq!(ByteOrder::from_big_endian(_0), _0); + assert_eq!(ByteOrder::from_big_endian(_1), _1); + assert_eq!(_0.to_big_endian(), _0); + assert_eq!(_1.to_big_endian(), _1); + } + } + } + } + + test_byte_order!(u8) + test_byte_order!(u16) + test_byte_order!(u32) + test_byte_order!(u64) + test_byte_order!(uint) } // FIXME #13642 (these benchmarks should be in another place) diff --git a/src/libcore/num/int_macros.rs b/src/libcore/num/int_macros.rs index 20bb12db694..8a1bd66aa1a 100644 --- a/src/libcore/num/int_macros.rs +++ b/src/libcore/num/int_macros.rs @@ -113,17 +113,6 @@ mod tests { assert!((0b1111001 as $T).count_zeros() == BITS as $T - 5); } - #[test] - fn test_swap_bytes() { - let n: $T = 0b0101100; assert_eq!(n.swap_bytes().swap_bytes(), n); - let n: $T = 0b0100001; assert_eq!(n.swap_bytes().swap_bytes(), n); - let n: $T = 0b1111001; assert_eq!(n.swap_bytes().swap_bytes(), n); - - // Swapping these should make no difference - let n: $T = 0; assert_eq!(n.swap_bytes(), n); - let n: $T = -1; assert_eq!(n.swap_bytes(), n); - } - #[test] fn test_rotate() { let n: $T = 0b0101100; assert_eq!(n.rotate_left(6).rotate_right(2).rotate_right(4), n); diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index eaa632be6d0..696abc05ed2 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -437,19 +437,6 @@ pub trait Bitwise: Bounded /// ``` fn trailing_zeros(&self) -> Self; - /// Reverses the byte order of a binary number. - /// - /// # Example - /// - /// ```rust - /// use std::num::Bitwise; - /// - /// let n = 0x0123456789ABCDEFu64; - /// let m = 0xEFCDAB8967452301u64; - /// assert_eq!(n.swap_bytes(), m); - /// ``` - fn swap_bytes(&self) -> Self; - /// Shifts the bits to the left by a specified amount amount, `r`, wrapping /// the truncated bits to the end of the resulting value. /// @@ -479,25 +466,17 @@ pub trait Bitwise: Bounded fn rotate_right(&self, r: uint) -> Self; } -/// Swapping a single byte does nothing. This is unsafe to be consistent with -/// the other `bswap` intrinsics. -#[inline] -unsafe fn bswap8(x: u8) -> u8 { x } - -macro_rules! bitwise_impl( - ($t:ty, $bits:expr, $co:ident, $lz:ident, $tz:ident, $bs:path) => { +macro_rules! bitwise_impl { + ($t:ty, $bits:expr, $co:path, $lz:path, $tz:path) => { impl Bitwise for $t { #[inline] - fn count_ones(&self) -> $t { unsafe { intrinsics::$co(*self) } } + fn count_ones(&self) -> $t { unsafe { $co(*self) } } #[inline] - fn leading_zeros(&self) -> $t { unsafe { intrinsics::$lz(*self) } } + fn leading_zeros(&self) -> $t { unsafe { $lz(*self) } } #[inline] - fn trailing_zeros(&self) -> $t { unsafe { intrinsics::$tz(*self) } } - - #[inline] - fn swap_bytes(&self) -> $t { unsafe { $bs(*self) } } + fn trailing_zeros(&self) -> $t { unsafe { $tz(*self) } } #[inline] fn rotate_left(&self, r: uint) -> $t { @@ -514,22 +493,19 @@ macro_rules! bitwise_impl( } } } -) +} -macro_rules! bitwise_cast_impl( - ($t:ty, $t_cast:ty, $bits:expr, $co:ident, $lz:ident, $tz:ident, $bs:path) => { +macro_rules! bitwise_cast_impl { + ($t:ty, $t_cast:ty, $bits:expr, $co:path, $lz:path, $tz:path) => { impl Bitwise for $t { #[inline] - fn count_ones(&self) -> $t { unsafe { intrinsics::$co(*self as $t_cast) as $t } } + fn count_ones(&self) -> $t { unsafe { $co(*self as $t_cast) as $t } } #[inline] - fn leading_zeros(&self) -> $t { unsafe { intrinsics::$lz(*self as $t_cast) as $t } } + fn leading_zeros(&self) -> $t { unsafe { $lz(*self as $t_cast) as $t } } #[inline] - fn trailing_zeros(&self) -> $t { unsafe { intrinsics::$tz(*self as $t_cast) as $t } } - - #[inline] - fn swap_bytes(&self) -> $t { unsafe { $bs(*self as $t_cast) as $t } } + fn trailing_zeros(&self) -> $t { unsafe { $tz(*self as $t_cast) as $t } } #[inline] fn rotate_left(&self, r: uint) -> $t { @@ -544,27 +520,27 @@ macro_rules! bitwise_cast_impl( } } } -) +} #[cfg(target_word_size = "32")] -bitwise_cast_impl!(uint, u32, 32, ctpop32, ctlz32, cttz32, intrinsics::bswap32) +bitwise_cast_impl!(uint, u32, 32, intrinsics::ctpop32, intrinsics::ctlz32, intrinsics::cttz32) #[cfg(target_word_size = "64")] -bitwise_cast_impl!(uint, u64, 64, ctpop64, ctlz64, cttz64, intrinsics::bswap64) +bitwise_cast_impl!(uint, u64, 64, intrinsics::ctpop64, intrinsics::ctlz64, intrinsics::cttz64) -bitwise_impl!(u8, 8, ctpop8, ctlz8, cttz8, bswap8) -bitwise_impl!(u16, 16, ctpop16, ctlz16, cttz16, intrinsics::bswap16) -bitwise_impl!(u32, 32, ctpop32, ctlz32, cttz32, intrinsics::bswap32) -bitwise_impl!(u64, 64, ctpop64, ctlz64, cttz64, intrinsics::bswap64) +bitwise_impl!(u8, 8, intrinsics::ctpop8, intrinsics::ctlz8, intrinsics::cttz8) +bitwise_impl!(u16, 16, intrinsics::ctpop16, intrinsics::ctlz16, intrinsics::cttz16) +bitwise_impl!(u32, 32, intrinsics::ctpop32, intrinsics::ctlz32, intrinsics::cttz32) +bitwise_impl!(u64, 64, intrinsics::ctpop64, intrinsics::ctlz64, intrinsics::cttz64) #[cfg(target_word_size = "32")] -bitwise_cast_impl!(int, u32, 32, ctpop32, ctlz32, cttz32, intrinsics::bswap32) +bitwise_cast_impl!(int, u32, 32, intrinsics::ctpop32, intrinsics::ctlz32, intrinsics::cttz32) #[cfg(target_word_size = "64")] -bitwise_cast_impl!(int, u64, 64, ctpop64, ctlz64, cttz64, intrinsics::bswap64) +bitwise_cast_impl!(int, u64, 64, intrinsics::ctpop64, intrinsics::ctlz64, intrinsics::cttz64) -bitwise_cast_impl!(i8, u8, 8, ctpop8, ctlz8, cttz8, bswap8) -bitwise_cast_impl!(i16, u16, 16, ctpop16, ctlz16, cttz16, intrinsics::bswap16) -bitwise_cast_impl!(i32, u32, 32, ctpop32, ctlz32, cttz32, intrinsics::bswap32) -bitwise_cast_impl!(i64, u64, 64, ctpop64, ctlz64, cttz64, intrinsics::bswap64) +bitwise_cast_impl!(i8, u8, 8, intrinsics::ctpop8, intrinsics::ctlz8, intrinsics::cttz8) +bitwise_cast_impl!(i16, u16, 16, intrinsics::ctpop16, intrinsics::ctlz16, intrinsics::cttz16) +bitwise_cast_impl!(i32, u32, 32, intrinsics::ctpop32, intrinsics::ctlz32, intrinsics::cttz32) +bitwise_cast_impl!(i64, u64, 64, intrinsics::ctpop64, intrinsics::ctlz64, intrinsics::cttz64) /// Specifies the available operations common to all of Rust's core numeric primitives. /// These may not always make sense from a purely mathematical point of view, but diff --git a/src/libcore/num/uint_macros.rs b/src/libcore/num/uint_macros.rs index 8e4ba101542..8548b141053 100644 --- a/src/libcore/num/uint_macros.rs +++ b/src/libcore/num/uint_macros.rs @@ -64,17 +64,6 @@ mod tests { assert!((0b1111001 as $T).count_zeros() == BITS as $T - 5); } - #[test] - fn test_swap_bytes() { - let n: $T = 0b0101100; assert_eq!(n.swap_bytes().swap_bytes(), n); - let n: $T = 0b0100001; assert_eq!(n.swap_bytes().swap_bytes(), n); - let n: $T = 0b1111001; assert_eq!(n.swap_bytes().swap_bytes(), n); - - // Swapping these should make no difference - let n: $T = 0; assert_eq!(n.swap_bytes(), n); - let n: $T = MAX; assert_eq!(n.swap_bytes(), n); - } - #[test] fn test_rotate() { let n: $T = 0b0101100; assert_eq!(n.rotate_left(6).rotate_right(2).rotate_right(4), n);