aarchmrs_types/
bit_value.rs

1/* Copyright (C) 2025 Ivan Boldyrev
2 *
3 * This document is licensed under the BSD 3-clause license.
4 */
5
6/// A wrapper for restricted bit-sized value with debug assert.
7///
8/// The idea is to make it validate in debug only, in the release it is zero-cost, and the caller is responsible
9/// for the validity of values.
10#[derive(Copy, Clone, Debug, Default, Hash, PartialEq, Eq)]
11pub struct BitValue<const BITS: u32>(pub u32);
12
13impl<const BITS: u32> BitValue<BITS> {
14    #[inline]
15    pub const fn new_u32(nested: u32) -> Self {
16        debug_assert!({
17            let upper_bits = i32::BITS - BITS;
18            nested == (nested << upper_bits) >> upper_bits
19        });
20
21        Self(nested)
22    }
23
24    #[inline]
25    pub const fn new_i32(nested: i32) -> Self {
26        let upper_bits = i32::BITS - BITS;
27        // debug_assert_eq is not const-compatible, debug_assert does work though
28        // it is not documented as const-compatible.
29        debug_assert!(nested == ((nested << upper_bits) >> upper_bits));
30
31        let mask = u32::MAX >> upper_bits;
32        let nested = nested as u32;
33        Self(nested & mask)
34    }
35
36    #[inline]
37    pub const fn into_inner(self) -> u32 {
38        self.0
39    }
40}
41
42impl<const BITS: u32> From<BitValue<BITS>> for u32 {
43    #[inline]
44    fn from(value: BitValue<BITS>) -> Self {
45        value.0
46    }
47}
48
49impl<const BITS: u32> From<u32> for BitValue<BITS> {
50    #[inline]
51    fn from(value: u32) -> Self {
52        Self::new_u32(value)
53    }
54}
55
56impl<const BITS: u32> From<i32> for BitValue<BITS> {
57    #[inline]
58    fn from(value: i32) -> Self {
59        Self::new_i32(value)
60    }
61}
62
63impl<const BITS: u32> From<u16> for BitValue<BITS> {
64    #[inline]
65    fn from(value: u16) -> Self {
66        Self::new_u32(value as _)
67    }
68}
69
70impl<const BITS: u32> From<i16> for BitValue<BITS> {
71    #[inline]
72    fn from(value: i16) -> Self {
73        Self::new_i32(value as _)
74    }
75}
76
77impl<const BITS: u32> From<u8> for BitValue<BITS> {
78    #[inline]
79    fn from(value: u8) -> Self {
80        Self::new_u32(value as _)
81    }
82}
83
84impl<const BITS: u32> From<i8> for BitValue<BITS> {
85    #[inline]
86    fn from(value: i8) -> Self {
87        Self::new_i32(value as _)
88    }
89}
90
91impl From<bool> for BitValue<1> {
92    #[inline]
93    fn from(value: bool) -> Self {
94        Self::new_u32(value as u32)
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_valid_u32() {
104        let _ = BitValue::<8>::new_u32(255);
105    }
106
107    #[test]
108    fn test_valid_u32_1() {
109        let _ = BitValue::<8>::new_u32(42);
110    }
111
112    #[test]
113    #[cfg_attr(debug_assertions, should_panic)]
114    fn test_invalid_u32() {
115        let _ = BitValue::<8>::new_u32(256);
116    }
117
118    #[test]
119    fn test_valid_i32() {
120        let _ = BitValue::<8>::new_i32(42);
121    }
122
123    #[test]
124    fn test_valid_i32_max() {
125        let _ = BitValue::<8>::new_i32(i8::MAX.into());
126    }
127
128    #[test]
129    fn test_valid_i32_min() {
130        let _ = BitValue::<8>::new_i32(i8::MIN.into());
131    }
132
133    #[test]
134    #[cfg_attr(debug_assertions, should_panic)]
135    fn test_invalid_i32_max() {
136        let max: i32 = i8::MAX.into();
137        let _ = BitValue::<8>::new_i32(max + 1);
138    }
139
140    #[test]
141    #[cfg_attr(debug_assertions, should_panic)]
142    fn test_invalid_i32_min() {
143        let min: i32 = i8::MIN.into();
144        let _ = BitValue::<8>::new_i32(min - 1);
145    }
146
147    #[test]
148    #[cfg_attr(debug_assertions, should_panic)]
149    fn test_invalid_i32_i16max() {
150        let max: i32 = i16::MAX.into();
151        let _ = BitValue::<8>::new_i32(max);
152    }
153
154    #[test]
155    #[cfg_attr(debug_assertions, should_panic)]
156    fn test_invalid_i32_i16min() {
157        let min: i32 = i16::MIN.into();
158        let _ = BitValue::<8>::new_i32(min);
159    }
160}