1use std::ops::Range;
2
3#[derive(Clone, Debug, Copy, PartialEq, Eq)]
5pub struct BitRange {
6 pub start: u32,
7 pub end: u32,
8}
9
10pub trait BitExtraction {
11 #[must_use]
13 fn extract_bit(self, position: u32) -> u64;
14
15 #[must_use]
17 fn extract_bits(self, range: Range<u32>) -> u64;
18
19 #[must_use]
21 fn low_bits(self, num_bits: u32) -> u64;
22
23 #[must_use]
25 fn low_bits_signed(self, num_bits: u32) -> u64;
26
27 #[must_use]
29 fn sign_extend(self, sign_bit: u32) -> u64;
30}
31
32impl BitExtraction for u64 {
33 #[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}