bitmath/
lib.rs

1use std::fmt::{Display, Formatter, LowerHex};
2use std::ops::{Index, IndexMut, Range, RangeInclusive};
3
4
5fn bit(b: bool) -> usize { if b { 1 } else { 0 } }
6
7
8#[derive(Default, Debug, Clone, Copy)]
9struct SignedHex(i32);
10
11impl LowerHex for SignedHex {
12    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
13        let prefix = if f.alternate() { "0x" } else { "" };
14        let bare_hex = format!("{:x}", self.0.abs());
15        f.pad_integral(self.0 >= 0, prefix, &bare_hex)
16    }
17}
18
19
20#[derive(Debug, Copy, Clone)]
21pub enum BitsError {
22    /// The given input string could not be parsed.
23    InvalidInputString,
24    /// The bit widths of the arguments are not equal (expected, found).
25    BitWidthMismatch { expected: usize, found: usize },
26    /// The provided bit number is outside the bounds of this value.
27    BitIndexOutOfRange,
28}
29
30
31/// The heart of the `bitmath` crate. `Bits` is an generically-sized bit vector,
32/// with support for accurate bitwise arithmetic including overflows and handling
33/// signed vs unsigned arguments and two's-complement conversions.
34#[derive(Debug, Copy, Clone)]
35pub struct Bits<const SIZE: usize>(pub [bool; SIZE]);
36
37impl<const SIZE: usize> Bits<SIZE> {
38    /// Create a new `Bits` with the given size, initialized to zero.
39    pub fn new() -> Self {
40        Bits([false; SIZE])
41    }
42
43    /// Create a new `Bits` by parsing the provided signed integer.
44    ///
45    /// Note that this function accepts differing bit widths. If the `Bits` constructed has
46    /// fewer bits than an `i32` then the given value is truncated to fit. If the `Bits`
47    /// constructed has more bits than an `i32` then the given value is sign-extended to match.
48    pub fn from_signed(x: i32) -> Self {
49        let mut bits = Vec::new();
50        if SIZE <= 32 {
51            for i in 0..SIZE {
52                bits.push(((x >> (SIZE-1 - i)) & 1) != 0);
53            }
54        }
55        else {
56            let extend_bits = SIZE - 32;
57            for _ in 0..extend_bits {
58                bits.push(if x < 0 { true } else { false });
59            }
60            for i in 0..32 {
61                bits.push(((x >> (31 - i)) & 1) != 0);
62            }
63        }
64        Bits(bits.try_into().unwrap())
65    }
66
67
68    /// Create a new `Bits` by parsing the provided unsigned integer.
69    ///
70    /// Note that this function accepts differing bit widths. If the `Bits` constructed has
71    /// fewer bits than a `u32` then the given value is truncated to fit. If the `Bits`
72    /// constructed has more bits than a `u32` then the given value is padded with zeros to match.
73    pub fn from_unsigned(x: u32) -> Self {
74        let mut bits = Vec::new();
75        if SIZE <= 32 {
76            for i in 0..SIZE {
77                bits.push(((x >> (SIZE-1 - i)) & 1) != 0);
78            }
79        }
80        else {
81            let extend_bits = SIZE - 32;
82            for _ in 0..extend_bits {
83                bits.push(false);
84            }
85            for i in 0..32 {
86                bits.push(((x >> (31 - i)) & 1) != 0);
87            }
88        }
89        Bits(bits.try_into().unwrap())
90    }
91
92    /// Create a new `Bits` from the given slice.
93    ///
94    /// This function requires that the slice width and the `Bits` width are identical.
95    pub fn from_slice(slice: &[bool]) -> Result<Self, BitsError> {
96        if slice.len() != SIZE {
97            return Err(BitsError::BitWidthMismatch { expected: SIZE, found: slice.len() });
98        }
99        let mut copied = [false; SIZE];
100        for i in 0..SIZE {
101            copied[i] = slice[i];
102        }
103        Ok(Bits(copied))
104    }
105
106    #[doc(hidden)]
107    // used internally for bitslice!() since #:# bit indexing works backwards
108    pub fn from_reverse_index(slice: &[bool], hi: usize, lo: usize) -> Result<Self, BitsError> {
109        let high = lo.max(hi);
110        let low = lo.min(hi);
111        let width = high - low + 1;
112        if slice.len() - high < 1 { // we already know low is >=0 because usize
113            return Err(BitsError::BitIndexOutOfRange);
114        }
115        if width != SIZE {
116            return Err(BitsError::BitWidthMismatch{ expected: SIZE, found: width});
117        }
118        let mut copied = [false; SIZE];
119        for i in 0..SIZE {
120            copied[i] = slice[slice.len() - high - 1 + i];
121        }
122        Ok(Bits(copied))
123    }
124
125    /// Returns the width of the `Bits` in bits.
126    pub const fn size(&self) -> usize { SIZE }
127
128    /// Gets an immutable reference to bit `n`, or `None` if `n` is out of bounds.
129    ///
130    /// Note that this function indexes in "regular" order, i.e. get_bit(0)
131    /// returns the leftmost, most significant bit.
132    pub fn get_bit(&self, n: usize) -> Option<&bool> { self.0.get(n) }
133
134
135    /// Gets a mutable reference to bit `n`, or `None` if `n` is out of bounds.
136    ///
137    /// Note that this function indexes in "regular" order, i.e. get_bit_mut(0)
138    /// returns the leftmost, most significant bit.
139    pub fn get_bit_mut(&mut self, n: usize) -> Option<&mut bool> { self.0.get_mut(n) }
140
141    /// Converts the bit vector into an unsigned integer value.
142    pub fn unsigned_value(&self) -> u32 {
143        let mut result = 0u32;
144        let start_idx = (SIZE as i32 - 32).max(0) as usize;
145        for i in 0..self.size().min(32) {
146            result <<= 1;
147            result |= bit(self.0[start_idx+i]) as u32;
148        }
149        result
150    }
151
152    /// Converts the bit vector into a signed integer value.
153    pub fn signed_value(&self) -> i32 {
154        let mut result = 0u32;
155        let start_idx = (SIZE as i32 - 32).max(0) as usize;
156        let extend_bits = (32 - SIZE as i32).max(0) as usize;
157        let is_negative = self.0[0] == true;
158        for _ in 0..extend_bits {
159            result <<= 1;
160            result |= if is_negative { 1 } else { 0 };
161        }
162        for i in 0..SIZE.min(32) {
163            result <<= 1;
164            result |= *self.get_bit(start_idx+i).unwrap() as u32;
165        }
166        unsafe { std::mem::transmute(result) }
167    }
168
169    /// Performs an unsigned addition between this and `other`, returning the result
170    /// as a new `Bits`, as well as whether or not an overflow occurred.
171    pub fn unsigned_add(&self, other: Self) -> (Self, bool) {
172        let a = self.unsigned_value() as u64;
173        let b = other.unsigned_value() as u64;
174        let sum = a + b;
175        let mut mask = 1u64;
176        for _ in 0..SIZE-1 {
177            mask <<= 1;
178            mask |= 1;
179        }
180        let result = (sum & mask) as u32;
181        (Bits::from_unsigned(result), (sum >> SIZE) > 0)
182    }
183
184    /// Performs a signed addition between this and `other`, returning the result
185    /// as a new `Bits`, as well as whether or not an overflow occurred.
186    pub fn signed_add(&self, other: Self) -> (Self, bool) {
187        let a = self.signed_value() as i64;
188        let b = other.signed_value() as i64;
189        let sum = a + b;
190        let mut mask = 1i64;
191        for _ in 0..SIZE-1 {
192            mask <<= 1;
193            mask |= 1;
194        }
195        let result = (sum & mask) as i32;
196        let overflow = sum < -(2u64.pow(SIZE as u32 - 1) as i64) || sum > (2u64.pow(SIZE as u32 - 1) - 1) as i64;
197        (Bits::from_signed(result), overflow)
198    }
199
200    /// Rotates the bits right by `n` bits. `n` can be greater than `SIZE`,
201    /// at which point it wraps back around.
202    pub fn rotate_right(&self, n: usize) -> Self {
203        let n = n % SIZE;
204        let mut result = Bits::new();
205        for i in 0..SIZE {
206            result.0[(i+n)%SIZE] = self.0[i];
207        }
208        result
209    }
210
211    /// Rotates the bits left by `n` bits. `n` can be greater than `SIZE`,
212    /// at which point it wraps back around.
213    pub fn rotate_left(&self, n: usize) -> Self {
214        let n = n % SIZE;
215        let mut result = Bits::new();
216        for i in 0..SIZE {
217            // conversion to signed to prevent underflow
218            result.0[(i+SIZE-n) % SIZE] = self.0[i];
219        }
220        result
221    }
222
223    /// Produces the contents of the bit vector as a string of ones and zeros.
224    ///
225    /// The parameter, `pretty`, determines whether or not spaces will be added
226    /// to the output string for readability.
227    pub fn bits_string(&self, pretty: bool) -> String {
228        let mut bitstr: String = self.0.map(|b| if b { "1".into() } else { "0".into() })
229            .into_iter()
230            .collect::<Vec<String>>()
231            .join("");
232        if pretty {
233            for i in 1..SIZE {
234                let idx = SIZE - i;
235                if idx % 4 == 0 {
236                    bitstr.insert(idx, ' ');
237                }
238            }
239        }
240        bitstr
241    }
242
243    /// Creates a nicely spaced hexadecimal string for the value of the bit vector,
244    /// intepreted as an unsigned integer.
245    pub fn pretty_uhex_string(&self) -> String {
246        let digits = (SIZE as f32 / 4.0).ceil() as usize;
247        let hex_padding = digits % 2;
248        let mut uhex_chars = vec![' '; hex_padding];
249        uhex_chars.extend(format!("{:01$x}", self.unsigned_value(), digits)
250            .chars()
251            .into_iter());
252        uhex_chars
253            .chunks(2)
254            // remove padding after chunks separated
255            .map(|chunk| chunk.iter().map(|c| String::from(*c)).collect::<Vec<_>>().join("").replace(" ",""))
256            .collect::<Vec<_>>()
257            .join(" ")
258    }
259}
260
261
262impl<const N: usize> Default for Bits<N> {
263    fn default() -> Self {
264        Bits([false; N])
265    }
266}
267
268
269impl<const N: usize> Display for Bits<N> {
270    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
271        write!(f, "Bits<{0}>{{ {1} | dec {2}/{3} | hex {4:#x}/{5:#x} }}",
272               N,
273               self.bits_string(true),
274               self.unsigned_value(),
275               self.signed_value(),
276               self.unsigned_value(),
277               SignedHex(self.signed_value()))
278    }
279}
280
281
282impl<const N: usize> TryFrom<&str> for Bits<N> {
283    type Error = BitsError;
284
285    fn try_from(input: &str) -> Result<Self, Self::Error> {
286        let input = input.replace(" ","");
287        if input.len() > N || input.chars().any(|c| c != '0' && c != '1') {
288            return Err(BitsError::InvalidInputString);
289        }
290        let mut result = Bits([false; N]);
291        for i in 0..N {
292            let c = input.chars().nth(i).unwrap();
293            result.0[i] = if c == '0' { false } else { true };
294        }
295        Ok(result)
296    }
297}
298
299
300impl<const N: usize> Index<usize> for Bits<N> {
301    type Output = bool;
302
303    fn index(&self, index: usize) -> &Self::Output {
304        self.get_bit(index).unwrap()
305    }
306}
307
308
309impl<const N: usize> IndexMut<usize> for Bits<N> {
310    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
311        self.get_bit_mut(index).unwrap()
312    }
313}
314
315
316impl <const N: usize> Index<Range<usize>>for Bits<N> {
317    type Output = [bool];
318
319    fn index(&self, index: Range<usize>) -> &Self::Output {
320        &self.0[index]
321    }
322}
323
324
325impl <const N: usize> Index<RangeInclusive<usize>>for Bits<N> {
326    type Output = [bool];
327
328    fn index(&self, index: RangeInclusive<usize>) -> &Self::Output {
329        &self.0[index]
330    }
331}
332
333
334impl <const N: usize> IndexMut<Range<usize>>for Bits<N> {
335    fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
336        &mut self.0[index]
337    }
338}
339
340
341impl <const N: usize> IndexMut<RangeInclusive<usize>>for Bits<N> {
342    fn index_mut(&mut self, index: RangeInclusive<usize>) -> &mut Self::Output {
343        &mut self.0[index]
344    }
345}
346
347/// convenience macro for indexing bitwise slices using `bits[7:0]` syntax
348#[macro_export]
349macro_rules! bitslice {
350    ($name:ident[$high:literal:$low:literal]) => {
351        bitmath::Bits::<{$high-$low+1}>::from_reverse_index(&$name.0,$high,$low).unwrap()
352    }
353}