Skip to content

Commit 92298b2

Browse files
bors[bot]Xiretzacuviper
authored
Merge #202
202: PrimInt: add reverse_bits() method r=cuviper a=Xiretza Co-authored-by: Xiretza <[email protected]> Co-authored-by: Josh Stone <[email protected]>
2 parents ee28ebb + 4ca742a commit 92298b2

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ fn main() {
1616
"has_to_int_unchecked",
1717
);
1818

19+
ac.emit_expression_cfg("1u32.reverse_bits()", "has_reverse_bits");
1920
ac.emit_expression_cfg("1u32.trailing_ones()", "has_leading_trailing_ones");
2021

2122
autocfg::rerun_path("build.rs");

src/int.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,26 @@ pub trait PrimInt:
250250
/// ```
251251
fn swap_bytes(self) -> Self;
252252

253+
/// Reverses the order of bits in the integer.
254+
///
255+
/// The least significant bit becomes the most significant bit, second least-significant bit
256+
/// becomes second most-significant bit, etc.
257+
///
258+
/// # Examples
259+
///
260+
/// ```
261+
/// use num_traits::PrimInt;
262+
///
263+
/// let n = 0x12345678u32;
264+
/// let m = 0x1e6a2c48u32;
265+
///
266+
/// assert_eq!(n.reverse_bits(), m);
267+
/// assert_eq!(0u32.reverse_bits(), 0);
268+
/// ```
269+
fn reverse_bits(self) -> Self {
270+
reverse_bits_fallback(self)
271+
}
272+
253273
/// Convert an integer from big endian to the target's endianness.
254274
///
255275
/// On big endian this is a no-op. On little endian the bytes are swapped.
@@ -338,6 +358,39 @@ pub trait PrimInt:
338358
fn pow(self, exp: u32) -> Self;
339359
}
340360

361+
fn one_per_byte<P: PrimInt>() -> P {
362+
// i8, u8: return 0x01
363+
// i16, u16: return 0x0101 = (0x01 << 8) | 0x01
364+
// i32, u32: return 0x01010101 = (0x0101 << 16) | 0x0101
365+
// ...
366+
let mut ret = P::one();
367+
let mut shift = 8;
368+
let mut b = ret.count_zeros() >> 3;
369+
while b != 0 {
370+
ret = (ret << shift) | ret;
371+
shift <<= 1;
372+
b >>= 1;
373+
}
374+
ret
375+
}
376+
377+
fn reverse_bits_fallback<P: PrimInt>(i: P) -> P {
378+
let rep_01: P = one_per_byte();
379+
let rep_03 = (rep_01 << 1) | rep_01;
380+
let rep_05 = (rep_01 << 2) | rep_01;
381+
let rep_0f = (rep_03 << 2) | rep_03;
382+
let rep_33 = (rep_03 << 4) | rep_03;
383+
let rep_55 = (rep_05 << 4) | rep_05;
384+
385+
// code above only used to determine rep_0f, rep_33, rep_55;
386+
// optimizer should be able to do it in compile time
387+
let mut ret = i.swap_bytes();
388+
ret = ((ret & rep_0f) << 4) | ((ret >> 4) & rep_0f);
389+
ret = ((ret & rep_33) << 2) | ((ret >> 2) & rep_33);
390+
ret = ((ret & rep_55) << 1) | ((ret >> 1) & rep_55);
391+
ret
392+
}
393+
341394
macro_rules! prim_int_impl {
342395
($T:ty, $S:ty, $U:ty) => {
343396
impl PrimInt for $T {
@@ -408,6 +461,12 @@ macro_rules! prim_int_impl {
408461
<$T>::swap_bytes(self)
409462
}
410463

464+
#[cfg(has_reverse_bits)]
465+
#[inline]
466+
fn reverse_bits(self) -> Self {
467+
<$T>::reverse_bits(self)
468+
}
469+
411470
#[inline]
412471
fn from_be(x: Self) -> Self {
413472
<$T>::from_be(x)
@@ -451,3 +510,59 @@ prim_int_impl!(i64, i64, u64);
451510
#[cfg(has_i128)]
452511
prim_int_impl!(i128, i128, u128);
453512
prim_int_impl!(isize, isize, usize);
513+
514+
#[cfg(test)]
515+
mod tests {
516+
use int::PrimInt;
517+
518+
#[test]
519+
pub fn reverse_bits() {
520+
use core::{i16, i32, i64, i8};
521+
522+
assert_eq!(
523+
PrimInt::reverse_bits(0x0123_4567_89ab_cdefu64),
524+
0xf7b3_d591_e6a2_c480
525+
);
526+
527+
assert_eq!(PrimInt::reverse_bits(0i8), 0);
528+
assert_eq!(PrimInt::reverse_bits(-1i8), -1);
529+
assert_eq!(PrimInt::reverse_bits(1i8), i8::MIN);
530+
assert_eq!(PrimInt::reverse_bits(i8::MIN), 1);
531+
assert_eq!(PrimInt::reverse_bits(-2i8), i8::MAX);
532+
assert_eq!(PrimInt::reverse_bits(i8::MAX), -2);
533+
534+
assert_eq!(PrimInt::reverse_bits(0i16), 0);
535+
assert_eq!(PrimInt::reverse_bits(-1i16), -1);
536+
assert_eq!(PrimInt::reverse_bits(1i16), i16::MIN);
537+
assert_eq!(PrimInt::reverse_bits(i16::MIN), 1);
538+
assert_eq!(PrimInt::reverse_bits(-2i16), i16::MAX);
539+
assert_eq!(PrimInt::reverse_bits(i16::MAX), -2);
540+
541+
assert_eq!(PrimInt::reverse_bits(0i32), 0);
542+
assert_eq!(PrimInt::reverse_bits(-1i32), -1);
543+
assert_eq!(PrimInt::reverse_bits(1i32), i32::MIN);
544+
assert_eq!(PrimInt::reverse_bits(i32::MIN), 1);
545+
assert_eq!(PrimInt::reverse_bits(-2i32), i32::MAX);
546+
assert_eq!(PrimInt::reverse_bits(i32::MAX), -2);
547+
548+
assert_eq!(PrimInt::reverse_bits(0i64), 0);
549+
assert_eq!(PrimInt::reverse_bits(-1i64), -1);
550+
assert_eq!(PrimInt::reverse_bits(1i64), i64::MIN);
551+
assert_eq!(PrimInt::reverse_bits(i64::MIN), 1);
552+
assert_eq!(PrimInt::reverse_bits(-2i64), i64::MAX);
553+
assert_eq!(PrimInt::reverse_bits(i64::MAX), -2);
554+
}
555+
556+
#[test]
557+
#[cfg(has_i128)]
558+
pub fn reverse_bits_i128() {
559+
use core::i128;
560+
561+
assert_eq!(PrimInt::reverse_bits(0i128), 0);
562+
assert_eq!(PrimInt::reverse_bits(-1i128), -1);
563+
assert_eq!(PrimInt::reverse_bits(1i128), i128::MIN);
564+
assert_eq!(PrimInt::reverse_bits(i128::MIN), 1);
565+
assert_eq!(PrimInt::reverse_bits(-2i128), i128::MAX);
566+
assert_eq!(PrimInt::reverse_bits(i128::MAX), -2);
567+
}
568+
}

0 commit comments

Comments
 (0)