Browse Source

Merge #202

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>
bors[bot] 3 years ago
parent
commit
92298b2a57
2 changed files with 116 additions and 0 deletions
  1. 1 0
      build.rs
  2. 115 0
      src/int.rs

+ 1 - 0
build.rs

@@ -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");

+ 115 - 0
src/int.rs

@@ -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.
@@ -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 {
@@ -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)
@@ -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);
+    }
+}