1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
//! ## Library Objective
//! This library provides a number of ways to compute the bit reversal of all primitive integers.
//! There are currently 3 different algorithms implemented: Bitwise, Parallel, and Lookup reversal.
//!
//! ## Example
//! ```
//! use bit_reverse::ParallelReverse;
//!
//! assert_eq!(0xA0u8.swap_bits(), 0x05u8);
//! ```
//! This library is very simple to uses just import the crate and the algorithm you want to use.
//! Then you can call swap_bits() on any primitive integer. If you want to try a different
//! algorithm just change the use statement and now your program will use the algorithm instead.
//!
//! ## YMMV Performance Comparison
//! I wouldn't use BitwiseReverse as it is mainly there for completeness and is strictly inferior
//! to ParallelReverse, which is a Bitwise Parallel Reverse and thus an order of magnitude faster.
//! I would recommend you use the ParallelReverse as it performs equal to or better than
//! LookupReverse for all types. Plus as an added bonus doesn't eat your cache with a lookup table.
//!
//! ## Memory Consumption
//! Bitwise uses the least amount of memory only using three integers to compute the reversal.
//! Parallel allocates 3 constants of the same size of the type being reversed.
//! Lookup allocates 256 u8s or 256 bytes to do its byte lookup reversal.
//!
//! ## no_std Compatible
//! To link to core instead of STD, disable default features for this library in your Cargo.toml.
//! [Cargo choosing features](http://doc.crates.io/specifying-dependencies.html#choosing-features)

// This library abuse overflowing literals to be able to use macros to reduce duplicate code.
#![allow(overflowing_literals)]

#![cfg_attr(not(feature = "use_std"), no_std)]

#[cfg(feature = "use_std")]
extern crate std as core;

/// Computes bit reversal by going bit by bit and setting the reverse position bit for the output.
pub trait BitwiseReverse<T> {
    /// Swaps the bits such that bit i is now bit N-i, where N is the length of the T in bits.
    fn swap_bits(self) -> T;
}

macro_rules! doit_bitwise { ($($ty:ty),*) => ($(
    impl BitwiseReverse<$ty> for $ty {
        // This algorithm uses the reverse variable as a like a stack to reverse the value.
        // The lesser significant bits are pushed onto the reverse variable and then the variable
        // is shifted down to make room for more significant bits. This algorithm has a shortcut,
        // that if there aren't anymore 1s to push onto the reverse variable the algorithm ends
        // early and shift the reverse to the correct position.
        #[inline]
        fn swap_bits(self) -> $ty {
            let mut v = self;

            // By initializing the reversal to value, we have already loaded the largest
            // significant bit into the correct location.
            let mut r = self;

            // Compute how many bits are left to shift at the end of the algorithm.
            let mut s = 8 * core::mem::size_of::<$ty>() - 1;

            v >>= 1;
            while v != 0 {  // Quit early if there are no more 1s to shift in
                r <<= 1;    // Make room for the next significant bit
                r |= v & 1; // Add the bit to the reverse variable
                v >>= 1;    // Go to the next significant bit
                s -= 1;     // Decrement the leftover bit count
            }

            // Shift the reversal to the correct position and return the reversal
            return r << s;
        }
    })*)
}

doit_bitwise!(u8, u16, u32, u64, usize);

/// Computes bit reversal by using a divide and conquer approach. Pairs of bits are swapped.
/// Then neighboring bit pairs are swapped. Each time swapping the next largest group of bits.
/// This is done until the entire data has been bit reversed.
pub trait ParallelReverse<T> {
    /// Swaps the bits such that bit i is now bit N-i, where N is the length of the T in bits.
    fn swap_bits(self) -> T;
}

macro_rules! doit_parallel { ($($ty:ty),*) => ($(
    impl ParallelReverse<$ty> for $ty {
        #[inline]
        fn swap_bits(self) -> $ty {
            let mut v = self;
            // Swap odd and even bits
            v = ((v >> 1) & (0x5555555555555555 as $ty)) | ((v & (0x5555555555555555 as $ty)) << 1);
            // Swap consecutive pairs
            v = ((v >> 2) & (0x3333333333333333 as $ty)) | ((v & (0x3333333333333333 as $ty)) << 2);
            // Swap nibbles
            v = ((v >> 4) & (0x0F0F0F0F0F0F0F0F as $ty)) | ((v & (0x0F0F0F0F0F0F0F0F as $ty)) << 4);

            return v.swap_bytes();
        }
    })*)
}

doit_parallel!(u8, u16, u32, u64, usize);

/// Computes bit reversal by using lookup table to translate a single byte into its reverse.
/// For multi-byte types, the byte order is swapped to complete the reversal.
pub trait LookupReverse<T> {
    /// Swaps the bits such that bit i is now bit N-i, where N is the length of the T in bits.
    fn swap_bits(self) -> T;
}

const REVERSE_LOOKUP: [u8; 256] =
    [0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, 8, 136, 72, 200, 40,
     168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, 4, 132, 68, 196, 36, 164, 100, 228, 20,
     148, 84, 212, 52, 180, 116, 244, 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60,
     188, 124, 252, 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, 10,
     138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, 6, 134, 70, 198, 38,
     166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, 14, 142, 78, 206, 46, 174, 110, 238, 30,
     158, 94, 222, 62, 190, 126, 254, 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49,
     177, 113, 241, 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 5,
     133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 13, 141, 77, 205, 45,
     173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 3, 131, 67, 195, 35, 163, 99, 227, 19,
     147, 83, 211, 51, 179, 115, 243, 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59,
     187, 123, 251, 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, 15,
     143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255];

