musli_zerocopy/swiss/constructor.rs
1// Note: This was ported from `hashbrown`, and still contains some of the
2// comments assuming that it performs internal allocations. These are most
3// likely wrong and need to be rewritten to take into account the safety
4// requirements towards `OwnedBuf`.
5
6use core::convert::{identity as likely, identity as unlikely};
7use core::marker::PhantomData;
8use core::mem::size_of;
9use core::ptr::NonNull;
10
11use crate::buf::{self, Buf, StoreBuf};
12use crate::error::{Error, ErrorKind};
13use crate::swiss::raw::{Group, ProbeSeq, h2, is_full, probe_seq, special_is_empty};
14use crate::traits::ZeroCopy;
15
16/// Construction of a raw swiss table.
17pub struct Constructor<'a, T, S: ?Sized> {
18 buf: &'a mut S,
19
20 // Mask to get an index from a hash value. The value is one less than the
21 // number of buckets in the table.
22 bucket_mask: usize,
23
24 // Control offset, where the control vectors are stored in `buf`.
25 ctrl_ptr: usize,
26
27 // Base offset where items are written in `buf`.
28 base_ptr: usize,
29
30 // Number of elements that can be inserted before we need to grow the table.
31 // Since we can't grow the table, reaching this point results in an error.
32 growth_left: usize,
33
34 // Hold onto T to make sure the API stays coherent with the types the
35 // constructor can write.
36 _marker: PhantomData<T>,
37}
38
39impl<'a, T, S: ?Sized> Constructor<'a, T, S>
40where
41 S: StoreBuf,
42{
43 /// Wrap the given buffer for table construction.
44 ///
45 /// # Safety
46 ///
47 /// The caller must ensure that buffer contains allocated and correctly
48 /// initialized memory at `ctrl_ptr` and `base_ptr`.
49 ///
50 /// * `ctrl_ptr` must point to a memory region that is `buckets + 1` length
51 /// sized for `Group` which has been bitwise initialized to [`EMPTY`].
52 /// * `base_ptr` must point to a memory region that is `buckets` length
53 /// sized for `T`.
54 /// * `buckets` must be a power of two.
55 ///
56 /// [`EMPTY`]: crate::swiss::raw::EMPTY
57 pub(crate) fn with_buf(
58 buf: &'a mut S,
59 ctrl_ptr: usize,
60 base_ptr: usize,
61 buckets: usize,
62 ) -> Self {
63 debug_assert!(buckets.is_power_of_two());
64
65 Self {
66 buf,
67 bucket_mask: buckets - 1,
68 ctrl_ptr,
69 base_ptr,
70 growth_left: bucket_mask_to_capacity(buckets - 1),
71 _marker: PhantomData,
72 }
73 }
74
75 /// Access the underlying buffer.
76 pub(crate) fn buf(&self) -> &Buf {
77 self.buf.as_buf()
78 }
79
80 /// Export bucket mask.
81 pub(crate) fn bucket_mask(&self) -> usize {
82 self.bucket_mask
83 }
84
85 /// Get the length of the table.
86 pub(crate) fn len(&self) -> usize {
87 // NB: `growth_left` starts out at the effective capacity of the table
88 // rather than at the number of buckets, since a portion of the buckets
89 // is reserved to always stay empty.
90 bucket_mask_to_capacity(self.bucket_mask) - self.growth_left
91 }
92
93 /// Returns the number of buckets in the table.
94 #[inline]
95 pub(crate) fn buckets(&self) -> usize {
96 self.bucket_mask + 1
97 }
98
99 /// Insert the given zero copy value into the table.
100 pub(crate) fn insert(&mut self, hash: u64, value: &T) -> Result<Bucket<'_, T>, Error>
101 where
102 T: ZeroCopy,
103 {
104 // SAFETY:
105 // 1. The [`Constructor`] must already have properly initialized control bytes since
106 // we will never expose `Constructor::new_uninitialized` in a public API.
107 let slot = self.find_insert_slot(hash)?;
108
109 // We can avoid growing the table once we have reached our load factor if we are replacing
110 // a tombstone. This works since the number of EMPTY slots does not change in this case.
111 //
112 // SAFETY: The function is guaranteed to return [`InsertSlot`] that contains an index
113 // in the range `0..=self.buckets()`.
114 let old_ctrl = *self.ctrl(slot.index);
115
116 if unlikely(self.growth_left == 0 && special_is_empty(old_ctrl)) {
117 return Err(Error::new(ErrorKind::CapacityError));
118 }
119
120 Ok(self.insert_in_slot(hash, slot, value))
121 }
122
123 /// Inserts a new element into the table in the given slot, and returns its
124 /// raw bucket.
125 #[inline]
126 pub fn insert_in_slot(&mut self, hash: u64, slot: InsertSlot, value: &T) -> Bucket<'_, T>
127 where
128 T: ZeroCopy,
129 {
130 let old_ctrl = *self.ctrl(slot.index);
131 self.record_item_insert_at(slot.index, old_ctrl, hash);
132 let bucket = self.bucket(slot.index);
133 bucket.write(value);
134 bucket
135 }
136
137 /// Returns a pointer to an element in the table.
138 #[inline]
139 pub fn bucket(&mut self, index: usize) -> Bucket<'_, T> {
140 debug_assert_ne!(self.bucket_mask, 0);
141 debug_assert!(index < self.buckets());
142
143 let Some(index) = index.checked_mul(size_of::<T>()) else {
144 panic!("Index `{index}` out of bounds");
145 };
146
147 let Some(start) = self.base_ptr.checked_add(index) else {
148 panic!("Start `{index}` out of bounds");
149 };
150
151 let end = start + size_of::<T>();
152
153 // SAFETY: The accessed data is a slice of u8's, which cannot be
154 // incorrectly initialized locally.
155 let slice = unsafe { self.buf.get_mut(start..end) };
156
157 let Some(slice) = slice else {
158 panic!("Missing bucket at range {start}..{end}");
159 };
160
161 // SAFETY: We've ensure that the bucket is appropriately sized just
162 // above.
163 unsafe { Bucket::from_slice(slice) }
164 }
165
166 /// Finds the position to insert something in a group.
167 ///
168 /// **This may have false positives and must be fixed up with `fix_insert_slot`
169 /// before it's used.**
170 ///
171 /// The function is guaranteed to return the index of an empty or deleted [`Bucket`]
172 /// in the range `0..self.buckets()` (`0..=self.bucket_mask`).
173 #[inline]
174 fn find_insert_slot_in_group(&self, group: &Group, probe_seq: &ProbeSeq) -> Option<usize> {
175 let bit = likely(group.match_empty_or_deleted().lowest_set_bit()?);
176
177 // This is the same as `(probe_seq.pos + bit) % self.buckets()` because the number
178 // of buckets is a power of two, and `self.bucket_mask = self.buckets() - 1`.
179 Some((probe_seq.pos + bit) & self.bucket_mask)
180 }
181
182 #[inline]
183 fn record_item_insert_at(&mut self, index: usize, old_ctrl: u8, hash: u64) {
184 self.growth_left -= usize::from(special_is_empty(old_ctrl));
185 self.set_ctrl_h2(index, hash);
186 }
187
188 /// Searches for an empty or deleted bucket which is suitable for inserting
189 /// a new element, returning the `index` for the new [`Bucket`].
190 #[inline]
191 fn find_insert_slot(&mut self, hash: u64) -> Result<InsertSlot, Error> {
192 let mut probe_seq = probe_seq(self.bucket_mask, hash);
193
194 loop {
195 let group = unsafe { Group::load(self.ctrl_group(probe_seq.pos).as_ptr()) };
196
197 if let Some(index) = self.find_insert_slot_in_group(&group, &probe_seq) {
198 return Ok(self.fix_insert_slot(index));
199 };
200
201 probe_seq.move_next(self.bucket_mask)?;
202 }
203 }
204
205 /// Fixes up an insertion slot returned by the
206 /// [`Constructor::find_insert_slot_in_group`] method.
207 ///
208 /// In tables smaller than the group width (`self.buckets() <
209 /// Group::WIDTH`), trailing control bytes outside the range of the table
210 /// are filled with [`EMPTY`] entries. These will unfortunately trigger a
211 /// match of [`Constructor::find_insert_slot_in_group`] function. This is
212 /// because the `Some(bit)` returned by
213 /// `group.match_empty_or_deleted().lowest_set_bit()` after masking
214 /// (`(probe_seq.pos + bit) & self.bucket_mask`) may point to a full bucket
215 /// that is already occupied. We detect this situation here and perform a
216 /// second scan starting at the beginning of the table. This second scan is
217 /// guaranteed to find an empty slot (due to the load factor) before hitting
218 /// the trailing control bytes (containing [`EMPTY`] bytes).
219 ///
220 /// If this function is called correctly, it is guaranteed to return
221 /// [`InsertSlot`] with an index of an empty or deleted bucket in the range
222 /// `0..self.buckets()` (see `Warning` and `Safety`).
223 ///
224 /// [`EMPTY`]: super::raw::EMPTY
225 ///
226 /// # Warning
227 ///
228 /// The table must have at least 1 empty or deleted `bucket`, otherwise if
229 /// the table is less than the group width (`self.buckets() < Group::WIDTH`)
230 /// this function returns an index outside of the table indices range
231 /// `0..self.buckets()` (`0..=self.bucket_mask`). Attempt to write data at
232 /// that index will cause immediate [`undefined behavior`].
233 ///
234 /// # Safety
235 ///
236 /// The safety rules are directly derived from the safety rules for
237 /// [`Constructor::ctrl`] method. Thus, in order to uphold those safety
238 /// contracts, as well as for the correct logic of the work of this crate,
239 /// the following rules are necessary and sufficient:
240 ///
241 /// * The [`Constructor`] must have properly initialized control bytes
242 /// otherwise calling this function results in [`undefined behavior`].
243 ///
244 /// * This function must only be used on insertion slots found by
245 /// [`find_insert_slot_in_group`] (after the `find_insert_slot_in_group`
246 /// function, but before insertion into the table).
247 ///
248 /// * The `index` must not be greater than the `self.bucket_mask`, i.e.
249 /// `(index + 1) <= self.buckets()` (this one is provided by the
250 /// [`find_insert_slot_in_group`] function).
251 ///
252 /// Calling this function with an index not provided by
253 /// [`find_insert_slot_in_group`] may result in [`undefined behavior`] even
254 /// if the index satisfies the safety rules of the [`ctrl`] function (`index
255 /// < self.bucket_mask + 1 + Group::WIDTH`).
256 ///
257 /// [`ctrl`]: Self::ctrl
258 /// [`find_insert_slot_in_group`]: Self::find_insert_slot_in_group
259 /// [`undefined behavior`]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
260 #[inline]
261 fn fix_insert_slot(&mut self, mut index: usize) -> InsertSlot {
262 // SAFETY: The caller of this function ensures that `index` is in the range `0..=self.bucket_mask`.
263 if unlikely(self.is_bucket_full(index)) {
264 debug_assert!(self.bucket_mask < Group::WIDTH);
265
266 // SAFETY:
267 //
268 // * Since the caller of this function ensures that the control bytes are properly
269 // initialized and `ptr = self.ctrl(0)` points to the start of the array of control
270 // bytes, therefore: `ctrl` is valid for reads, properly aligned to `Group::WIDTH`
271 // and points to the properly initialized control bytes (see also
272 // `TableLayout::calculate_layout_for` and `ptr::read`);
273 //
274 // * Because the caller of this function ensures that the index was provided by the
275 // `self.find_insert_slot_in_group()` function, so for for tables larger than the
276 // group width (self.buckets() >= Group::WIDTH), we will never end up in the given
277 // branch, since `(probe_seq.pos + bit) & self.bucket_mask` in `find_insert_slot_in_group`
278 // cannot return a full bucket index. For tables smaller than the group width, calling
279 // the `unwrap_unchecked` function is also safe, as the trailing control bytes outside
280 // the range of the table are filled with EMPTY bytes (and we know for sure that there
281 // is at least one FULL bucket), so this second scan either finds an empty slot (due to
282 // the load factor) or hits the trailing control bytes (containing EMPTY).
283 index = unsafe {
284 Group::load_aligned(self.ctrl_group(0).as_ptr())
285 .match_empty_or_deleted()
286 .lowest_set_bit()
287 .unwrap_unchecked()
288 };
289 }
290
291 InsertSlot { index }
292 }
293
294 /// Sets a control byte to the hash, and possibly also the replicated control byte at
295 /// the end of the array.
296 ///
297 /// This function does not make any changes to the `data` parts of the table,
298 /// or any changes to the the `items` or `growth_left` field of the table.
299 ///
300 /// # Safety
301 ///
302 /// The caller must ensure that the `index` is not out of bounds of the control allocation.
303 #[inline]
304 fn set_ctrl_h2(&mut self, index: usize, hash: u64) {
305 self.set_ctrl(index, h2(hash));
306 }
307
308 /// Sets a control byte, and possibly also the replicated control byte at
309 /// the end of the array.
310 ///
311 /// This function does not make any changes to the `data` parts of the table,
312 /// or any changes to the the `items` or `growth_left` field of the table.
313 ///
314 /// # Safety
315 ///
316 /// The caller must ensure that `index` is not out of bounds of the control
317 /// allocation.
318 #[inline]
319 fn set_ctrl(&mut self, index: usize, ctrl: u8) {
320 // Replicate the first Group::WIDTH control bytes at the end of
321 // the array without using a branch. If the tables smaller than
322 // the group width (self.buckets() < Group::WIDTH),
323 // `index2 = Group::WIDTH + index`, otherwise `index2` is:
324 //
325 // - If index >= Group::WIDTH then index == index2.
326 // - Otherwise index2 == self.bucket_mask + 1 + index.
327 //
328 // The very last replicated control byte is never actually read because
329 // we mask the initial index for unaligned loads, but we write it
330 // anyways because it makes the set_ctrl implementation simpler.
331 //
332 // If there are fewer buckets than Group::WIDTH then this code will
333 // replicate the buckets at the end of the trailing group. For example
334 // with 2 buckets and a group size of 4, the control bytes will look
335 // like this:
336 //
337 // Real | Replicated
338 // ---------------------------------------------
339 // | [A] | [B] | [EMPTY] | [EMPTY] | [A] | [B] |
340 // ---------------------------------------------
341
342 // This is the same as `(index.wrapping_sub(Group::WIDTH)) % self.buckets() + Group::WIDTH`
343 // because the number of buckets is a power of two, and `self.bucket_mask = self.buckets() - 1`.
344 let index2 = ((index.wrapping_sub(Group::WIDTH)) & self.bucket_mask) + Group::WIDTH;
345
346 // SAFETY: The caller must uphold the safety rules for the
347 // `Constructor::set_ctrl`.
348 *self.ctrl_mut(index) = ctrl;
349 *self.ctrl_mut(index2) = ctrl;
350 }
351
352 /// Checks whether the bucket at `index` is full.
353 ///
354 /// # Safety
355 ///
356 /// The caller must ensure `index` is less than the number of buckets.
357 #[inline]
358 fn is_bucket_full(&self, index: usize) -> bool {
359 debug_assert!(index < self.buckets());
360 is_full(*self.ctrl(index))
361 }
362
363 #[inline]
364 fn ctrl_mut(&mut self, index: usize) -> &mut u8 {
365 debug_assert!(index < self.num_ctrl_bytes());
366
367 let offset = self.ctrl_ptr + index;
368
369 // SAFETY: The control bytes are u8's, which cannot be incorrectly
370 // initialized.
371 let ctrl = unsafe { self.buf.get_mut(offset) };
372
373 let Some(ctrl) = ctrl else {
374 panic!("Missing control byte at {offset}");
375 };
376
377 ctrl
378 }
379
380 #[inline]
381 fn ctrl(&self, index: usize) -> &u8 {
382 debug_assert!(index < self.num_ctrl_bytes());
383
384 let offset = self.ctrl_ptr + index;
385
386 let Some(ctrl) = self.buf.get(offset) else {
387 panic!("Missing control byte at {offset}");
388 };
389
390 ctrl
391 }
392
393 #[inline]
394 fn ctrl_group(&self, index: usize) -> &[u8] {
395 debug_assert!(index < self.num_ctrl_bytes());
396
397 let start = self.ctrl_ptr + index;
398 let end = start + Group::WIDTH;
399
400 let Some(ctrl) = self.buf.get(start..end) else {
401 panic!("Missing control byte at range {start}..{end}");
402 };
403
404 ctrl
405 }
406
407 #[inline]
408 fn num_ctrl_bytes(&self) -> usize {
409 self.bucket_mask + 1 + Group::WIDTH
410 }
411}
412
413/// A reference to a hash table bucket containing a `T`.
414///
415/// This is usually just a pointer to the element itself. However if the element
416/// is a ZST, then we instead track the index of the element in the table so
417/// that `erase` works properly.
418pub struct Bucket<'a, T> {
419 data: NonNull<u8>,
420 _marker: PhantomData<&'a mut T>,
421}
422
423impl<'a, T> Bucket<'a, T> {
424 /// Construct a bucket from the given slice.
425 ///
426 /// # Safety
427 ///
428 /// Caller must ensure that the slice is sized for `T`.
429 #[inline]
430 unsafe fn from_slice(data: &'a mut [u8]) -> Self {
431 debug_assert!(data.len() == size_of::<T>());
432
433 Self {
434 data: unsafe { NonNull::new_unchecked(data.as_mut_ptr()) },
435 _marker: PhantomData,
436 }
437 }
438
439 /// Overwrites a memory location with the given `value` without reading or
440 /// dropping the old value (like [`ptr::write`] function).
441 ///
442 /// [`ptr::write`]: core::ptr::write
443 #[inline]
444 pub(crate) fn write(&self, value: &T)
445 where
446 T: ZeroCopy,
447 {
448 // SAFETY: During bucket construction we've asserted that a buffer of
449 // the appropriate size (that is not guaranteed to be aligned) is used.
450 unsafe { buf::store_unaligned(self.data.cast(), value) }
451 }
452}
453
454/// A reference to an empty bucket into which an can be inserted.
455pub struct InsertSlot {
456 index: usize,
457}
458
459/// Returns the maximum effective capacity for the given bucket mask, taking
460/// the maximum load factor into account.
461#[inline]
462fn bucket_mask_to_capacity(bucket_mask: usize) -> usize {
463 if bucket_mask < 8 {
464 // For tables with 1/2/4/8 buckets, we always reserve one empty slot.
465 // Keep in mind that the bucket mask is one less than the bucket count.
466 bucket_mask
467 } else {
468 // For larger tables we reserve 12.5% of the slots as empty.
469 ((bucket_mask + 1) / 8) * 7
470 }
471}