From 9c6f1489880ce7e8874fdd4a67e93c26c15ab30b Mon Sep 17 00:00:00 2001 From: Xiretza Date: Fri, 12 Feb 2021 12:50:56 +0100 Subject: [PATCH 1/3] PrimInt: add reverse_bits() method --- src/int.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/int.rs b/src/int.rs index 10e751a9..15bea1b7 100644 --- a/src/int.rs +++ b/src/int.rs @@ -218,6 +218,24 @@ 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; + /// 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. @@ -364,6 +382,11 @@ macro_rules! prim_int_impl { <$T>::swap_bytes(self) } + #[inline] + fn reverse_bits(self) -> Self { + <$T>::reverse_bits(self) + } + #[inline] fn from_be(x: Self) -> Self { <$T>::from_be(x) From 502426f7e074eef70c5597635b9a89af7b265850 Mon Sep 17 00:00:00 2001 From: Xiretza Date: Fri, 26 Feb 2021 11:26:26 +0100 Subject: [PATCH 2/3] PrimInt: add fallback for reverse_bits on rustc<1.37 --- build.rs | 2 ++ src/int.rs | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 816ddadd..1717ee5f 100644 --- a/build.rs +++ b/build.rs @@ -16,5 +16,7 @@ fn main() { "has_to_int_unchecked", ); + ac.emit_expression_cfg("1u32.reverse_bits()", "has_reverse_bits"); + autocfg::rerun_path("build.rs"); } diff --git a/src/int.rs b/src/int.rs index 15bea1b7..0d099e28 100644 --- a/src/int.rs +++ b/src/int.rs @@ -234,7 +234,9 @@ pub trait PrimInt: /// assert_eq!(n.reverse_bits(), m); /// assert_eq!(0u32.reverse_bits(), 0); /// ``` - fn reverse_bits(self) -> Self; + fn reverse_bits(self) -> Self { + reverse_bits_fallback(self) + } /// Convert an integer from big endian to the target's endianness. /// @@ -324,6 +326,39 @@ pub trait PrimInt: fn pow(self, exp: u32) -> Self; } +fn one_per_byte() -> 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(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 { @@ -382,6 +417,7 @@ macro_rules! prim_int_impl { <$T>::swap_bytes(self) } + #[cfg(has_reverse_bits)] #[inline] fn reverse_bits(self) -> Self { <$T>::reverse_bits(self) From 94761f71a734d40e3dda9c84150d02c8c07fa998 Mon Sep 17 00:00:00 2001 From: Xiretza Date: Tue, 13 Apr 2021 21:51:57 +0200 Subject: [PATCH 3/3] PrimInt: add unit tests for reverse_bits --- src/int.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/int.rs b/src/int.rs index 0d099e28..086bd2eb 100644 --- a/src/int.rs +++ b/src/int.rs @@ -466,3 +466,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); + } +}