Skip to main content

linker_utils/
bit_misc.rs

1use std::ops::Range;
2
3// Half-opened range bounded inclusively below and exclusively above: [`start`, `end`)
4#[derive(Clone, Debug, Copy, PartialEq, Eq)]
5pub struct BitRange {
6    pub start: u32,
7    pub end: u32,
8}
9
10pub trait BitExtraction {
11    /// Extract a single bit from the provided `value`.
12    #[must_use]
13    fn extract_bit(self, position: u32) -> u64;
14
15    /// Extract range bits from the provided `value`.
16    #[must_use]
17    fn extract_bit_range(self, range: Range<u32>) -> u64;
18
19    /// Extract the low `num_bits` bits from `self`.
20    #[must_use]
21    fn low_bits(self, num_bits: u32) -> u64;
22
23    /// Extract the low `num_bits` bits from `self`, sign extending from the most significant bit.
24    #[must_use]
25    fn low_bits_signed(self, num_bits: u32) -> u64;
26
27    /// Sign-extend `self` from the given sign bit.
28    #[must_use]
29    fn sign_extend(self, sign_bit: u32) -> u64;
30}
31
32impl BitExtraction for u64 {
33    fn extract_bit(self, position: u32) -> u64 {
34        self.extract_bit_range(position..position + 1)
35    }
36
37    fn extract_bit_range(self, range: Range<u32>) -> u64 {
38        if range.start == 0 && range.end == u64::BITS {
39            return self;
40        }
41        debug_assert!(range.start < range.end);
42        (self >> range.start) & ((1 << range.len()) - 1)
43    }
44
45    fn low_bits(self, num_bits: u32) -> u64 {
46        self & ((1 << num_bits) - 1)
47    }
48
49    fn low_bits_signed(self, num_bits: u32) -> u64 {
50        self.low_bits(num_bits).sign_extend(num_bits - 1)
51    }
52
53    fn sign_extend(self, sign_bit: u32) -> u64 {
54        if self & (1 << sign_bit) != 0 {
55            self | !((2 << sign_bit) - 1)
56        } else {
57            self
58        }
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn test_bit_operations() {
68        assert_eq!(0b11000, 0b1100_0000u64.extract_bit_range(3..8));
69        assert_eq!(
70            0b1010_1010_0000,
71            0b10101010_00001111u64.extract_bit_range(4..16)
72        );
73        assert_eq!(u32::MAX, u64::MAX.extract_bit_range(0..32) as u32);
74    }
75
76    #[test]
77    #[cfg(debug_assertions)]
78    #[should_panic]
79    #[allow(clippy::reversed_empty_ranges)]
80    fn test_extract_bits_wrong_range() {
81        let _ = 0u64.extract_bit_range(2..1);
82    }
83
84    #[test]
85    #[cfg(debug_assertions)]
86    #[should_panic]
87    fn test_extract_bits_too_large() {
88        let _ = 0u64.extract_bit_range(0..100);
89    }
90
91    #[test]
92    fn test_sign_extend() {
93        assert_eq!(0u64.sign_extend(5), 0);
94        assert_eq!(31u64.sign_extend(5), 31);
95        assert_eq!(32u64.sign_extend(5) as i64, -32);
96        assert_eq!(33u64.sign_extend(5) as i64, -31);
97        assert_eq!(63u64.sign_extend(5) as i64, -1);
98    }
99}