diff --git a/src/libcore/num/int_macros.rs b/src/libcore/num/int_macros.rs index 445ac248cd67f..20bb12db694c0 100644 --- a/src/libcore/num/int_macros.rs +++ b/src/libcore/num/int_macros.rs @@ -113,6 +113,33 @@ 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); + let n: $T = 0b0100001; assert_eq!(n.rotate_left(3).rotate_left(2).rotate_right(5), n); + let n: $T = 0b1111001; assert_eq!(n.rotate_left(6).rotate_right(2).rotate_right(4), n); + + // Rotating these should make no difference + // + // We test using 124 bits because to ensure that overlong bit shifts do + // not cause undefined behaviour. See #10183. + let n: $T = 0; assert_eq!(n.rotate_left(124), n); + let n: $T = -1; assert_eq!(n.rotate_left(124), n); + let n: $T = 0; assert_eq!(n.rotate_right(124), n); + let n: $T = -1; assert_eq!(n.rotate_right(124), n); + } + #[test] fn test_signed_checked_div() { assert!(10i.checked_div(&2) == Some(5)); diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 3a847004df909..eaa632be6d04c 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -436,57 +436,135 @@ pub trait Bitwise: Bounded /// assert_eq!(n.trailing_zeros(), 3); /// ``` 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. + /// + /// # Example + /// + /// ```rust + /// use std::num::Bitwise; + /// + /// let n = 0x0123456789ABCDEFu64; + /// let m = 0x3456789ABCDEF012u64; + /// assert_eq!(n.rotate_left(12), m); + /// ``` + fn rotate_left(&self, r: uint) -> Self; + + /// Shifts the bits to the right by a specified amount amount, `r`, wrapping + /// the truncated bits to the beginning of the resulting value. + /// + /// # Example + /// + /// ```rust + /// use std::num::Bitwise; + /// + /// let n = 0x0123456789ABCDEFu64; + /// let m = 0xDEF0123456789ABCu64; + /// assert_eq!(n.rotate_right(12), m); + /// ``` + 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, $co:path, $lz:path, $tz:path) => { + ($t:ty, $bits:expr, $co:ident, $lz:ident, $tz:ident, $bs:path) => { impl Bitwise for $t { #[inline] - fn count_ones(&self) -> $t { unsafe { $co(*self) } } + fn count_ones(&self) -> $t { unsafe { intrinsics::$co(*self) } } #[inline] - fn leading_zeros(&self) -> $t { unsafe { $lz(*self) } } + fn leading_zeros(&self) -> $t { unsafe { intrinsics::$lz(*self) } } #[inline] - fn trailing_zeros(&self) -> $t { unsafe { $tz(*self) } } + fn trailing_zeros(&self) -> $t { unsafe { intrinsics::$tz(*self) } } + + #[inline] + fn swap_bytes(&self) -> $t { unsafe { $bs(*self) } } + + #[inline] + fn rotate_left(&self, r: uint) -> $t { + // Protect against undefined behaviour for overlong bit shifts + let r = r % $bits; + (*self << r) | (*self >> ($bits - r)) + } + + #[inline] + fn rotate_right(&self, r: uint) -> $t { + // Protect against undefined behaviour for overlong bit shifts + let r = r % $bits; + (*self >> r) | (*self << ($bits - r)) + } } } ) macro_rules! bitwise_cast_impl( - ($t:ty, $t_cast:ty, $co:path, $lz:path, $tz:path) => { + ($t:ty, $t_cast:ty, $bits:expr, $co:ident, $lz:ident, $tz:ident, $bs:path) => { impl Bitwise for $t { #[inline] - fn count_ones(&self) -> $t { unsafe { $co(*self as $t_cast) as $t } } + fn count_ones(&self) -> $t { unsafe { intrinsics::$co(*self as $t_cast) as $t } } + + #[inline] + fn leading_zeros(&self) -> $t { unsafe { intrinsics::$lz(*self as $t_cast) as $t } } + + #[inline] + fn trailing_zeros(&self) -> $t { unsafe { intrinsics::$tz(*self as $t_cast) as $t } } #[inline] - fn leading_zeros(&self) -> $t { unsafe { $lz(*self as $t_cast) as $t } } + fn swap_bytes(&self) -> $t { unsafe { $bs(*self as $t_cast) as $t } } #[inline] - fn trailing_zeros(&self) -> $t { unsafe { $tz(*self as $t_cast) as $t } } + fn rotate_left(&self, r: uint) -> $t { + // cast to prevent the sign bit from being corrupted + (*self as $t_cast).rotate_left(r) as $t + } + + #[inline] + fn rotate_right(&self, r: uint) -> $t { + // cast to prevent the sign bit from being corrupted + (*self as $t_cast).rotate_right(r) as $t + } } } ) #[cfg(target_word_size = "32")] -bitwise_cast_impl!(uint, u32, intrinsics::ctpop32, intrinsics::ctlz32, intrinsics::cttz32) +bitwise_cast_impl!(uint, u32, 32, ctpop32, ctlz32, cttz32, intrinsics::bswap32) #[cfg(target_word_size = "64")] -bitwise_cast_impl!(uint, u64, intrinsics::ctpop64, intrinsics::ctlz64, intrinsics::cttz64) +bitwise_cast_impl!(uint, u64, 64, ctpop64, ctlz64, cttz64, intrinsics::bswap64) -bitwise_impl!(u8, intrinsics::ctpop8, intrinsics::ctlz8, intrinsics::cttz8) -bitwise_impl!(u16, intrinsics::ctpop16, intrinsics::ctlz16, intrinsics::cttz16) -bitwise_impl!(u32, intrinsics::ctpop32, intrinsics::ctlz32, intrinsics::cttz32) -bitwise_impl!(u64, 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) #[cfg(target_word_size = "32")] -bitwise_cast_impl!(int, u32, intrinsics::ctpop32, intrinsics::ctlz32, intrinsics::cttz32) +bitwise_cast_impl!(int, u32, 32, ctpop32, ctlz32, cttz32, intrinsics::bswap32) #[cfg(target_word_size = "64")] -bitwise_cast_impl!(int, u64, intrinsics::ctpop64, intrinsics::ctlz64, intrinsics::cttz64) +bitwise_cast_impl!(int, u64, 64, ctpop64, ctlz64, cttz64, intrinsics::bswap64) -bitwise_cast_impl!(i8, u8, intrinsics::ctpop8, intrinsics::ctlz8, intrinsics::cttz8) -bitwise_cast_impl!(i16, u16, intrinsics::ctpop16, intrinsics::ctlz16, intrinsics::cttz16) -bitwise_cast_impl!(i32, u32, intrinsics::ctpop32, intrinsics::ctlz32, intrinsics::cttz32) -bitwise_cast_impl!(i64, u64, 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) /// 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 0e094b9ec3143..8e4ba10154244 100644 --- a/src/libcore/num/uint_macros.rs +++ b/src/libcore/num/uint_macros.rs @@ -64,6 +64,33 @@ 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); + let n: $T = 0b0100001; assert_eq!(n.rotate_left(3).rotate_left(2).rotate_right(5), n); + let n: $T = 0b1111001; assert_eq!(n.rotate_left(6).rotate_right(2).rotate_right(4), n); + + // Rotating these should make no difference + // + // We test using 124 bits because to ensure that overlong bit shifts do + // not cause undefined behaviour. See #10183. + let n: $T = 0; assert_eq!(n.rotate_left(124), n); + let n: $T = MAX; assert_eq!(n.rotate_left(124), n); + let n: $T = 0; assert_eq!(n.rotate_right(124), n); + let n: $T = MAX; assert_eq!(n.rotate_right(124), n); + } + #[test] fn test_unsigned_checked_div() { assert!(10u.checked_div(&2) == Some(5));