Skip to content

Commit

Permalink
Merge #202
Browse files Browse the repository at this point in the history
202: PrimInt: add reverse_bits() method r=cuviper a=Xiretza



Co-authored-by: Xiretza <xiretza@xiretza.xyz>
Co-authored-by: Josh Stone <cuviper@gmail.com>
  • Loading branch information
3 people committed Jun 16, 2021
2 parents ee28ebb + 4ca742a commit 92298b2
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 0 deletions.
1 change: 1 addition & 0 deletions build.rs
Expand Up @@ -16,6 +16,7 @@ fn main() {
"has_to_int_unchecked",
);

ac.emit_expression_cfg("1u32.reverse_bits()", "has_reverse_bits");
ac.emit_expression_cfg("1u32.trailing_ones()", "has_leading_trailing_ones");

autocfg::rerun_path("build.rs");
Expand Down
115 changes: 115 additions & 0 deletions src/int.rs
Expand Up @@ -250,6 +250,26 @@ pub trait PrimInt:
/// ```
fn swap_bytes(self) -> Self;

/// Reverses the order of bits in the integer.
///
/// The least significant bit becomes the most significant bit, second least-significant bit
/// becomes second most-significant bit, etc.
///
/// # Examples
///
/// ```
/// use num_traits::PrimInt;
///
/// let n = 0x12345678u32;
/// let m = 0x1e6a2c48u32;
///
/// assert_eq!(n.reverse_bits(), m);
/// assert_eq!(0u32.reverse_bits(), 0);
/// ```
fn reverse_bits(self) -> Self {
reverse_bits_fallback(self)
}

/// Convert an integer from big endian to the target's endianness.
///
/// On big endian this is a no-op. On little endian the bytes are swapped.
Expand Down Expand Up @@ -338,6 +358,39 @@ pub trait PrimInt:
fn pow(self, exp: u32) -> Self;
}

fn one_per_byte<P: PrimInt>() -> P {
// i8, u8: return 0x01
// i16, u16: return 0x0101 = (0x01 << 8) | 0x01
// i32, u32: return 0x01010101 = (0x0101 << 16) | 0x0101
// ...
let mut ret = P::one();
let mut shift = 8;
let mut b = ret.count_zeros() >> 3;
while b != 0 {
ret = (ret << shift) | ret;
shift <<= 1;
b >>= 1;
}
ret
}

fn reverse_bits_fallback<P: PrimInt>(i: P) -> P {
let rep_01: P = one_per_byte();
let rep_03 = (rep_01 << 1) | rep_01;
let rep_05 = (rep_01 << 2) | rep_01;
let rep_0f = (rep_03 << 2) | rep_03;
let rep_33 = (rep_03 << 4) | rep_03;
let rep_55 = (rep_05 << 4) | rep_05;

// code above only used to determine rep_0f, rep_33, rep_55;
// optimizer should be able to do it in compile time
let mut ret = i.swap_bytes();
ret = ((ret & rep_0f) << 4) | ((ret >> 4) & rep_0f);
ret = ((ret & rep_33) << 2) | ((ret >> 2) & rep_33);
ret = ((ret & rep_55) << 1) | ((ret >> 1) & rep_55);
ret
}

macro_rules! prim_int_impl {
($T:ty, $S:ty, $U:ty) => {
impl PrimInt for $T {
Expand Down Expand Up @@ -408,6 +461,12 @@ macro_rules! prim_int_impl {
<$T>::swap_bytes(self)
}

#[cfg(has_reverse_bits)]
#[inline]
fn reverse_bits(self) -> Self {
<$T>::reverse_bits(self)
}

#[inline]
fn from_be(x: Self) -> Self {
<$T>::from_be(x)
Expand Down Expand Up @@ -451,3 +510,59 @@ prim_int_impl!(i64, i64, u64);
#[cfg(has_i128)]
prim_int_impl!(i128, i128, u128);
prim_int_impl!(isize, isize, usize);

#[cfg(test)]
mod tests {
use int::PrimInt;

#[test]
pub fn reverse_bits() {
use core::{i16, i32, i64, i8};

assert_eq!(
PrimInt::reverse_bits(0x0123_4567_89ab_cdefu64),
0xf7b3_d591_e6a2_c480
);

assert_eq!(PrimInt::reverse_bits(0i8), 0);
assert_eq!(PrimInt::reverse_bits(-1i8), -1);
assert_eq!(PrimInt::reverse_bits(1i8), i8::MIN);
assert_eq!(PrimInt::reverse_bits(i8::MIN), 1);
assert_eq!(PrimInt::reverse_bits(-2i8), i8::MAX);
assert_eq!(PrimInt::reverse_bits(i8::MAX), -2);

assert_eq!(PrimInt::reverse_bits(0i16), 0);
assert_eq!(PrimInt::reverse_bits(-1i16), -1);
assert_eq!(PrimInt::reverse_bits(1i16), i16::MIN);
assert_eq!(PrimInt::reverse_bits(i16::MIN), 1);
assert_eq!(PrimInt::reverse_bits(-2i16), i16::MAX);
assert_eq!(PrimInt::reverse_bits(i16::MAX), -2);

assert_eq!(PrimInt::reverse_bits(0i32), 0);
assert_eq!(PrimInt::reverse_bits(-1i32), -1);
assert_eq!(PrimInt::reverse_bits(1i32), i32::MIN);
assert_eq!(PrimInt::reverse_bits(i32::MIN), 1);
assert_eq!(PrimInt::reverse_bits(-2i32), i32::MAX);
assert_eq!(PrimInt::reverse_bits(i32::MAX), -2);

assert_eq!(PrimInt::reverse_bits(0i64), 0);
assert_eq!(PrimInt::reverse_bits(-1i64), -1);
assert_eq!(PrimInt::reverse_bits(1i64), i64::MIN);
assert_eq!(PrimInt::reverse_bits(i64::MIN), 1);
assert_eq!(PrimInt::reverse_bits(-2i64), i64::MAX);
assert_eq!(PrimInt::reverse_bits(i64::MAX), -2);
}

#[test]
#[cfg(has_i128)]
pub fn reverse_bits_i128() {
use core::i128;

assert_eq!(PrimInt::reverse_bits(0i128), 0);
assert_eq!(PrimInt::reverse_bits(-1i128), -1);
assert_eq!(PrimInt::reverse_bits(1i128), i128::MIN);
assert_eq!(PrimInt::reverse_bits(i128::MIN), 1);
assert_eq!(PrimInt::reverse_bits(-2i128), i128::MAX);
assert_eq!(PrimInt::reverse_bits(i128::MAX), -2);
}
}

0 comments on commit 92298b2

Please sign in to comment.