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_bits(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    // TODO: seems like a clippy issue as `position..=position` would lead to InclusiveRange type.
34    #[allow(clippy::range_plus_one)]
35    fn extract_bit(self, position: u32) -> u64 {
36        self.extract_bits(position..position + 1)
37    }
38
39    fn extract_bits(self, range: Range<u32>) -> u64 {
40        if range.start == 0 && range.end == u64::BITS {
41            return self;
42        }
43        debug_assert!(range.start < range.end);
44        (self >> range.start) & ((1 << range.len()) - 1)
45    }
46
47    fn low_bits(self, num_bits: u32) -> u64 {
48        self & ((1 << num_bits) - 1)
49    }
50
51    fn low_bits_signed(self, num_bits: u32) -> u64 {
52        self.low_bits(num_bits).sign_extend(num_bits - 1)
53    }
54
55    fn sign_extend(self, sign_bit: u32) -> u64 {
56        if self & (1 << sign_bit) != 0 {
57            self | !((2 << sign_bit) - 1)
58        } else {
59            self
60        }
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn test_bit_operations() {
70        assert_eq!(0b11000, 0b1100_0000u64.extract_bits(3..8));
71        assert_eq!(0b1010_1010_0000, 0b10101010_00001111u64.extract_bits(4..16));
72        assert_eq!(u32::MAX, u64::MAX.extract_bits(0..32) as u32);
73    }
74
75    #[test]
76    #[cfg(debug_assertions)]
77    #[should_panic]
78    #[allow(clippy::reversed_empty_ranges)]
79    fn test_extract_bits_wrong_range() {
80        let _ = 0u64.extract_bits(2..1);
81    }
82
83    #[test]
84    #[cfg(debug_assertions)]
85    #[should_panic]
86    fn test_extract_bits_too_large() {
87        let _ = 0u64.extract_bits(0..100);
88    }
89
90    #[test]
91    fn test_sign_extend() {
92        assert_eq!(0u64.sign_extend(5), 0);
93        assert_eq!(31u64.sign_extend(5), 31);
94        assert_eq!(32u64.sign_extend(5) as i64, -32);
95        assert_eq!(33u64.sign_extend(5) as i64, -31);
96        assert_eq!(63u64.sign_extend(5) as i64, -1);
97    }
98}