Skip to main content

sparse_hash_map/
sparse_array.rs

1//! One sparse array: a group of up to 64 logical buckets backed by a bitmap.
2//!
3//! Instead of one flat slot per bucket, buckets are grouped into arrays of
4//! [`BITMAP_NB_BITS`] logical indices. An array stores only the present values,
5//! packed contiguously, plus a bitmap of which indices hold a value and a bitmap
6//! of which indices are tombstones. The dense offset of a logical index is the
7//! number of value bits below it. An empty logical bucket costs about one bit,
8//! not the size of a value.
9
10#[cfg(not(target_pointer_width = "64"))]
11use crate::popcount::popcount;
12#[cfg(target_pointer_width = "64")]
13use crate::popcount::popcountll;
14
15/// Number of logical buckets per sparse array.
16///
17/// 64 on 64-bit targets, 32 on 32-bit targets. Popcount on 64-bit words is slow
18/// on 32-bit machines, so a narrower bitmap is used there.
19#[cfg(target_pointer_width = "64")]
20pub const BITMAP_NB_BITS: usize = 64;
21/// Number of logical buckets per sparse array.
22#[cfg(not(target_pointer_width = "64"))]
23pub const BITMAP_NB_BITS: usize = 32;
24
25#[cfg(target_pointer_width = "64")]
26const BUCKET_SHIFT: usize = 6;
27#[cfg(not(target_pointer_width = "64"))]
28const BUCKET_SHIFT: usize = 5;
29
30const BUCKET_MASK: usize = BITMAP_NB_BITS - 1;
31
32/// The bitmap word. Wide enough to hold [`BITMAP_NB_BITS`] bits.
33#[cfg(target_pointer_width = "64")]
34pub type Bitmap = u64;
35/// The bitmap word.
36#[cfg(not(target_pointer_width = "64"))]
37pub type Bitmap = u32;
38
39/// Which sparse array holds a global bucket index.
40#[inline]
41pub fn sparse_ibucket(ibucket: usize) -> usize {
42    ibucket >> BUCKET_SHIFT
43}
44
45/// The logical index within its sparse array for a global bucket index.
46#[inline]
47pub fn index_in_sparse_bucket(ibucket: usize) -> u8 {
48    (ibucket & BUCKET_MASK) as u8
49}
50
51/// Number of sparse arrays needed for `bucket_count` logical buckets.
52#[inline]
53pub fn nb_sparse_buckets(bucket_count: usize) -> usize {
54    if bucket_count == 0 {
55        return 0;
56    }
57    let rounded = crate::util::round_up_to_power_of_two(bucket_count);
58    core::cmp::max(1, sparse_ibucket(rounded))
59}
60
61/// Count the occupied bits in a bitmap word.
62#[inline]
63pub(crate) fn popcount_bitmap(val: Bitmap) -> u8 {
64    #[cfg(target_pointer_width = "64")]
65    {
66        popcountll(val) as u8
67    }
68    #[cfg(not(target_pointer_width = "64"))]
69    {
70        popcount(val) as u8
71    }
72}
73
74/// A group of logical buckets stored densely with occupancy bitmaps.
75///
76/// Present values live in `values` in ascending index order. `bitmap_vals` marks
77/// occupied indices. `bitmap_deleted_vals` marks tombstones left by erase.
78pub struct SparseArray<T> {
79    values: Vec<T>,
80    bitmap_vals: Bitmap,
81    bitmap_deleted_vals: Bitmap,
82    last_array: bool,
83}
84
85impl<T> SparseArray<T> {
86    /// An empty array that is not the last in a table.
87    pub fn new() -> Self {
88        Self {
89            values: Vec::new(),
90            bitmap_vals: 0,
91            bitmap_deleted_vals: 0,
92            last_array: false,
93        }
94    }
95
96    /// Whether this array holds no values.
97    #[inline]
98    pub fn is_empty(&self) -> bool {
99        self.values.is_empty()
100    }
101
102    /// Number of values held.
103    #[inline]
104    pub fn len(&self) -> usize {
105        self.values.len()
106    }
107
108    /// The heap capacity of the dense value block.
109    ///
110    /// The block over-allocates in fixed steps set by the sparsity level, so a
111    /// full array reserves exactly `growth_step` more slots rather than doubling.
112    #[cfg(test)]
113    #[inline]
114    pub fn capacity(&self) -> usize {
115        self.values.capacity()
116    }
117
118    /// Whether this is the last array in its table.
119    #[inline]
120    pub fn last(&self) -> bool {
121        self.last_array
122    }
123
124    /// Flag this array as the last in its table.
125    #[inline]
126    pub fn set_as_last(&mut self) {
127        self.last_array = true;
128    }
129
130    /// The occupancy bitmap. Bit `i` set means index `i` holds a value.
131    #[inline]
132    pub fn bitmap_vals(&self) -> Bitmap {
133        self.bitmap_vals
134    }
135
136    /// The tombstone bitmap. Bit `i` set means index `i` was erased.
137    #[inline]
138    pub fn bitmap_deleted_vals(&self) -> Bitmap {
139        self.bitmap_deleted_vals
140    }
141
142    /// The dense value slice in ascending index order.
143    #[inline]
144    pub fn values(&self) -> &[T] {
145        &self.values
146    }
147
148    /// The dense value slice as mutable references.
149    #[inline]
150    pub fn values_mut(&mut self) -> &mut [T] {
151        &mut self.values
152    }
153
154    /// Whether logical index `index` holds a value.
155    #[inline]
156    pub fn has_value(&self, index: u8) -> bool {
157        (self.bitmap_vals & (1 as Bitmap) << index) != 0
158    }
159
160    /// Whether logical index `index` is a tombstone.
161    #[inline]
162    pub fn has_deleted_value(&self, index: u8) -> bool {
163        (self.bitmap_deleted_vals & (1 as Bitmap) << index) != 0
164    }
165
166    /// The dense offset of logical `index`: the count of value bits below it.
167    #[inline]
168    pub fn index_to_offset(&self, index: u8) -> usize {
169        let mask = ((1 as Bitmap) << index).wrapping_sub(1);
170        popcount_bitmap(self.bitmap_vals & mask) as usize
171    }
172
173    /// The logical index of the value at dense `offset`.
174    pub fn offset_to_index(&self, offset: usize) -> u8 {
175        let mut bitmap = self.bitmap_vals;
176        let mut index: u8 = 0;
177        let mut nb_ones = 0;
178        while bitmap != 0 {
179            if bitmap & 0x1 == 1 {
180                if nb_ones == offset {
181                    break;
182                }
183                nb_ones += 1;
184            }
185            index += 1;
186            bitmap >>= 1;
187        }
188        index
189    }
190
191    /// A shared reference to the value at logical `index`.
192    ///
193    /// The caller must ensure `index` holds a value.
194    #[inline]
195    pub fn value(&self, index: u8) -> &T {
196        &self.values[self.index_to_offset(index)]
197    }
198
199    /// A mutable reference to the value at logical `index`.
200    #[inline]
201    pub fn value_mut(&mut self, index: u8) -> &mut T {
202        let offset = self.index_to_offset(index);
203        &mut self.values[offset]
204    }
205
206    /// Insert `value` at logical `index`, which must be empty.
207    ///
208    /// `growth_step` is the sparsity capacity step. When the block is full it
209    /// grows by exactly `growth_step` slots, not by `Vec` doubling, so the heap
210    /// slack tracks the sparsity level. Returns the dense offset where the value
211    /// landed.
212    pub fn set(&mut self, index: u8, value: T, growth_step: usize) -> usize {
213        debug_assert!(!self.has_value(index));
214        let offset = self.index_to_offset(index);
215        if self.values.len() == self.values.capacity() {
216            self.values.reserve_exact(growth_step.max(1));
217        }
218        self.values.insert(offset, value);
219        self.bitmap_vals |= (1 as Bitmap) << index;
220        self.bitmap_deleted_vals &= !((1 as Bitmap) << index);
221        offset
222    }
223
224    /// Erase the value at dense `offset` and return it.
225    ///
226    /// Marks `index` as a tombstone.
227    pub fn remove_value(&mut self, offset: usize, index: u8) -> T {
228        debug_assert!(self.has_value(index));
229        let value = self.values.remove(offset);
230        self.bitmap_vals &= !((1 as Bitmap) << index);
231        self.bitmap_deleted_vals |= (1 as Bitmap) << index;
232        value
233    }
234
235    /// Tombstone every present value and drop the dense block.
236    ///
237    /// Moves each occupied bit into the tombstone bitmap and clears the value
238    /// bitmap, matching a slot-by-slot erase but in one pass. Returns how many
239    /// values were dropped so the caller can update its counters.
240    pub fn erase_all(&mut self) -> usize {
241        let removed = self.values.len();
242        self.values.clear();
243        self.bitmap_deleted_vals |= self.bitmap_vals;
244        self.bitmap_vals = 0;
245        removed
246    }
247
248    /// Consume the array and yield its dense values in index order.
249    #[inline]
250    pub fn into_values(self) -> Vec<T> {
251        self.values
252    }
253
254    /// Rebuild an array from decoded bitmaps and dense values.
255    ///
256    /// Used by hash-compatible deserialization, which restores slots directly
257    /// without re-hashing.
258    pub fn from_parts(bitmap_vals: Bitmap, bitmap_deleted_vals: Bitmap, values: Vec<T>) -> Self {
259        debug_assert_eq!(popcount_bitmap(bitmap_vals) as usize, values.len());
260        Self {
261            values,
262            bitmap_vals,
263            bitmap_deleted_vals,
264            last_array: false,
265        }
266    }
267}
268
269impl<T: Clone> Clone for SparseArray<T> {
270    fn clone(&self) -> Self {
271        Self {
272            values: self.values.clone(),
273            bitmap_vals: self.bitmap_vals,
274            bitmap_deleted_vals: self.bitmap_deleted_vals,
275            last_array: self.last_array,
276        }
277    }
278}
279
280impl<T> Default for SparseArray<T> {
281    fn default() -> Self {
282        Self::new()
283    }
284}
285
286#[cfg(test)]
287mod tests {
288    use super::*;
289
290    // Capacity grows by the fixed step, not by Vec doubling. Fill the low bits
291    // of one array in index order and check the block reserves in steps.
292    #[test]
293    fn capacity_grows_by_fixed_step() {
294        const STEP: usize = 4;
295        let mut array: SparseArray<i32> = SparseArray::new();
296        for index in 0..8u8 {
297            array.set(index, index as i32, STEP);
298            // After each insert the capacity is the value count rounded up to a
299            // multiple of the step. Doubling would jump past these bounds.
300            let expected = array.len().div_ceil(STEP) * STEP;
301            assert_eq!(array.capacity(), expected, "at len {}", array.len());
302        }
303    }
304
305    #[test]
306    fn a_larger_step_reserves_more_slack() {
307        let mut high: SparseArray<i32> = SparseArray::new();
308        let mut low: SparseArray<i32> = SparseArray::new();
309        // One value each. The larger step over-allocates more.
310        high.set(0, 0, 2);
311        low.set(0, 0, 8);
312        assert_eq!(high.capacity(), 2);
313        assert_eq!(low.capacity(), 8);
314    }
315}