impl LookupReverse<u8> for u8 {
    #[inline]
    fn swap_bits(self) -> u8 {
        unsafe { *REVERSE_LOOKUP.get_unchecked(self as usize) }
    }
}

impl LookupReverse<u16> for u16 {
    #[inline]
    fn swap_bits(self) -> u16 {
        unsafe {
            (*REVERSE_LOOKUP.get_unchecked(self as u8 as usize) as u16) << 8 |
            (*REVERSE_LOOKUP.get_unchecked((self >> 8) as u8 as usize) as u16)
        }
    }
}

impl LookupReverse<u32> for u32 {
    #[inline]
    fn swap_bits(self) -> u32 {
        unsafe {
            (*REVERSE_LOOKUP.get_unchecked(self as u8 as usize) as u32) << 24 |
            (*REVERSE_LOOKUP.get_unchecked((self >> 8) as u8 as usize) as u32) << 16 |
            (*REVERSE_LOOKUP.get_unchecked((self >> 16) as u8 as usize) as u32) << 8 |
            (*REVERSE_LOOKUP.get_unchecked((self >> 24) as u8 as usize) as u32)
        }
    }
}

impl LookupReverse<u64> for u64 {
    #[inline]
    fn swap_bits(self) -> u64 {
        unsafe {
            (*REVERSE_LOOKUP.get_unchecked(self as u8 as usize) as u64) << 56 |
            (*REVERSE_LOOKUP.get_unchecked((self >> 8) as u8 as usize) as u64) << 48 |
            (*REVERSE_LOOKUP.get_unchecked((self >> 16) as u8 as usize) as u64) << 40 |
            (*REVERSE_LOOKUP.get_unchecked((self >> 24) as u8 as usize) as u64) << 32 |
            (*REVERSE_LOOKUP.get_unchecked((self >> 32) as u8 as usize) as u64) << 24 |
            (*REVERSE_LOOKUP.get_unchecked((self >> 40) as u8 as usize) as u64) << 16 |
            (*REVERSE_LOOKUP.get_unchecked((self >> 48) as u8 as usize) as u64) << 8 |
            (*REVERSE_LOOKUP.get_unchecked((self >> 56) as u8 as usize) as u64)
        }
    }
}

impl LookupReverse<usize> for usize {
    #[inline]
    #[cfg(target_pointer_width = "32")]
    fn swap_bits(self) -> usize {
        use LookupReverse;
        LookupReverse::swap_bits(self as u32) as usize
    }

    #[inline]
    #[cfg(target_pointer_width = "64")]
    fn swap_bits(self) -> usize {
        LookupReverse::swap_bits(self as u64) as usize
    }
}

macro_rules! doit_signed {
    ($($Algo:ident),*) => ($(
        impl $Algo<i8> for i8 {
            #[inline]
            fn swap_bits(self) -> i8 {
                $Algo::swap_bits(self as u8) as i8
            }
        }

        impl $Algo<i16> for i16 {
            #[inline]
            fn swap_bits(self) -> i16 {
                $Algo::swap_bits(self as u16) as i16
            }
        }

        impl $Algo<i32> for i32 {
            #[inline]
            fn swap_bits(self) -> i32 {
                $Algo::swap_bits(self as u32) as i32
            }
        }

        impl $Algo<i64> for i64 {
            #[inline]
            fn swap_bits(self) -> i64 {
                $Algo::swap_bits(self as u64) as i64
            }
        }

        impl $Algo<isize> for isize {
            #[inline]
            fn swap_bits(self) -> isize {
                $Algo::swap_bits(self as usize) as isize
            }
        }
    )*)
}

doit_signed!(BitwiseReverse, ParallelReverse, LookupReverse);

macro_rules! test_suite {
    ($name:ident, $algo:path) => (
        #[cfg(test)]
        mod $name {
            use $algo;

            #[test]
            fn reverse_u8() {
                assert_eq!(0xABu8.swap_bits(), 0xD5u8);
            }

            #[test]
            fn reverse_u16() {
                assert_eq!(0xABCDu16.swap_bits(), 0xB3D5u16);
            }

            #[test]
            fn reverse_u32() {
                assert_eq!(0xABCD2345u32.swap_bits(), 0xA2C4B3D5u32);
            }

            #[test]
            fn reverse_u64() {
                assert_eq!(0x0123456789ABCDEFu64.swap_bits(), 0xF7B3D591E6A2C480u64);
            }

            #[test]
            fn reverse_usize() {
                assert_eq!(0xFFusize.swap_bits(), 0xFFusize.swap_bytes());
            }

            #[test]
            fn reverse_i8() {
                assert_eq!(0xABi8.swap_bits(), 0xD5i8);
            }

            #[test]
            fn reverse_i16() {
                assert_eq!(0xABCDi16.swap_bits(), 0xB3D5i16);
            }

            #[test]
            fn reverse_i32() {
                assert_eq!(0xABCD2345i32.swap_bits(), 0xA2C4B3D5i32);
            }

            #[test]
            fn reverse_i64() {
                assert_eq!(0x0123456789ABCDEFi64.swap_bits(), 0xF7B3D591E6A2C480i64);
            }

            #[test]
            fn reverse_isize() {
                assert_eq!(0xFFisize.swap_bits(), 0xFFisize.swap_bytes());
            }
        }
    )
}

test_suite!(test_bitwise_reverse, BitwiseReverse);
test_suite!(test_parallel_reverse, ParallelReverse);
test_suite!(test_lookup_reverse, LookupReverse);