Skip to content

Commit

Permalink
Add Bitwise::{swap_bytes, rotate_left, rotate_right} methods
Browse files Browse the repository at this point in the history
  • Loading branch information
brendanzab committed Jun 13, 2014
1 parent 021bea1 commit ffa4ae8
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 20 deletions.
27 changes: 27 additions & 0 deletions src/libcore/num/int_macros.rs
Expand Up @@ -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));
Expand Down
118 changes: 98 additions & 20 deletions src/libcore/num/mod.rs
Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions src/libcore/num/uint_macros.rs
Expand Up @@ -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));
Expand Down

5 comments on commit ffa4ae8

@bors
Copy link
Contributor

@bors bors commented on ffa4ae8 Jun 14, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from alexcrichton
at brendanzab@ffa4ae8

@bors
Copy link
Contributor

@bors bors commented on ffa4ae8 Jun 14, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging bjz/rust/bitwise = ffa4ae8 into auto

@bors
Copy link
Contributor

@bors bors commented on ffa4ae8 Jun 14, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bjz/rust/bitwise = ffa4ae8 merged ok, testing candidate = 1cde9d8

@bors
Copy link
Contributor

@bors bors commented on ffa4ae8 Jun 14, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = 1cde9d8

Please sign in to comment.