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_bit_range(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 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}