Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PrimInt: add reverse_bits() method #202

Merged
merged 4 commits into from Jun 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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);
}
}