Skip to content

Commit 1cde9d8

Browse files
committed
auto merge of #14866 : bjz/rust/bitwise, r=alexcrichton
2 parents 3851d68 + ffa4ae8 commit 1cde9d8

File tree

3 files changed

+152
-20
lines changed

3 files changed

+152
-20
lines changed

src/libcore/num/int_macros.rs

+27
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,33 @@ mod tests {
113113
assert!((0b1111001 as $T).count_zeros() == BITS as $T - 5);
114114
}
115115

116+
#[test]
117+
fn test_swap_bytes() {
118+
let n: $T = 0b0101100; assert_eq!(n.swap_bytes().swap_bytes(), n);
119+
let n: $T = 0b0100001; assert_eq!(n.swap_bytes().swap_bytes(), n);
120+
let n: $T = 0b1111001; assert_eq!(n.swap_bytes().swap_bytes(), n);
121+
122+
// Swapping these should make no difference
123+
let n: $T = 0; assert_eq!(n.swap_bytes(), n);
124+
let n: $T = -1; assert_eq!(n.swap_bytes(), n);
125+
}
126+
127+
#[test]
128+
fn test_rotate() {
129+
let n: $T = 0b0101100; assert_eq!(n.rotate_left(6).rotate_right(2).rotate_right(4), n);
130+
let n: $T = 0b0100001; assert_eq!(n.rotate_left(3).rotate_left(2).rotate_right(5), n);
131+
let n: $T = 0b1111001; assert_eq!(n.rotate_left(6).rotate_right(2).rotate_right(4), n);
132+
133+
// Rotating these should make no difference
134+
//
135+
// We test using 124 bits because to ensure that overlong bit shifts do
136+
// not cause undefined behaviour. See #10183.
137+
let n: $T = 0; assert_eq!(n.rotate_left(124), n);
138+
let n: $T = -1; assert_eq!(n.rotate_left(124), n);
139+
let n: $T = 0; assert_eq!(n.rotate_right(124), n);
140+
let n: $T = -1; assert_eq!(n.rotate_right(124), n);
141+
}
142+
116143
#[test]
117144
fn test_signed_checked_div() {
118145
assert!(10i.checked_div(&2) == Some(5));

src/libcore/num/mod.rs

+98-20
Original file line numberDiff line numberDiff line change
@@ -436,57 +436,135 @@ pub trait Bitwise: Bounded
436436
/// assert_eq!(n.trailing_zeros(), 3);
437437
/// ```
438438
fn trailing_zeros(&self) -> Self;
439+
440+
/// Reverses the byte order of a binary number.
441+
///
442+
/// # Example
443+
///
444+
/// ```rust
445+
/// use std::num::Bitwise;
446+
///
447+
/// let n = 0x0123456789ABCDEFu64;
448+
/// let m = 0xEFCDAB8967452301u64;
449+
/// assert_eq!(n.swap_bytes(), m);
450+
/// ```
451+
fn swap_bytes(&self) -> Self;
452+
453+
/// Shifts the bits to the left by a specified amount amount, `r`, wrapping
454+
/// the truncated bits to the end of the resulting value.
455+
///
456+
/// # Example
457+
///
458+
/// ```rust
459+
/// use std::num::Bitwise;
460+
///
461+
/// let n = 0x0123456789ABCDEFu64;
462+
/// let m = 0x3456789ABCDEF012u64;
463+
/// assert_eq!(n.rotate_left(12), m);
464+
/// ```
465+
fn rotate_left(&self, r: uint) -> Self;
466+
467+
/// Shifts the bits to the right by a specified amount amount, `r`, wrapping
468+
/// the truncated bits to the beginning of the resulting value.
469+
///
470+
/// # Example
471+
///
472+
/// ```rust
473+
/// use std::num::Bitwise;
474+
///
475+
/// let n = 0x0123456789ABCDEFu64;
476+
/// let m = 0xDEF0123456789ABCu64;
477+
/// assert_eq!(n.rotate_right(12), m);
478+
/// ```
479+
fn rotate_right(&self, r: uint) -> Self;
439480
}
440481

