abi_stable/sabi_types/
bitarray.rs

1//! A packed array of boolean enum values.
2
3use crate::const_utils::low_bit_mask_u64;
4
5use std::{
6    fmt::{self, Debug},
7    iter::ExactSizeIterator,
8    marker::PhantomData,
9};
10
11#[cfg(all(test, not(feature = "only_new_tests")))]
12mod tests;
13
14/// An array of 64 binary enum values.
15///
16/// # Example
17///
18/// ```rust
19/// use abi_stable::sabi_types::bitarray::{BitArray64, BooleanEnum};
20///
21/// assert!(SET.at(0).is_even());
22/// assert!(SET.at(1).is_odd());
23/// assert!(SET.at(20).is_even());
24/// assert!(SET.at(21).is_odd());
25///
26///
27///
28///
29/// static SET: BitArray64<IsEven> = {
30///     let mut set = BitArray64::empty();
31///
32///     let mut i = 0;
33///     while i < 64 {
34///         set = set.set(i, IsEven::Yes);
35///         i += 2;
36///     }
37///
38///     set
39/// };
40///
41///
42/// #[derive(Debug, Copy, Clone)]
43/// #[repr(u8)]
44/// enum IsEven {
45///     No,
46///     Yes,
47/// }
48///
49/// unsafe impl BooleanEnum for IsEven {
50///     const FALSE: Self = Self::No;
51///     const TRUE: Self = Self::Yes;
52/// }
53///
54/// impl IsEven {
55///     pub const fn is_even(self) -> bool {
56///         matches!(self, IsEven::Yes)
57///     }
58///     pub const fn is_odd(self) -> bool {
59///         matches!(self, IsEven::No)
60///     }
61/// }
62///
63/// ```
64#[must_use = "BitArray64 is returned by value by every mutating method."]
65#[derive(StableAbi, PartialEq, Eq)]
66#[repr(transparent)]
67pub struct BitArray64<E> {
68    bits: u64,
69    _marker: PhantomData<E>,
70}
71
72impl<E> Copy for BitArray64<E> {}
73impl<E> Clone for BitArray64<E> {
74    fn clone(&self) -> Self {
75        Self {
76            bits: self.bits,
77            _marker: PhantomData,
78        }
79    }
80}
81
82impl<E> BitArray64<E> {
83    /// Creates a BitArray64 where the first `count` elements are truthy.
84    #[inline]
85    pub const fn with_count(count: usize) -> Self
86    where
87        E: BooleanEnum,
88    {
89        Self {
90            bits: low_bit_mask_u64(count as u32),
91            _marker: PhantomData,
92        }
93    }
94
95    /// Creates a BitArray64 from a u64.
96    #[inline]
97    pub const fn from_u64(bits: u64) -> Self {
98        Self {
99            bits,
100            _marker: PhantomData,
101        }
102    }
103
104    /// Creates a BitArray64 where all elements are falsy.
105    #[inline]
106    pub const fn empty() -> Self {
107        Self {
108            bits: 0,
109            _marker: PhantomData,
110        }
111    }
112}
113
114impl<E> BitArray64<E> {
115    /// Gets the value of `E` at `index`
116    ///
117    /// # Panics
118    ///
119    /// This function panics if `index >= 64`
120    ///
121    pub const fn at(self, index: usize) -> E
122    where
123        E: BooleanEnum,
124    {
125        Self::assert_index(index);
126
127        bool_to_enum((self.bits & (1u64 << index)) != 0)
128    }
129
130    /// Sets the value at `index` to `value`
131    ///
132    /// # Panics
133    ///
134    /// This function panics if `index >= 64`
135    ///
136    pub const fn set(mut self, index: usize, value: E) -> Self
137    where
138        E: BooleanEnum,
139    {
140        if enum_to_bool(value) {
141            self.bits |= 1u64 << index;
142        } else {
143            self.bits &= !(1u64 << index);
144        }
145        self
146    }
147
148    #[track_caller]
149    const fn assert_index(index: usize) {
150        use const_panic::{concat_panic, FmtArg, PanicVal};
151
152        if index >= 64 {
153            concat_panic(&[&[
154                PanicVal::write_str("index out of bounds: the length is "),
155                PanicVal::from_usize(64, FmtArg::DEBUG),
156                PanicVal::write_str(" but the index is "),
157                PanicVal::from_usize(index, FmtArg::DEBUG),
158            ]])
159        }
160    }
161}
162
163impl<E> BitArray64<E> {
164    /// Truncates self so that only the first `length` elements are truthy.
165    pub const fn truncated(mut self, length: usize) -> Self {
166        self.bits &= low_bit_mask_u64(length as u32);
167        self
168    }
169
170    /// Converts this array to its underlying representation
171    #[inline]
172    pub const fn bits(self) -> u64 {
173        self.bits
174    }
175
176    /// An iterator over the first `count` elements of the array.
177    #[allow(clippy::missing_const_for_fn)]
178    pub fn iter(self) -> BitArray64Iter<E> {
179        BitArray64Iter {
180            count: 64,
181            bits: self.bits(),
182            _marker: PhantomData,
183        }
184    }
185
186    /// Whether this array is equal to `other` up to the `count` element.
187    pub fn eq(self, other: Self, count: usize) -> bool {
188        let all_accessible = low_bit_mask_u64(count as u32);
189        let implication = (!self.bits | other.bits) & all_accessible;
190        println!(
191            "self:{:b}\nother:{:b}\nall_accessible:{:b}\nimplication:{:b}",
192            self.bits, other.bits, all_accessible, implication,
193        );
194        implication == all_accessible
195    }
196}
197
198impl<E> Debug for BitArray64<E>
199where
200    E: BooleanEnum,
201{
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        f.debug_list().entries(self.iter()).finish()
204    }
205}
206
207////////////////////////////////////////////////////////////////////////////////
208
209/// A trait for enums with two variants where one is `truthy` and the other one is `falsy`.
210///
211/// # Safety
212///
213/// This type must:
214/// - be represented as a `u8`
215/// - have exactly two possible values
216/// - assign one value to the `FALSE` associated constant,
217/// - assign another value to the `TRUE` associated constant
218///
219pub unsafe trait BooleanEnum: Debug + Copy + 'static {
220    /// The falsy value of this type
221    const FALSE: Self;
222    /// The truthy value of this type
223    const TRUE: Self;
224}
225
226const _: () = assert!(std::mem::size_of::<bool>() == 1);
227unsafe impl BooleanEnum for bool {
228    const FALSE: Self = false;
229    const TRUE: Self = true;
230}
231
232/// Converts a bool to a [`BooleanEnum`].
233///
234/// Converts `true` to [`BooleanEnum::TRUE`],
235/// and `false` to [`BooleanEnum::FALSE`],
236///
237pub const fn bool_to_enum<E>(b: bool) -> E
238where
239    E: BooleanEnum,
240{
241    if b {
242        E::TRUE
243    } else {
244        E::FALSE
245    }
246}
247
248/// Converts a [`BooleanEnum`] to a bool
249///
250/// Converts [`BooleanEnum::TRUE`] to `true`,
251/// and [`BooleanEnum::FALSE`] to `false`,
252///
253pub const fn enum_to_bool<E>(b: E) -> bool
254where
255    E: BooleanEnum,
256{
257    enum_as_u8(b) == EnumConsts::<E>::TRUE_U8
258}
259
260const fn enum_as_u8<E: BooleanEnum>(x: E) -> u8 {
261    // SAFETY: `BooleanEnum` requires `E` to be represented as a `u8`
262    unsafe { const_transmute!(E, u8, x) }
263}
264
265struct EnumConsts<E: BooleanEnum>(E);
266
267impl<E: BooleanEnum> EnumConsts<E> {
268    const TRUE_U8: u8 = enum_as_u8(E::TRUE);
269}
270
271////////////////////////////////////////////////////////////////////////////////
272
273////////////////////////////////////////////////////////////////////////////////
274
275/// Iterator over the enums inside a [`BitArray64`]
276#[derive(Debug, Clone)]
277pub struct BitArray64Iter<E> {
278    count: usize,
279    bits: u64,
280    _marker: PhantomData<E>,
281}
282
283impl<E> BitArray64Iter<E>
284where
285    E: BooleanEnum,
286{
287    #[inline]
288    fn next_inner<F>(&mut self, f: F) -> Option<E>
289    where
290        F: FnOnce(&mut Self) -> E,
291    {
292        if self.count == 0 {
293            None
294        } else {
295            Some(f(self))
296        }
297    }
298}
299impl<E> Iterator for BitArray64Iter<E>
300where
301    E: BooleanEnum,
302{
303    type Item = E;
304
305    fn next(&mut self) -> Option<E> {
306        self.next_inner(|this| {
307            this.count -= 1;
308            let cond = (this.bits & 1) != 0;
309            this.bits >>= 1;
310            bool_to_enum(cond)
311        })
312    }
313
314    #[inline]
315    fn size_hint(&self) -> (usize, Option<usize>) {
316        (self.len(), Some(self.len()))
317    }
318}
319
320impl<E> DoubleEndedIterator for BitArray64Iter<E>
321where
322    E: BooleanEnum,
323{
324    fn next_back(&mut self) -> Option<E> {
325        self.next_inner(|this| {
326            this.count -= 1;
327            bool_to_enum((this.bits & (1 << this.count)) != 0)
328        })
329    }
330}
331
332impl<E> ExactSizeIterator for BitArray64Iter<E>
333where
334    E: BooleanEnum,
335{
336    #[inline]
337    fn len(&self) -> usize {
338        self.count
339    }
340}