Skip to main content

ixa/data_structures/
bit_set.rs

1//! A semi-dynamic bit mask data structure the size of which is fixed at construction.
2//!
3//! Supports very efficient "is empty" check, `get`, and `set` operations. A `BitSet` allocates all
4//! of its storage up front. The storage is chunked into 64 bit words, which means allocated
5//! capacity can be greater than the value of `bit_count` the `BitSet` was constructed with. For
6//! simplicity, we allow the entire capacity to be used (with `set` and `get`). As a consequence,
7//! the actual value of `bit_count` the `BitSet` was constructed with is not recoverable after
8//! construction.
9//!
10//! ## Implementation
11//!
12//! Storage is inline for <=64 bits, which makes access and cloning very cheap for the
13//! overwhelmingly common case. Internally we use a `set_count` to make `is_empty` very fast
14//! independent of whether or not storage is inline.
15
16pub struct BitSet {
17    /// 64 bits inline or heap allocated spillover.
18    storage: Storage,
19    /// Count of set bits, for O(1) empty check. Maintained in mutation operations.
20    set_count: u32,
21}
22
23enum Storage {
24    /// Inline storage for bit counts <= 64. No heap allocation; `Clone` is a register copy.
25    Inline(u64),
26    /// Heap storage for bit counts > 64. Fixed length, allocated once.
27    Heap(Box<[u64]>),
28}
29
30impl BitSet {
31    pub fn new(bit_count: usize) -> Self {
32        let storage = if bit_count <= 64 {
33            Storage::Inline(0)
34        } else {
35            let num_words = bit_count.div_ceil(64);
36            Storage::Heap(vec![0u64; num_words].into_boxed_slice())
37        };
38        Self {
39            storage,
40            set_count: 0,
41        }
42    }
43
44    #[inline(always)]
45    pub fn is_empty(&self) -> bool {
46        self.set_count == 0
47    }
48
49    /// Clones this `BitSet` if it contains any set bits.
50    #[inline(always)]
51    pub fn clone_if_nonempty(&self) -> Option<Self> {
52        if self.is_empty() {
53            None
54        } else {
55            Some(self.clone())
56        }
57    }
58
59    /// Returns the total number of bits that can be stored in this `BitSet`.
60    #[inline(always)]
61    pub fn capacity(&self) -> usize {
62        match &self.storage {
63            Storage::Inline(_) => 64,
64            Storage::Heap(words) => words.len() * 64,
65        }
66    }
67
68    /// Returns `true` if the `n`th bit is set, `false` otherwise.
69    #[inline(always)]
70    pub fn get(&self, n: usize) -> bool {
71        match &self.storage {
72            Storage::Inline(word) => (word >> n) & 1 != 0,
73            Storage::Heap(words) => {
74                let word = n >> 6; // n / 64
75                let bit = n & 63; // n % 64
76                (words[word] >> bit) & 1 != 0
77            }
78        }
79    }
80
81    /// Sets the `n`th bit.
82    #[inline(always)]
83    pub fn set(&mut self, n: usize) {
84        match &mut self.storage {
85            Storage::Inline(word) => {
86                let mask = 1u64 << n;
87                if *word & mask == 0 {
88                    *word |= mask;
89                    self.set_count += 1;
90                }
91            }
92            Storage::Heap(words) => {
93                let idx = n >> 6;
94                let mask = 1u64 << (n & 63);
95                let prev = words[idx];
96                if prev & mask == 0 {
97                    words[idx] = prev | mask;
98                    self.set_count += 1;
99                }
100            }
101        }
102    }
103
104    /// Clears the `n`th bit.
105    #[inline(always)]
106    pub fn reset(&mut self, n: usize) {
107        match &mut self.storage {
108            Storage::Inline(word) => {
109                let mask = 1u64 << n;
110                if *word & mask != 0 {
111                    *word &= !mask;
112                    self.set_count -= 1;
113                }
114            }
115            Storage::Heap(words) => {
116                let idx = n >> 6;
117                let mask = 1u64 << (n & 63);
118                let prev = words[idx];
119                if prev & mask != 0 {
120                    words[idx] = prev & !mask;
121                    self.set_count -= 1;
122                }
123            }
124        }
125    }
126
127    /// Clears the entire `BitSet`, resetting every bit.
128    pub fn clear(&mut self) {
129        match &mut self.storage {
130            Storage::Inline(word) => *word = 0,
131            Storage::Heap(words) => {
132                for word in words.iter_mut() {
133                    *word = 0;
134                }
135            }
136        }
137        self.set_count = 0;
138    }
139}
140
141// We manually implement `Clone` in order to decorate with `#[inline]`. Deriving `Clone` would
142// generate essentially the same code but with weaker confidence of inlining. (Code complexity,
143// compiler version, platform, whether LTO is enabled, and other mysterious factors contribute
144// to the inlining decision threshold in the general case.)
145impl Clone for BitSet {
146    #[inline]
147    fn clone(&self) -> Self {
148        let storage = match &self.storage {
149            // Register copy, no allocation.
150            Storage::Inline(word) => Storage::Inline(*word),
151            // Allocates. Only reached for bit counts > 64.
152            Storage::Heap(words) => Storage::Heap(words.clone()),
153        };
154        Self {
155            storage,
156            set_count: self.set_count,
157        }
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::BitSet;
164
165    #[test]
166    fn capacity_is_at_least_one_word_and_rounds_up_to_whole_words() {
167        for (bit_count, expected_capacity) in [
168            (0, 64),
169            (1, 64),
170            (64, 64),
171            (65, 128),
172            (128, 128),
173            (129, 192),
174        ] {
175            let bit_set = BitSet::new(bit_count);
176            assert_eq!(bit_set.capacity(), expected_capacity);
177            assert!(bit_set.is_empty());
178        }
179    }
180
181    #[test]
182    fn set_and_get_inline_bits() {
183        let mut bit_set = BitSet::new(64);
184
185        for bit in [0, 1, 31, 32, 63] {
186            assert!(!bit_set.get(bit));
187            bit_set.set(bit);
188            assert!(bit_set.get(bit));
189        }
190
191        assert!(!bit_set.is_empty());
192        assert!(!bit_set.get(2));
193        assert!(!bit_set.get(62));
194    }
195
196    #[test]
197    fn set_and_get_heap_bits_across_word_boundaries() {
198        let mut bit_set = BitSet::new(130);
199
200        for bit in [0, 63, 64, 65, 127, 128, 129, 191] {
201            assert!(!bit_set.get(bit));
202            bit_set.set(bit);
203            assert!(bit_set.get(bit));
204        }
205
206        assert!(!bit_set.is_empty());
207        assert!(!bit_set.get(1));
208        assert!(!bit_set.get(66));
209        assert!(!bit_set.get(190));
210    }
211
212    #[test]
213    fn repeated_set_and_reset_are_idempotent() {
214        let mut bit_set = BitSet::new(65);
215
216        bit_set.set(64);
217        bit_set.set(64);
218        bit_set.set(127);
219        bit_set.reset(64);
220        bit_set.reset(64);
221
222        assert!(!bit_set.get(64));
223        assert!(bit_set.get(127));
224        assert!(!bit_set.is_empty());
225
226        bit_set.reset(127);
227        assert!(bit_set.is_empty());
228
229        bit_set.reset(127);
230        assert!(bit_set.is_empty());
231    }
232
233    #[test]
234    fn clear_resets_inline_storage_and_allows_reuse() {
235        let mut bit_set = BitSet::new(64);
236        for bit in [0, 32, 63] {
237            bit_set.set(bit);
238        }
239
240        bit_set.clear();
241
242        assert!(bit_set.is_empty());
243        for bit in [0, 32, 63] {
244            assert!(!bit_set.get(bit));
245        }
246
247        bit_set.set(32);
248        assert!(bit_set.get(32));
249        assert!(!bit_set.is_empty());
250    }
251
252    #[test]
253    fn clear_resets_every_heap_word_and_allows_reuse() {
254        let mut bit_set = BitSet::new(192);
255        for bit in [0, 63, 64, 127, 128, 191] {
256            bit_set.set(bit);
257        }
258
259        bit_set.clear();
260
261        assert!(bit_set.is_empty());
262        for bit in [0, 63, 64, 127, 128, 191] {
263            assert!(!bit_set.get(bit));
264        }
265
266        bit_set.set(128);
267        assert!(bit_set.get(128));
268        assert!(!bit_set.is_empty());
269    }
270
271    #[test]
272    fn inline_clone_is_independent() {
273        assert_clone_is_independent(64, 1, 63);
274    }
275
276    #[test]
277    fn heap_clone_is_independent() {
278        assert_clone_is_independent(128, 64, 127);
279    }
280
281    #[test]
282    fn clone_if_nonempty_returns_none_for_empty_inline_and_heap_storage() {
283        assert!(BitSet::new(64).clone_if_nonempty().is_none());
284        assert!(BitSet::new(65).clone_if_nonempty().is_none());
285    }
286
287    #[test]
288    fn clone_if_nonempty_returns_an_independent_clone() {
289        let mut original = BitSet::new(65);
290        original.set(64);
291
292        let mut cloned = original.clone_if_nonempty().unwrap();
293        cloned.reset(64);
294        cloned.set(65);
295
296        assert!(original.get(64));
297        assert!(!original.get(65));
298        assert!(!cloned.get(64));
299        assert!(cloned.get(65));
300    }
301
302    fn assert_clone_is_independent(bit_count: usize, original_bit: usize, clone_bit: usize) {
303        let mut original = BitSet::new(bit_count);
304        original.set(original_bit);
305
306        let mut cloned = original.clone();
307        cloned.reset(original_bit);
308        cloned.set(clone_bit);
309
310        assert!(original.get(original_bit));
311        assert!(!original.get(clone_bit));
312        assert!(!cloned.get(original_bit));
313        assert!(cloned.get(clone_bit));
314        assert!(!original.is_empty());
315        assert!(!cloned.is_empty());
316    }
317}