482+
/// Swapping a single byte does nothing. This is unsafe to be consistent with
483+
/// the other `bswap` intrinsics.
484+
#[inline]
485+
unsafe fn bswap8(x: u8) -> u8 { x }
486+
441487
macro_rules! bitwise_impl(
442-
($t:ty, $co:path, $lz:path, $tz:path) => {
488+
($t:ty, $bits:expr, $co:ident, $lz:ident, $tz:ident, $bs:path) => {
443489
impl Bitwise for $t {
444490
#[inline]
445-
fn count_ones(&self) -> $t { unsafe { $co(*self) } }
491+
fn count_ones(&self) -> $t { unsafe { intrinsics::$co(*self) } }
446492

447493
#[inline]
448-
fn leading_zeros(&self) -> $t { unsafe { $lz(*self) } }
494+
fn leading_zeros(&self) -> $t { unsafe { intrinsics::$lz(*self) } }
449495

450496
#[inline]
451-
fn trailing_zeros(&self) -> $t { unsafe { $tz(*self) } }
497+
fn trailing_zeros(&self) -> $t { unsafe { intrinsics::$tz(*self) } }
498+
499+
#[inline]
500+
fn swap_bytes(&self) -> $t { unsafe { $bs(*self) } }
501+
502+
#[inline]
503+
fn rotate_left(&self, r: uint) -> $t {
504+
// Protect against undefined behaviour for overlong bit shifts
505+
let r = r % $bits;
506+
(*self << r) | (*self >> ($bits - r))
507+
}
508+
509+
#[inline]
510+
fn rotate_right(&self, r: uint) -> $t {
511+
// Protect against undefined behaviour for overlong bit shifts
512+
let r = r % $bits;
513+
(*self >> r) | (*self << ($bits - r))
514+
}
452515
}
453516
}
454517
)
455518

456519
macro_rules! bitwise_cast_impl(
457-
($t:ty, $t_cast:ty, $co:path, $lz:path, $tz:path) => {
520+
($t:ty, $t_cast:ty, $bits:expr, $co:ident, $lz:ident, $tz:ident, $bs:path) => {
458521
impl Bitwise for $t {
459522
#[inline]
460-
fn count_ones(&self) -> $t { unsafe { $co(*self as $t_cast) as $t } }
523+
fn count_ones(&self) -> $t { unsafe { intrinsics::$co(*self as $t_cast) as $t } }
524+
525+
#[inline]
526+
fn leading_zeros(&self) -> $t { unsafe { intrinsics::$lz(*self as $t_cast) as $t } }
527+
528+
#[inline]
529+
fn trailing_zeros(&self) -> $t { unsafe { intrinsics::$tz(*self as $t_cast) as $t } }
461530

462531
#[inline]
463-
fn leading_zeros(&self) -> $t { unsafe { $lz(*self as $t_cast) as $t } }
532+
fn swap_bytes(&self) -> $t { unsafe { $bs(*self as $t_cast) as $t } }
464533

465534
#[inline]
466-
fn trailing_zeros(&self) -> $t { unsafe { $tz(*self as $t_cast) as $t } }
535+
fn rotate_left(&self, r: uint) -> $t {
536+
// cast to prevent the sign bit from being corrupted
537+
(*self as $t_cast).rotate_left(r) as $t
538+
}
539+
540+
#[inline]
541+
fn rotate_right(&self, r: uint) -> $t {
542+
// cast to prevent the sign bit from being corrupted
543+
(*self as $t_cast).rotate_right(r) as $t
544+
}
467545
}
468546
}
469547
)
470548

471549
#[cfg(target_word_size = "32")]
472-
bitwise_cast_impl!(uint, u32, intrinsics::ctpop32, intrinsics::ctlz32, intrinsics::cttz32)
550+
bitwise_cast_impl!(uint, u32, 32, ctpop32, ctlz32, cttz32, intrinsics::bswap32)
473551
#[cfg(target_word_size = "64")]
474-
bitwise_cast_impl!(uint, u64, intrinsics::ctpop64, intrinsics::ctlz64, intrinsics::cttz64)
552+
bitwise_cast_impl!(uint, u64, 64, ctpop64, ctlz64, cttz64, intrinsics::bswap64)
475553

476-
bitwise_impl!(u8, intrinsics::ctpop8, intrinsics::ctlz8, intrinsics::cttz8)
477-
bitwise_impl!(u16, intrinsics::ctpop16, intrinsics::ctlz16, intrinsics::cttz16)
478-
bitwise_impl!(u32, intrinsics::ctpop32, intrinsics::ctlz32, intrinsics::cttz32)
479-
bitwise_impl!(u64, intrinsics::ctpop64, intrinsics::ctlz64, intrinsics::cttz64)
554+
bitwise_impl!(u8, 8, ctpop8, ctlz8, cttz8, bswap8)
555+
bitwise_impl!(u16, 16, ctpop16, ctlz16, cttz16, intrinsics::bswap16)
556+
bitwise_impl!(u32, 32, ctpop32, ctlz32, cttz32, intrinsics::bswap32)
557+
bitwise_impl!(u64, 64, ctpop64, ctlz64, cttz64, intrinsics::bswap64)
480558

481559
#[cfg(target_word_size = "32")]
482-
bitwise_cast_impl!(int, u32, intrinsics::ctpop32, intrinsics::ctlz32, intrinsics::cttz32)
560+
bitwise_cast_impl!(int, u32, 32, ctpop32, ctlz32, cttz32, intrinsics::bswap32)
483561
#[cfg(target_word_size = "64")]
484-
bitwise_cast_impl!(int, u64, intrinsics::ctpop64, intrinsics::ctlz64, intrinsics::cttz64)
562+
bitwise_cast_impl!(int, u64, 64, ctpop64, ctlz64, cttz64, intrinsics::bswap64)
485563

486-
bitwise_cast_impl!(i8, u8, intrinsics::ctpop8, intrinsics::ctlz8, intrinsics::cttz8)
487-
bitwise_cast_impl!(i16, u16, intrinsics::ctpop16, intrinsics::ctlz16, intrinsics::cttz16)
488-
bitwise_cast_impl!(i32, u32, intrinsics::ctpop32, intrinsics::ctlz32, intrinsics::cttz32)
489-
bitwise_cast_impl!(i64, u64, intrinsics::ctpop64, intrinsics::ctlz64, intrinsics::cttz64)
564+
bitwise_cast_impl!(i8, u8, 8, ctpop8, ctlz8, cttz8, bswap8)
565+
bitwise_cast_impl!(i16, u16, 16, ctpop16, ctlz16, cttz16, intrinsics::bswap16)
566+
bitwise_cast_impl!(i32, u32, 32, ctpop32, ctlz32, cttz32, intrinsics::bswap32)
567+
bitwise_cast_impl!(i64, u64, 64, ctpop64, ctlz64, cttz64, intrinsics::bswap64)
490568

491569
/// Specifies the available operations common to all of Rust's core numeric primitives.
492570
/// These may not always make sense from a purely mathematical point of view, but

src/libcore/num/uint_macros.rs

+27
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,33 @@ mod tests {
6464
assert!((0b1111001 as $T).count_zeros() == BITS as $T - 5);
6565
}
6666

67+
#[test]
68+
fn test_swap_bytes() {
69+
let n: $T = 0b0101100; assert_eq!(n.swap_bytes().swap_bytes(), n);
70+
let n: $T = 0b0100001; assert_eq!(n.swap_bytes().swap_bytes(), n);
71+
let n: $T = 0b1111001; assert_eq!(n.swap_bytes().swap_bytes(), n);
72+
73+
// Swapping these should make no difference
74+
let n: $T = 0; assert_eq!(n.swap_bytes(), n);
75+
let n: $T = MAX; assert_eq!(n.swap_bytes(), n);
76+
}
77+
78+
#[test]
79+
fn test_rotate() {
80+
let n: $T = 0b0101100; assert_eq!(n.rotate_left(6).rotate_right(2).rotate_right(4), n);
81+
let n: $T = 0b0100001; assert_eq!(n.rotate_left(3).rotate_left(2).rotate_right(5), n);
82+
let n: $T = 0b1111001; assert_eq!(n.rotate_left(6).rotate_right(2).rotate_right(4), n);
83+
84+
// Rotating these should make no difference
85+
//
86+
// We test using 124 bits because to ensure that overlong bit shifts do
87+
// not cause undefined behaviour. See #10183.
88+
let n: $T = 0; assert_eq!(n.rotate_left(124), n);
89+
let n: $T = MAX; assert_eq!(n.rotate_left(124), n);
90+
let n: $T = 0; assert_eq!(n.rotate_right(124), n);
91+
let n: $T = MAX; assert_eq!(n.rotate_right(124), n);
92+
}
93+
6794
#[test]
6895
fn test_unsigned_checked_div() {
6996
assert!(10u.checked_div(&2) == Some(5));

0 commit comments

Comments
 (0)