kdmp_parser/
bits.rs

1// Axel '0vercl0k' Souchet - January 21 2024
2//! This defines and implements the [`Bits`] trait which allows user to extract
3//! bit / range of bits off regular integer.
4//!
5//! //! # Examples
6//!
7//! ```
8//! # use kdmp_parser::bits::Bits;
9//! let v = 0xAB_CD_EF_01_23_45_67_89u64;
10//! assert_eq!(v.bits(0..=63), v);
11//! assert_eq!(v.bits(0..=55), 0xCD_EF_01_23_45_67_89);
12//! ```
13use std::mem;
14use std::ops::RangeInclusive;
15
16/// Utility trait to make it easier to extract ranges of bits.
17pub trait Bits: Sized {
18    /// Get a range of bits.
19    #[must_use]
20    fn bits(&self, r: RangeInclusive<usize>) -> Self;
21
22    /// Get a bit.
23    #[must_use]
24    fn bit(&self, n: usize) -> Self {
25        self.bits(n..=n)
26    }
27}
28
29impl<T> Bits for T
30where
31    T: Into<u128> + Copy + TryFrom<u128>,
32    <T as TryFrom<u128>>::Error: std::fmt::Debug,
33{
34    fn bits(&self, r: RangeInclusive<usize>) -> Self {
35        let (start, end) = r.into_inner();
36        let capacity = mem::size_of_val(self) * 8;
37        assert!(start <= end, "the range should have a start <= end");
38        assert!(
39            end < capacity,
40            "the end ({end}) of the range can't exceed the bits capacity ({capacity}) of Self"
41        );
42        let value = (*self).into();
43        let base = value >> start;
44        let n = end - start + 1;
45
46        let mask = if n == capacity {
47            // This prevents to overflow a u128 when doing `(1 << 128) - 1`
48            !0
49        } else {
50            (1 << n) - 1
51        };
52
53        // This cannot fail as we are zero extending `Self` into a `u128` and then the
54        // `mask` cannot index outside the bit capacity of `Self`.
55        T::try_from(base & mask).unwrap()
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::Bits;
62
63    #[test]
64    fn bits64() {
65        let v = 0xAB_CD_EF_01_23_45_67_89u64;
66        assert_eq!(v.bits(0..=63), v);
67        assert_eq!(v.bits(0..=55), 0xCD_EF_01_23_45_67_89);
68        assert_eq!(v.bits(0..=47), 0xEF_01_23_45_67_89);
69        assert_eq!(v.bits(0..=39), 0x01_23_45_67_89);
70        assert_eq!(v.bits(0..=31), 0x23_45_67_89);
71        assert_eq!(v.bits(0..=23), 0x45_67_89);
72        assert_eq!(v.bits(0..=15), 0x67_89);
73        assert_eq!(v.bits(0..=7), 0x89);
74        assert_eq!(v.bits(0..=3), 0x9);
75
76        assert_eq!(v.bits(0..=7), 0x89);
77        assert_eq!(v.bits(8..=15), 0x67);
78        assert_eq!(v.bits(16..=23), 0x45);
79        assert_eq!(v.bits(24..=31), 0x23);
80        assert_eq!(v.bits(32..=39), 0x01);
81        assert_eq!(v.bits(40..=47), 0xEF);
82        assert_eq!(v.bits(48..=55), 0xCD);
83        assert_eq!(v.bits(56..=63), 0xAB);
84        assert_eq!(v.bit(0), 1);
85    }
86
87    #[test]
88    fn bits128() {
89        let v = 0xAB_CD_EF_01_23_45_67_89u128;
90        assert_eq!(v.bits(0..=125), v);
91        assert_eq!(v.bits(0..=55), 0xCD_EF_01_23_45_67_89);
92        assert_eq!(v.bits(0..=47), 0xEF_01_23_45_67_89);
93        assert_eq!(v.bits(0..=39), 0x01_23_45_67_89);
94        assert_eq!(v.bits(0..=31), 0x23_45_67_89);
95        assert_eq!(v.bits(0..=23), 0x45_67_89);
96        assert_eq!(v.bits(0..=15), 0x67_89);
97        assert_eq!(v.bits(0..=7), 0x89);
98        assert_eq!(v.bits(0..=3), 0x9);
99
100        assert_eq!(v.bits(0..=7), 0x89);
101        assert_eq!(v.bits(8..=15), 0x67);
102        assert_eq!(v.bits(16..=23), 0x45);
103        assert_eq!(v.bits(24..=31), 0x23);
104        assert_eq!(v.bits(32..=39), 0x01);
105        assert_eq!(v.bits(40..=47), 0xEF);
106        assert_eq!(v.bits(48..=55), 0xCD);
107        assert_eq!(v.bits(56..=63), 0xAB);
108    }
109
110    #[test]
111    #[allow(clippy::reversed_empty_ranges)]
112    fn invalid_ranges() {
113        assert!(std::panic::catch_unwind(|| 1u64.bits(10..=0)).is_err());
114        assert!(std::panic::catch_unwind(|| 1u128.bits(0..=128)).is_err());
115        assert!(std::panic::catch_unwind(|| 1u64.bits(0..=64)).is_err());
116    }
117}