Skip to main content

fbl/
hash_table.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7use crate::array::Array;
8use crate::doubly_linked_list::{self, DoublyLinkedList, DoublyLinkedListContainable};
9use crate::ptr_traits::{ManagedPtr, PtrTraits};
10use crate::singly_linked_list::{self, SinglyLinkedList, SinglyLinkedListContainable};
11use crate::size_tracker::SizeTracker;
12use core::borrow::Borrow;
13use core::marker::PhantomData;
14use core::pin::Pin;
15use core::ptr::NonNull;
16use kalloc::AllocError;
17
18/// Trait that types must implement to expose the key used to store them in a `HashTable`.
19///
20/// This is the Rust analog of the `GetKey` portion of C++'s `fbl::DefaultKeyedObjectTraits`.
21/// Keys are compared for equality using `K: Eq` (the analog of `KeyTraits::EqualTo`).
22///
23/// The key of an object must not change while the object is contained in a `HashTable`.
24pub trait HashTableKeyable<K: ?Sized> {
25    /// The type of key yielded by `get_key`, borrowing `K`.
26    ///
27    /// Implementations may return an owned/by-value key (e.g. `u64`) or a borrowed/by-reference
28    /// key (e.g. `&'a KeyStruct`).
29    type Key<'a>: Borrow<K>
30    where
31        Self: 'a;
32
33    /// Returns the key of this object.
34    fn get_key(&self) -> Self::Key<'_>;
35}
36
37/// Traits defining the hash function used by a `HashTable` to select a bucket for a key.
38///
39/// `get_hash` may return any `usize`; the hash table reduces it modulo its bucket count to
40/// determine the index of the bucket the key belongs in.
41pub trait HashTraits<K: ?Sized> {
42    /// Returns the hash value of `key`.
43    fn get_hash(key: &K) -> usize;
44}
45
46/// Key types which can be hashed by `DefaultHashTraits`.
47///
48/// This is the Rust analog of the static `GetHash` method which C++'s `fbl::DefaultHashTraits`
49/// requires. Implementations should return what amounts to a "large number"; the hash table
50/// takes it modulo its bucket count to determine the bucket index.
51///
52/// This trait is implemented for all primitive integer types, returning the value of the integer
53/// itself (converted with `as usize`).
54pub trait DefaultHashable {
55    /// Returns the (unreduced) hash value of this key.
56    fn hash_key(&self) -> usize;
57}
58
59macro_rules! impl_default_hashable_for_int {
60    ($($t:ty),*) => {
61        $(
62            impl DefaultHashable for $t {
63                #[inline]
64                fn hash_key(&self) -> usize {
65                    *self as usize
66                }
67            }
68        )*
69    };
70}
71
72impl_default_hashable_for_int!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
73
74/// The default `HashTraits` implementation for a `HashTable`.
75///
76/// The hash value of a key is the value returned by its `DefaultHashable::hash_key`.
77#[derive(Debug, Default, Clone, Copy)]
78pub struct DefaultHashTraits;
79
80impl<K: DefaultHashable + ?Sized> HashTraits<K> for DefaultHashTraits {
81    #[inline]
82    fn get_hash(key: &K) -> usize {
83        key.hash_key()
84    }
85}
86
87mod sealed {
88    use super::*;
89
90    pub trait Sealed {}
91
92    impl<P, Tag, S> Sealed for SinglyLinkedList<P, Tag, S>
93    where
94        P: PtrTraits,
95        P::Target: SinglyLinkedListContainable<P::Target, Tag>,
96        S: SizeTracker,
97    {
98    }
99
100    impl<P, Tag, S> Sealed for DoublyLinkedList<P, Tag, S>
101    where
102        P: PtrTraits,
103        P::Target: DoublyLinkedListContainable<P::Target, Tag>,
104        S: SizeTracker,
105    {
106    }
107}
108
109/// A list type which can be used as the bucket type of a `HashTable` of pointers of type `P`.
110///
111/// This trait is sealed. It is implemented for:
112///
113/// * `SinglyLinkedList<P, Tag>`: Erasing a specific element (e.g. with
114///   `HashTable::erase_element`) searches its bucket, so it runs in time proportional to the
115///   length of the bucket.
116/// * `DoublyLinkedList<P, Tag>`: Erasing a specific element runs in constant time. The buckets
117///   must not move once initialized; the hash table keeps them pinned in its bucket storage.
118pub trait HashTableBucket<P: PtrTraits>: sealed::Sealed + Sized {
119    /// An iterator over the elements of a bucket.
120    type Iter<'a>: core::iter::Iterator<Item = &'a P::Target>
121    where
122        Self: 'a,
123        P: 'a;
124
125    /// Allocates the storage for `count` empty buckets.
126    fn try_new_buckets(count: usize) -> Result<Pin<Array<Self>>, AllocError>;
127
128    /// Returns true if the bucket is empty.
129    fn is_empty(&self) -> bool;
130
131    /// Pushes an element to the front of the bucket.
132    ///
133    /// # Safety
134    ///
135    /// The caller must ensure that `ptr` is a valid pointer to an object that is not currently in
136    /// any container, and that the object outlives the reference from the bucket.
137    unsafe fn push_front_raw(self: Pin<&mut Self>, ptr: P);
138
139    /// Finds the first element of the bucket which satisfies the predicate.
140    fn find_if<F>(&self, f: F) -> Option<&P::Target>
141    where
142        F: FnMut(&P::Target) -> bool;
143
144    /// Finds the first element of the bucket which satisfies the predicate, erases it from the
145    /// bucket, and returns it.
146    fn erase_if<F>(self: Pin<&mut Self>, f: F) -> Option<P>
147    where
148        F: FnMut(&P::Target) -> bool;
149
150    /// Erases `obj` from the bucket, returning it. Returns `None` if `obj` is not in any container.
151    ///
152    /// # Safety
153    ///
154    /// The caller must ensure that `obj` is either not in any container, or is currently in this
155    /// bucket.
156    unsafe fn erase(self: Pin<&mut Self>, obj: &P::Target) -> Option<P>;
157
158    /// Replaces `obj` with `replacement` in the bucket, returning the replaced element.
159    ///
160    /// # Safety
161    ///
162    /// The caller must ensure that `obj` is currently in this bucket, and that `replacement` is a
163    /// valid pointer to an object that is not currently in any container and that outlives the
164    /// reference from the bucket.
165    unsafe fn replace_raw(self: Pin<&mut Self>, obj: &P::Target, replacement: P) -> Option<P>;
166
167    /// Removes all elements from the bucket.
168    fn clear(self: Pin<&mut Self>);
169
170    /// Returns an iterator over the elements of the bucket.
171    fn iter<'a>(&'a self) -> Self::Iter<'a>
172    where
173        P: 'a;
174}
175
176/// Returns mutable access to a pinned `SinglyLinkedList` bucket.
177#[inline]
178fn unpin_singly_linked_list<P, Tag, S>(
179    list: Pin<&mut SinglyLinkedList<P, Tag, S>>,
180) -> &mut SinglyLinkedList<P, Tag, S>
181where
182    P: PtrTraits,
183    P::Target: SinglyLinkedListContainable<P::Target, Tag>,
184    S: SizeTracker,
185{
186    // SAFETY: A `SinglyLinkedList` does not depend on its own address (its elements never point
187    // back to the list), so it may be moved freely and does not need to stay pinned.
188    unsafe { list.get_unchecked_mut() }
189}
190
191impl<P, Tag, S> HashTableBucket<P> for SinglyLinkedList<P, Tag, S>
192where
193    P: PtrTraits,
194    P::Target: SinglyLinkedListContainable<P::Target, Tag>,
195    S: SizeTracker,
196{
197    type Iter<'a>
198        = singly_linked_list::Iterator<'a, P, Tag>
199    where
200        Self: 'a,
201        P: 'a;
202
203    #[inline]
204    fn try_new_buckets(count: usize) -> Result<Pin<Array<Self>>, AllocError> {
205        Ok(Array::<Self>::try_new(count)?.into_pin())
206    }
207
208    #[inline]
209    fn is_empty(&self) -> bool {
210        SinglyLinkedList::is_empty(self)
211    }
212
213    #[inline]
214    unsafe fn push_front_raw(self: Pin<&mut Self>, ptr: P) {
215        // SAFETY: The caller guarantees that `ptr` is valid, not in any container, and outlives
216        // its reference from the list.
217        unsafe { SinglyLinkedList::push_front_raw(unpin_singly_linked_list(self), ptr) }
218    }
219
220    #[inline]
221    fn find_if<F>(&self, f: F) -> Option<&P::Target>
222    where
223        F: FnMut(&P::Target) -> bool,
224    {
225        SinglyLinkedList::find_if(self, f)
226    }
227
228    #[inline]
229    fn erase_if<F>(self: Pin<&mut Self>, f: F) -> Option<P>
230    where
231        F: FnMut(&P::Target) -> bool,
232    {
233        SinglyLinkedList::erase_if(unpin_singly_linked_list(self), f)
234    }
235
236    #[inline]
237    unsafe fn erase(self: Pin<&mut Self>, obj: &P::Target) -> Option<P> {
238        if !<P::Target as SinglyLinkedListContainable<P::Target, Tag>>::get_node(obj).in_container()
239        {
240            return None;
241        }
242        SinglyLinkedList::erase_if(unpin_singly_linked_list(self), |other| {
243            core::ptr::eq(other, obj)
244        })
245    }
246
247    #[inline]
248    unsafe fn replace_raw(self: Pin<&mut Self>, obj: &P::Target, replacement: P) -> Option<P> {
249        // SAFETY: The caller guarantees that `replacement` is valid, not in any container, and
250        // outlives its reference from the list.
251        unsafe {
252            SinglyLinkedList::replace_if_raw(
253                unpin_singly_linked_list(self),
254                |other| core::ptr::eq(other, obj),
255                replacement,
256            )
257        }
258    }
259
260    #[inline]
261    fn clear(self: Pin<&mut Self>) {
262        SinglyLinkedList::clear(unpin_singly_linked_list(self))
263    }
264
265    #[inline]
266    fn iter<'a>(&'a self) -> Self::Iter<'a>
267    where
268        P: 'a,
269    {
270        SinglyLinkedList::iter(self)
271    }
272}
273
274impl<P, Tag, S> HashTableBucket<P> for DoublyLinkedList<P, Tag, S>
275where
276    P: PtrTraits,
277    P::Target: DoublyLinkedListContainable<P::Target, Tag>,
278    S: SizeTracker,
279{
280    type Iter<'a>
281        = doubly_linked_list::ForwardIterator<'a, P, Tag>
282    where
283        Self: 'a,
284        P: 'a;
285
286    #[inline]
287    fn try_new_buckets(count: usize) -> Result<Pin<Array<Self>>, AllocError> {
288        Array::try_new_pin_init(count, Self::new)
289    }
290
291    #[inline]
292    fn is_empty(&self) -> bool {
293        DoublyLinkedList::is_empty(self)
294    }
295
296    #[inline]
297    unsafe fn push_front_raw(self: Pin<&mut Self>, ptr: P) {
298        // SAFETY: The caller guarantees that `ptr` is valid, not in any container, and outlives
299        // its reference from the list.
300        unsafe { DoublyLinkedList::push_front_raw(self, ptr) }
301    }
302
303    #[inline]
304    fn find_if<F>(&self, f: F) -> Option<&P::Target>
305    where
306        F: FnMut(&P::Target) -> bool,
307    {
308        DoublyLinkedList::find_if(self, f)
309    }
310
311    #[inline]
312    fn erase_if<F>(self: Pin<&mut Self>, f: F) -> Option<P>
313    where
314        F: FnMut(&P::Target) -> bool,
315    {
316        DoublyLinkedList::erase_if(self, f)
317    }
318
319    #[inline]
320    unsafe fn erase(self: Pin<&mut Self>, obj: &P::Target) -> Option<P> {
321        // SAFETY: The caller guarantees that `obj` is either in no container (in which case the
322        // list returns `None`) or in this list.
323        unsafe { DoublyLinkedList::erase(self, obj) }
324    }
325
326    #[inline]
327    unsafe fn replace_raw(self: Pin<&mut Self>, obj: &P::Target, replacement: P) -> Option<P> {
328        // SAFETY: The caller guarantees that `obj` is in this list, and that `replacement` is
329        // valid, not in any container, and outlives its reference from the list.
330        unsafe { DoublyLinkedList::replace_raw(self, obj, replacement) }
331    }
332
333    #[inline]
334    fn clear(self: Pin<&mut Self>) {
335        DoublyLinkedList::clear(self)
336    }
337
338    #[inline]
339    fn iter<'a>(&'a self) -> Self::Iter<'a>
340    where
341        P: 'a,
342    {
343        self.forward_iter()
344    }
345}
346
347/// The result of `HashTable::insert_or_find`.
348///
349/// `Ok(())` if the element was inserted, or `Err((ptr, cursor))` with the rejected pointer and a
350/// cursor positioned at the colliding element otherwise.
351pub type HashTableInsertOrFindResult<'a, K, P, B, H> =
352    Result<(), (P, HashTableCursorMut<'a, K, P, B, H>)>;
353
354/// An intrusive, unordered, associative hash table container.
355///
356/// ### Bookkeeping & Memory Storage
357///
358/// Objects are stored in an array of buckets, each of which is an intrusive linked list (see
359/// "Buckets" below). The bookkeeping storage required to link an object into its bucket (a list
360/// node) lives on the object itself, so adding or removing members to/from the container never
361/// requires allocation. The only allocation performed by the hash table is the one-time
362/// allocation of its bucket array, which is fallible.
363///
364/// The hash table stores pointers to the objects, not the objects themselves, and is parameterized
365/// based on the specific pointer wrapper to be stored (`P`). Supported pointer wrappers are:
366///
367/// * `*mut T`       : Raw unmanaged pointers.
368/// * `UniquePtr<T>` : Unique managed pointers.
369/// * `RefPtr<T>`    : Shared managed pointers to reference-counted objects.
370///
371/// ### Buckets
372///
373/// The type of the buckets is selected by the `B` parameter (see `HashTableBucket`):
374///
375/// * `SinglyLinkedList<P, Tag>` (the default): Objects need a `SinglyLinkedListNode`, which is a
376///   single pointer. Erasing an element by reference searches its bucket.
377/// * `DoublyLinkedList<P, Tag>`: Objects need a `DoublyLinkedListNode`, which is two pointers.
378///   Erasing an element by reference runs in constant time.
379///
380/// The `Tag` of the bucket type selects the node used by the hash table, which allows an object
381/// to be in a hash table and in other containers at the same time.
382///
383/// ### Keys & Hashing
384///
385/// Objects expose their key of type `K` through `HashTableKeyable<K>`, and keys are compared using
386/// `K: Eq`. Duplicate keys are not allowed. The bucket an object belongs to is the hash of its
387/// key, as computed by the hash traits `H` (see `HashTraits`, which default to
388/// `DefaultHashTraits`), modulo the bucket count of the hash table.
389///
390/// The key of an object must not change while the object is in the hash table.
391///
392/// ### Bucket Storage & Initialization
393///
394/// The bucket count of a `HashTable` is chosen at runtime. A hash table may be created with its
395/// bucket storage in a single step using `try_new`, or created in an "invalid" (uninitialized)
396/// state using the infallible `const fn new` and later initialized exactly once using `try_init`
397/// (the analog of C++'s `HashTableOption::DelayedInit` followed by `Init`). An uninitialized hash
398/// table is empty and may be cleared, iterated, or dropped, but it must be initialized before any
399/// keyed operation (insert, find, erase, ...) is performed on it.
400///
401/// The bucket storage is heap allocated and its buckets never move, so unlike the other intrusive
402/// containers in this crate, a `HashTable` itself does not need to be pinned (even with
403/// `DoublyLinkedList` buckets) and may be freely moved.
404///
405/// Bucket storage with a bucket count fixed at compile time is not currently supported.
406///
407/// ### Lifecycle Management
408///
409/// * **Managed Pointers (`UniquePtr`/`RefPtr`)**: The hash table holds ownership references of
410///   elements. Clearing the hash table or dropping it automatically releases references, which may
411///   destruct the elements if it was their last reference.
412///
413/// * **Unmanaged Pointers (`*mut T`)**: The hash table performs no lifecycle management. It is up
414///   to the caller to ensure elements outlive the hash table and are freed correctly. As a safety
415///   check, a hash table of unmanaged pointers will debug-assert if it is dropped with elements
416///   still inside.
417///
418/// ### Complexity
419///
420/// Insert, find and erase-by-key run in time proportional to the length of the bucket the key
421/// hashes to. Direct erase operations (where a reference to the element to be erased is already
422/// known, e.g. `erase_element` and `HashTableCursorMut::erase`) run in constant time with
423/// `DoublyLinkedList` buckets, and in time proportional to the length of the bucket with
424/// `SinglyLinkedList` buckets. `len` and `is_empty` run in constant time.
425///
426/// ---
427///
428/// ### Example: Hash table of unique managed pointers
429///
430/// ```rust
431/// use fbl::{
432///     DoublyLinkedList, DoublyLinkedListContainable, DoublyLinkedListNode, HashTable,
433///     HashTableKeyable, Recyclable, SinglyLinkedListContainable, SinglyLinkedListNode, UniquePtr,
434/// };
435///
436/// #[derive(SinglyLinkedListContainable, DoublyLinkedListContainable, Recyclable)]
437/// struct Foo {
438///     id: u64,
439///     #[sll_node]
440///     sll_node: SinglyLinkedListNode<Foo>,
441///     #[dll_node]
442///     dll_node: DoublyLinkedListNode<Foo>,
443/// }
444///
445/// impl Foo {
446///     fn new(id: u64) -> UniquePtr<Foo> {
447///         let sll_node = SinglyLinkedListNode::new();
448///         let dll_node = DoublyLinkedListNode::new();
449///         UniquePtr::try_new(Foo { id, sll_node, dll_node }).unwrap()
450///     }
451/// }
452///
453/// impl HashTableKeyable<u64> for Foo {
454///     type Key<'a> = u64;
455///     fn get_key(&self) -> u64 {
456///         self.id
457///     }
458/// }
459///
460/// // A hash table with the default `SinglyLinkedList` buckets.
461/// let mut table = HashTable::<u64, UniquePtr<Foo>>::try_new(37).unwrap();
462/// table.insert(Foo::new(1));
463/// table.insert(Foo::new(2));
464///
465/// assert_eq!(table.len(), 2);
466/// assert_eq!(table.find(&1).map(|foo| foo.id), Some(1));
467///
468/// let foo = table.erase(&2).unwrap();
469/// assert_eq!(foo.id, 2);
470///
471/// // A hash table with `DoublyLinkedList` buckets, which can erase an element by reference in
472/// // constant time.
473/// type DllBuckets = DoublyLinkedList<UniquePtr<Foo>>;
474/// let mut dll_table = HashTable::<u64, UniquePtr<Foo>, DllBuckets>::try_new(37).unwrap();
475/// dll_table.insert(foo);
476/// let foo: *const Foo = dll_table.find(&2).unwrap();
477/// // SAFETY: `foo` is in `dll_table` and its key has not changed.
478/// assert!(unsafe { dll_table.erase_element(&*foo) }.is_some());
479///
480/// // Dropping the tables automatically drops the remaining unique pointers.
481/// ```
482pub struct HashTable<K, P, B = SinglyLinkedList<P>, H = DefaultHashTraits>
483where
484    P: PtrTraits,
485    P::Target: HashTableKeyable<K>,
486    B: HashTableBucket<P>,
487    K: Eq,
488    H: HashTraits<K>,
489{
490    /// The number of elements currently in the hash table.
491    count: usize,
492
493    /// The bucket storage.
494    ///
495    /// The buckets are pinned: they live in a heap allocation which is never resized, and they
496    /// are only ever accessed in place (see `buckets_mut`). The storage is only ever replaced
497    /// wholesale while it holds no buckets.
498    ///
499    /// An empty array means that the hash table has not been initialized yet.
500    buckets: Pin<Array<B>>,
501
502    _phantom: PhantomData<(K, P, H)>,
503}
504
505impl<K, P, B, H> HashTable<K, P, B, H>
506where
507    P: PtrTraits,
508    P::Target: HashTableKeyable<K>,
509    B: HashTableBucket<P>,
510    K: Eq,
511    H: HashTraits<K>,
512{
513    /// Creates a new, uninitialized hash table without any bucket storage.
514    ///
515    /// The hash table must be initialized with `try_init` before any keyed operation is performed
516    /// on it. This is the analog of constructing a C++ `fbl::HashTable` with
517    /// `HashTableOption::DelayedInit`.
518    #[inline]
519    pub const fn new() -> Self {
520        Self { count: 0, buckets: Array::new().into_pin(), _phantom: PhantomData }
521    }
522
523    /// Creates a new, empty hash table with `bucket_count` buckets.
524    ///
525    /// Returns an error if the bucket storage could not be allocated.
526    ///
527    /// # Panics
528    ///
529    /// Panics if `bucket_count` is zero.
530    pub fn try_new(bucket_count: usize) -> Result<Self, AllocError> {
531        let mut table = Self::new();
532        table.try_init(bucket_count)?;
533        Ok(table)
534    }
535
536    /// Initializes a hash table created with `new` with `bucket_count` buckets.
537    ///
538    /// Initialization is not idempotent and must be performed exactly once before use. Returns an
539    /// error if the bucket storage could not be allocated, in which case the hash table remains
540    /// uninitialized.
541    ///
542    /// # Panics
543    ///
544    /// Panics if `bucket_count` is zero, or if the hash table has already been initialized.
545    pub fn try_init(&mut self, bucket_count: usize) -> Result<(), AllocError> {
546        assert!(!self.is_initialized(), "HashTable may only be initialized once");
547        assert!(bucket_count > 0, "HashTables must have at least one bucket");
548        self.buckets = B::try_new_buckets(bucket_count)?;
549        Ok(())
550    }
551
552    /// Returns true if the hash table has been given its bucket storage.
553    #[inline]
554    pub fn is_initialized(&self) -> bool {
555        !self.buckets.is_empty()
556    }
557
558    /// Returns the number of buckets in the hash table, or zero if it has not been initialized.
559    #[inline]
560    pub fn bucket_count(&self) -> usize {
561        self.buckets.len()
562    }
563
564    /// Returns the number of elements in the hash table.
565    #[inline]
566    pub fn len(&self) -> usize {
567        self.count
568    }
569
570    /// Returns true if the hash table contains no elements.
571    #[inline]
572    pub fn is_empty(&self) -> bool {
573        self.count == 0
574    }
575
576    /// Returns pinned mutable access to the bucket at index `ndx`.
577    ///
578    /// # Panics
579    ///
580    /// Panics if `ndx` is out of bounds.
581    #[inline]
582    fn bucket_mut(&mut self, ndx: usize) -> Pin<&mut B> {
583        // SAFETY: The buckets are pinned in `self.buckets`, and projecting to one of them in place
584        // does not move it.
585        unsafe { self.buckets.as_mut().map_unchecked_mut(|buckets| &mut buckets[ndx]) }
586    }
587
588    /// Returns the index of the bucket `key` belongs in.
589    #[inline]
590    fn bucket_index(&self, key: &K) -> usize {
591        debug_assert!(self.is_initialized(), "HashTable used before initialization");
592        H::get_hash(key) % self.buckets.len()
593    }
594
595    /// Returns the index of the bucket `obj` belongs in.
596    #[inline]
597    fn bucket_index_of(&self, obj: &P::Target) -> usize {
598        let obj_key = <P::Target as HashTableKeyable<K>>::get_key(obj);
599        self.bucket_index(obj_key.borrow())
600    }
601
602    /// Returns true if the key of `obj` is equal to `key`.
603    #[inline]
604    fn key_matches(obj: &P::Target, key: &K) -> bool {
605        let obj_key = <P::Target as HashTableKeyable<K>>::get_key(obj);
606        let obj_key: &K = obj_key.borrow();
607        obj_key == key
608    }
609
610    /// Returns a pointer to the element of `bucket` whose key is `key`, if there is one.
611    #[inline]
612    fn find_in_bucket(bucket: &B, key: &K) -> Option<NonNull<P::Target>> {
613        bucket.find_if(|obj| Self::key_matches(obj, key)).map(NonNull::from)
614    }
615
616    /// Locates the bucket for the object pointed to by `ptr`, and any element already in that
617    /// bucket with the same key. Returns the bucket index and a pointer to the colliding element,
618    /// if there is one.
619    #[inline]
620    fn locate(&self, ptr: &P) -> (usize, Option<NonNull<P::Target>>) {
621        let obj_key = <P::Target as HashTableKeyable<K>>::get_key(PtrTraits::get_ref(ptr));
622        let key: &K = obj_key.borrow();
623        let ndx = self.bucket_index(key);
624        (ndx, Self::find_in_bucket(&self.buckets[ndx], key))
625    }
626
627    /// Inserts an element into the hash table.
628    ///
629    /// Duplicate keys are disallowed. If there might already be an element with the same key in
630    /// the hash table, use `insert_or_find` or `insert_or_replace` instead.
631    ///
632    /// For raw pointers, use `insert_raw` instead.
633    ///
634    /// # Panics
635    ///
636    /// Panics if the hash table has not been initialized. Panics in debug builds if the object is
637    /// already in a container, or if an element with the same key is already in the hash table.
638    #[inline]
639    pub fn insert(&mut self, ptr: P)
640    where
641        P: ManagedPtr,
642    {
643        // SAFETY: `P` is a `ManagedPtr`, which guarantees that the pointer is valid and that the
644        // object will outlive its reference from this hash table.
645        unsafe { self.insert_raw(ptr) }
646    }
647
648    /// Inserts an element into the hash table.
649    ///
650    /// Duplicate keys are disallowed. If there might already be an element with the same key in
651    /// the hash table, use `insert_or_find_raw` or `insert_or_replace_raw` instead.
652    ///
653    /// # Panics
654    ///
655    /// Panics if the hash table has not been initialized. Panics in debug builds if the object is
656    /// already in a container, or if an element with the same key is already in the hash table.
657    ///
658    /// # Safety
659    ///
660    /// The caller must ensure that `ptr` is a valid pointer to an object that is not currently in
661    /// any container, and that the object outlives the reference from the hash table.
662    pub unsafe fn insert_raw(&mut self, ptr: P) {
663        let (ndx, existing) = self.locate(&ptr);
664
665        // Duplicate keys are disallowed. Debug assert if someone tries to insert an element with a
666        // duplicate key. If the user thought that there might be a duplicate key in the hash table
667        // already, they should have used insert_or_find() instead.
668        debug_assert!(existing.is_none(), "Duplicate keys are not allowed in a HashTable");
669
670        // SAFETY: The caller guarantees that `ptr` is valid, not in any container, and outlives
671        // its reference from the bucket.
672        unsafe { self.bucket_mut(ndx).push_front_raw(ptr) };
673        self.count += 1;
674    }
675
676    /// Inserts the element pointed to by `ptr` if no element with the same key is already in the
677    /// hash table, or finds the element that `ptr` collided with instead.
678    ///
679    /// For raw pointers, use `insert_or_find_raw` instead.
680    ///
681    /// # Returns
682    ///
683    /// * `Ok(())` if there was no collision and the element was successfully inserted. In this
684    ///   case, the hash table takes ownership of `ptr`.
685    /// * `Err((ptr, cursor))` if there was a collision. In this case, `ptr` is not consumed and is
686    ///   returned to the caller, along with a cursor positioned at the colliding element already in
687    ///   the hash table.
688    ///
689    /// # Panics
690    ///
691    /// Panics if the hash table has not been initialized. Panics in debug builds if the object is
692    /// already in a container.
693    #[inline]
694    pub fn insert_or_find(&mut self, ptr: P) -> HashTableInsertOrFindResult<'_, K, P, B, H>
695    where
696        P: ManagedPtr,
697    {
698        // SAFETY: `P` is a `ManagedPtr`, which guarantees that the pointer is valid and that the
699        // object will outlive its reference from this hash table.
700        unsafe { self.insert_or_find_raw(ptr) }
701    }
702
703    /// Inserts the element pointed to by `ptr` if no element with the same key is already in the
704    /// hash table, or finds the element that `ptr` collided with instead.
705    ///
706    /// See `insert_or_find` for a description of the return value.
707    ///
708    /// # Panics
709    ///
710    /// Panics if the hash table has not been initialized. Panics in debug builds if the object is
711    /// already in a container.
712    ///
713    /// # Safety
714    ///
715    /// The caller must ensure that `ptr` is a valid pointer to an object that is not currently in
716    /// any container, and that the object outlives the reference from the hash table.
717    pub unsafe fn insert_or_find_raw(
718        &mut self,
719        ptr: P,
720    ) -> HashTableInsertOrFindResult<'_, K, P, B, H> {
721        let (ndx, existing) = self.locate(&ptr);
722        if existing.is_some() {
723            return Err((ptr, HashTableCursorMut { table: self, bucket: ndx, current: existing }));
724        }
725
726        // SAFETY: The caller guarantees that `ptr` is valid, not in any container, and outlives
727        // its reference from the bucket.
728        unsafe { self.bucket_mut(ndx).push_front_raw(ptr) };
729        self.count += 1;
730        Ok(())
731    }
732
733    /// Finds the element in the hash table with the same key as `*ptr` and replaces it with `ptr`,
734    /// returning the element which was replaced.
735    ///
736    /// If no element in the hash table shares a key with `*ptr`, simply adds `ptr` to the hash
737    /// table and returns `None`. In both cases, `ptr` is consumed.
738    ///
739    /// For raw pointers, use `insert_or_replace_raw` instead.
740    ///
741    /// # Panics
742    ///
743    /// Panics if the hash table has not been initialized. Panics in debug builds if the object is
744    /// already in a container.
745    #[inline]
746    pub fn insert_or_replace(&mut self, ptr: P) -> Option<P>
747    where
748        P: ManagedPtr,
749    {
750        // SAFETY: `P` is a `ManagedPtr`, which guarantees that the pointer is valid and that the
751        // object will outlive its reference from this hash table.
752        unsafe { self.insert_or_replace_raw(ptr) }
753    }
754
755    /// Finds the element in the hash table with the same key as `*ptr` and replaces it with `ptr`,
756    /// returning the element which was replaced.
757    ///
758    /// If no element in the hash table shares a key with `*ptr`, simply adds `ptr` to the hash
759    /// table and returns `None`. In both cases, `ptr` is consumed.
760    ///
761    /// # Panics
762    ///
763    /// Panics if the hash table has not been initialized. Panics in debug builds if the object is
764    /// already in a container.
765    ///
766    /// # Safety
767    ///
768    /// The caller must ensure that `ptr` is a valid pointer to an object that is not currently in
769    /// any container, and that the object outlives the reference from the hash table.
770    pub unsafe fn insert_or_replace_raw(&mut self, ptr: P) -> Option<P> {
771        let (ndx, existing) = self.locate(&ptr);
772        let bucket = self.bucket_mut(ndx);
773        match existing {
774            None => {
775                // SAFETY: The caller guarantees that `ptr` is valid, not in any container, and
776                // outlives its reference from the bucket.
777                unsafe { bucket.push_front_raw(ptr) };
778                self.count += 1;
779                None
780            }
781            // SAFETY: `existing` was just found in `bucket` and nothing has modified the bucket
782            // since. The caller guarantees that `ptr` is valid, not in any container, and outlives
783            // its reference from the bucket.
784            Some(existing) => unsafe { bucket.replace_raw(existing.as_ref(), ptr) },
785        }
786    }
787
788    /// Finds the element in the hash table with the given key.
789    ///
790    /// # Panics
791    ///
792    /// Panics if the hash table has not been initialized.
793    #[inline]
794    pub fn find(&self, key: &K) -> Option<&P::Target> {
795        let ndx = self.bucket_index(key);
796        self.buckets[ndx].find_if(|obj| Self::key_matches(obj, key))
797    }
798
799    /// Finds the element in the hash table with the given key and returns a cursor positioned at
800    /// it.
801    ///
802    /// If the key is not found, the returned cursor is not positioned at any element (i.e.
803    /// `cursor.get()` returns `None`).
804    ///
805    /// # Panics
806    ///
807    /// Panics if the hash table has not been initialized.
808    #[inline]
809    pub fn find_cursor(&mut self, key: &K) -> HashTableCursorMut<'_, K, P, B, H> {
810        let ndx = self.bucket_index(key);
811        let current = Self::find_in_bucket(&self.buckets[ndx], key);
812        HashTableCursorMut { table: self, bucket: ndx, current }
813    }
814
815    /// Erases the element with the given key from the hash table, returning it.
816    ///
817    /// Returns `None` if no element in the hash table has the given key.
818    ///
819    /// # Panics
820    ///
821    /// Panics if the hash table has not been initialized.
822    #[inline]
823    pub fn erase(&mut self, key: &K) -> Option<P> {
824        let ndx = self.bucket_index(key);
825        let ret = self.bucket_mut(ndx).erase_if(|obj| Self::key_matches(obj, key));
826        if ret.is_some() {
827            self.count -= 1;
828        }
829        ret
830    }
831
832    /// Erases the given element from the hash table, returning it.
833    ///
834    /// Returns `None` if `obj` is not in any container. This operation runs in constant time with
835    /// `DoublyLinkedList` buckets, and in time proportional to the length of the bucket of `obj`
836    /// with `SinglyLinkedList` buckets.
837    ///
838    /// # Panics
839    ///
840    /// Panics if the hash table has not been initialized.
841    ///
842    /// # Safety
843    ///
844    /// The caller must ensure that `obj` is either not in any container, or is currently in this
845    /// hash table instance and its key has not changed since it was inserted.
846    #[inline]
847    pub unsafe fn erase_element(&mut self, obj: &P::Target) -> Option<P> {
848        let ndx = self.bucket_index_of(obj);
849        // SAFETY: The caller guarantees that `obj` is either in no container (in which case the
850        // bucket returns `None`) or in this hash table with an unchanged key, in which case it is
851        // in the bucket its key hashes to, `self.buckets[ndx]`.
852        let ret = unsafe { self.bucket_mut(ndx).erase(obj) };
853        if ret.is_some() {
854            self.count -= 1;
855        }
856        ret
857    }
858
859    /// Finds the first element of the hash table which satisfies the predicate, erases it from the
860    /// hash table, and returns it. Returns `None` if no element satisfies the predicate.
861    pub fn erase_if<F>(&mut self, mut f: F) -> Option<P>
862    where
863        F: FnMut(&P::Target) -> bool,
864    {
865        debug_assert!(self.is_initialized(), "HashTable used before initialization");
866
867        if self.is_empty() {
868            return None;
869        }
870
871        let ret = (0..self.bucket_count()).find_map(|ndx| self.bucket_mut(ndx).erase_if(&mut f));
872        if ret.is_some() {
873            self.count -= 1;
874        }
875        ret
876    }
877
878    /// Finds the first element of the hash table which satisfies the predicate. Returns `None` if
879    /// no element satisfies the predicate.
880    #[inline]
881    pub fn find_if<F>(&self, mut f: F) -> Option<&P::Target>
882    where
883        F: FnMut(&P::Target) -> bool,
884    {
885        debug_assert!(self.is_initialized(), "HashTable used before initialization");
886        self.iter().find(|&obj| f(obj))
887    }
888
889    /// Removes all elements from the hash table.
890    ///
891    /// For managed pointer types, this releases all of the references held by the hash table to
892    /// the objects which were in it. Clearing an uninitialized hash table is a no-op.
893    pub fn clear(&mut self) {
894        for ndx in 0..self.bucket_count() {
895            self.bucket_mut(ndx).clear();
896        }
897        self.count = 0;
898    }
899
900    /// Returns an iterator over the elements of the hash table, in an unspecified order.
901    #[inline]
902    pub fn iter(&self) -> HashTableIter<'_, P, B> {
903        HashTableIter { buckets: self.buckets.iter(), current: None, remaining: self.count }
904    }
905}
906
907impl<K, P, B, H> Default for HashTable<K, P, B, H>
908where
909    P: PtrTraits,
910    P::Target: HashTableKeyable<K>,
911    B: HashTableBucket<P>,
912    K: Eq,
913    H: HashTraits<K>,
914{
915    /// Creates a new, uninitialized hash table. See `HashTable::new`.
916    #[inline]
917    fn default() -> Self {
918        Self::new()
919    }
920}
921
922impl<K, P, B, H> Drop for HashTable<K, P, B, H>
923where
924    P: PtrTraits,
925    P::Target: HashTableKeyable<K>,
926    B: HashTableBucket<P>,
927    K: Eq,
928    H: HashTraits<K>,
929{
930    fn drop(&mut self) {
931        if P::IS_MANAGED {
932            self.clear();
933        } else {
934            debug_assert!(self.is_empty(), "HashTable must be empty on destruction");
935        }
936    }
937}
938
939impl<K, P, B, H> core::fmt::Debug for HashTable<K, P, B, H>
940where
941    P: PtrTraits,
942    P::Target: HashTableKeyable<K> + core::fmt::Debug,
943    B: HashTableBucket<P>,
944    K: Eq,
945    H: HashTraits<K>,
946{
947    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
948        f.debug_set().entries(self.iter()).finish()
949    }
950}
951
952impl<'a, K, P, B, H> IntoIterator for &'a HashTable<K, P, B, H>
953where
954    P: PtrTraits,
955    P::Target: HashTableKeyable<K>,
956    B: HashTableBucket<P>,
957    K: Eq,
958    H: HashTraits<K>,
959{
960    type Item = &'a P::Target;
961    type IntoIter = HashTableIter<'a, P, B>;
962
963    #[inline]
964    fn into_iter(self) -> Self::IntoIter {
965        self.iter()
966    }
967}
968
969/// A cursor positioned at an element of a `HashTable` (or at no element), which can be used to
970/// access or erase that element.
971///
972/// Returned by `HashTable::find_cursor` and `HashTable::insert_or_find`.
973pub struct HashTableCursorMut<'a, K, P, B = SinglyLinkedList<P>, H = DefaultHashTraits>
974where
975    P: PtrTraits,
976    P::Target: HashTableKeyable<K>,
977    B: HashTableBucket<P>,
978    K: Eq,
979    H: HashTraits<K>,
980{
981    table: &'a mut HashTable<K, P, B, H>,
982    /// The index of the bucket containing `current`.
983    bucket: usize,
984    /// The element the cursor is positioned at, if any.
985    current: Option<NonNull<P::Target>>,
986}
987
988impl<'a, K, P, B, H> HashTableCursorMut<'a, K, P, B, H>
989where
990    P: PtrTraits,
991    P::Target: HashTableKeyable<K>,
992    B: HashTableBucket<P>,
993    K: Eq,
994    H: HashTraits<K>,
995{
996    /// Returns a reference to the element the cursor is positioned at, or `None` if it is not
997    /// positioned at any element.
998    #[inline]
999    pub fn get(&self) -> Option<&P::Target> {
1000        // SAFETY: `self.current` points to an element of `self.table`, which has been exclusively
1001        // borrowed by this cursor since the element was found, so it is still valid.
1002        self.current.map(|current| unsafe { current.as_ref() })
1003    }
1004
1005    /// Erases the element the cursor is positioned at from the hash table, returning it.
1006    ///
1007    /// Returns `None` if the cursor is not positioned at any element.
1008    #[inline]
1009    pub fn erase(self) -> Option<P> {
1010        let current = self.current?;
1011        // SAFETY: `current` was found in `self.table.buckets[self.bucket]`, and the table has been
1012        // exclusively borrowed by this cursor since, so it is still valid and in that bucket.
1013        let ret = unsafe { self.table.bucket_mut(self.bucket).erase(current.as_ref()) };
1014        if ret.is_some() {
1015            self.table.count -= 1;
1016        }
1017        ret
1018    }
1019}
1020
1021/// An iterator over the elements of a `HashTable`, in an unspecified order.
1022pub struct HashTableIter<'a, P, B = SinglyLinkedList<P>>
1023where
1024    P: PtrTraits + 'a,
1025    B: HashTableBucket<P>,
1026{
1027    /// The buckets which have not been visited yet.
1028    buckets: core::slice::Iter<'a, B>,
1029    /// The iterator over the bucket currently being visited, if any.
1030    current: Option<B::Iter<'a>>,
1031    /// The number of elements which have not been yielded yet.
1032    remaining: usize,
1033}
1034
1035impl<'a, P, B> Iterator for HashTableIter<'a, P, B>
1036where
1037    P: PtrTraits + 'a,
1038    B: HashTableBucket<P> + 'a,
1039{
1040    type Item = &'a P::Target;
1041
1042    fn next(&mut self) -> Option<Self::Item> {
1043        loop {
1044            if let Some(obj) = self.current.as_mut().and_then(|iter| iter.next()) {
1045                self.remaining -= 1;
1046                return Some(obj);
1047            }
1048            match self.buckets.next() {
1049                Some(bucket) => self.current = Some(bucket.iter()),
1050                None => {
1051                    self.current = None;
1052                    return None;
1053                }
1054            }
1055        }
1056    }
1057
1058    #[inline]
1059    fn size_hint(&self) -> (usize, Option<usize>) {
1060        (self.remaining, Some(self.remaining))
1061    }
1062}
1063
1064impl<'a, P, B> ExactSizeIterator for HashTableIter<'a, P, B>
1065where
1066    P: PtrTraits + 'a,
1067    B: HashTableBucket<P> + 'a,
1068{
1069}
1070
1071impl<'a, P, B> core::iter::FusedIterator for HashTableIter<'a, P, B>
1072where
1073    P: PtrTraits + 'a,
1074    B: HashTableBucket<P> + 'a,
1075{
1076}
1077
1078#[cfg(test)]
1079mod tests {
1080    extern crate alloc;
1081    use super::*;
1082    use crate::doubly_linked_list::DoublyLinkedListNode;
1083    use crate::intrusive_container_test_support::*;
1084    use crate::ref_ptr::RefPtr;
1085    use crate::singly_linked_list::SinglyLinkedListNode;
1086    use crate::unique_ptr::UniquePtr;
1087
1088    const REGULAR_BUCKETS: usize = 37;
1089    const SHARED_BUCKETS: usize = 1031;
1090
1091    #[derive(
1092        crate::SinglyLinkedListContainable, crate::DoublyLinkedListContainable, crate::Recyclable,
1093    )]
1094    struct TestObject {
1095        value: i32,
1096        #[sll_node]
1097        sll_node: SinglyLinkedListNode<TestObject>,
1098        #[dll_node]
1099        dll_node: DoublyLinkedListNode<TestObject>,
1100    }
1101
1102    impl TestValue for TestObject {
1103        fn new(value: i32) -> Self {
1104            Self {
1105                value,
1106                sll_node: SinglyLinkedListNode::new(),
1107                dll_node: DoublyLinkedListNode::new(),
1108            }
1109        }
1110    }
1111
1112    impl HashTableKeyable<usize> for TestObject {
1113        type Key<'a> = usize;
1114        fn get_key(&self) -> usize {
1115            self.value as usize
1116        }
1117    }
1118
1119    #[derive(
1120        crate::SinglyLinkedListContainable, crate::DoublyLinkedListContainable, crate::Recyclable,
1121    )]
1122    struct UniqueTestObject {
1123        value: i32,
1124        #[sll_node]
1125        sll_node: SinglyLinkedListNode<UniqueTestObject>,
1126        #[dll_node]
1127        dll_node: DoublyLinkedListNode<UniqueTestObject>,
1128    }
1129
1130    impl TestValue for UniqueTestObject {
1131        fn new(value: i32) -> Self {
1132            Self {
1133                value,
1134                sll_node: SinglyLinkedListNode::new(),
1135                dll_node: DoublyLinkedListNode::new(),
1136            }
1137        }
1138    }
1139
1140    impl HashTableKeyable<usize> for UniqueTestObject {
1141        type Key<'a> = usize;
1142        fn get_key(&self) -> usize {
1143            self.value as usize
1144        }
1145    }
1146
1147    #[fbl::ref_counted]
1148    #[derive(
1149        crate::SinglyLinkedListContainable, crate::DoublyLinkedListContainable, crate::Recyclable,
1150    )]
1151    #[repr(C)]
1152    pub struct RefTestObject {
1153        value: i32,
1154        #[sll_node]
1155        sll_node: SinglyLinkedListNode<RefTestObject>,
1156        #[dll_node]
1157        dll_node: DoublyLinkedListNode<RefTestObject>,
1158    }
1159
1160    impl TestValue for RefTestObject {
1161        fn new_ref_counted(value: i32) -> RefPtr<Self> {
1162            crate::make_ref_counted!(RefTestObject {
1163                value: value,
1164                sll_node: SinglyLinkedListNode::new(),
1165                dll_node: DoublyLinkedListNode::new()
1166            })
1167            .unwrap()
1168        }
1169    }
1170
1171    impl HashTableKeyable<usize> for RefTestObject {
1172        type Key<'a> = usize;
1173        fn get_key(&self) -> usize {
1174            self.value as usize
1175        }
1176    }
1177
1178    // A dynamically sized HashTable consists of an element count, a pointer to the bucket storage,
1179    // and a bucket count, regardless of the bucket type.
1180    ::zr::static_assert!(
1181        core::mem::size_of::<HashTable<usize, *mut TestObject>>()
1182            == 3 * core::mem::size_of::<usize>()
1183    );
1184    ::zr::static_assert!(
1185        core::mem::align_of::<HashTable<usize, *mut TestObject>>()
1186            == core::mem::align_of::<usize>()
1187    );
1188    ::zr::static_assert!(
1189        core::mem::size_of::<HashTable<usize, *mut TestObject, DoublyLinkedList<*mut TestObject>>>(
1190        ) == 3 * core::mem::size_of::<usize>()
1191    );
1192
1193    macro_rules! generate_hash_table_tests {
1194        ($mod_name:ident, $ptr_type:ty, $bucket_type:ty, $factory_type:ty, $insert:expr) => {
1195            mod $mod_name {
1196                use super::*;
1197
1198                type Table = HashTable<usize, $ptr_type, $bucket_type>;
1199
1200                fn raw_of(ptr: &$ptr_type) -> *const <$ptr_type as PtrTraits>::Target {
1201                    PtrTraits::get_ref(ptr)
1202                }
1203
1204                /// Returns pointers to the elements of a bucket, in iteration order.
1205                fn bucket_elements(
1206                    table: &Table,
1207                    ndx: usize,
1208                ) -> alloc::vec::Vec<*const <$ptr_type as PtrTraits>::Target> {
1209                    table.buckets[ndx].iter().map(|obj| obj as *const _).collect()
1210                }
1211
1212                #[test]
1213                fn test_basic() {
1214                    let mut factory = <$factory_type>::new();
1215                    let mut table = Table::try_new(REGULAR_BUCKETS).unwrap();
1216                    assert!(table.is_initialized());
1217                    assert_eq!(table.bucket_count(), REGULAR_BUCKETS);
1218                    assert!(table.is_empty());
1219                    assert_eq!(table.len(), 0);
1220
1221                    $insert(&mut table, factory.create(1));
1222                    $insert(&mut table, factory.create(2));
1223                    $insert(&mut table, factory.create(3));
1224                    assert!(!table.is_empty());
1225                    assert_eq!(table.len(), 3);
1226
1227                    assert_eq!(table.find(&1).unwrap().value, 1);
1228                    assert_eq!(table.find(&2).unwrap().value, 2);
1229                    assert_eq!(table.find(&3).unwrap().value, 3);
1230                    assert!(table.find(&4).is_none());
1231
1232                    let erased = table.erase(&2).unwrap();
1233                    assert_eq!(PtrTraits::get_ref(&erased).value, 2);
1234                    assert_eq!(table.len(), 2);
1235                    assert!(table.find(&2).is_none());
1236                    assert!(table.erase(&2).is_none());
1237                    assert_eq!(table.len(), 2);
1238
1239                    table.clear();
1240                    assert!(table.is_empty());
1241                    assert_eq!(table.len(), 0);
1242                    assert!(table.find(&1).is_none());
1243                    assert!(table.find(&3).is_none());
1244                }
1245
1246                #[test]
1247                fn test_collisions() {
1248                    let mut factory = <$factory_type>::new();
1249                    let mut table = Table::try_new(REGULAR_BUCKETS).unwrap();
1250
1251                    // All of these keys hash to the same bucket.
1252                    let base = 5;
1253                    let keys = [base, base + REGULAR_BUCKETS, base + 2 * REGULAR_BUCKETS];
1254                    for key in keys {
1255                        $insert(&mut table, factory.create(key as i32));
1256                    }
1257                    assert_eq!(table.len(), 3);
1258                    assert_eq!(table.buckets[base].iter().count(), 3);
1259                    for (ndx, bucket) in table.buckets.iter().enumerate() {
1260                        if ndx != base {
1261                            assert!(bucket.is_empty());
1262                        }
1263                    }
1264
1265                    for key in keys {
1266                        assert_eq!(table.find(&key).unwrap().value, key as i32);
1267                    }
1268
1269                    // Erase the middle element of the chain; the others must still be found.
1270                    assert!(table.erase(&keys[1]).is_some());
1271                    assert_eq!(table.len(), 2);
1272                    assert!(table.find(&keys[1]).is_none());
1273                    assert_eq!(table.find(&keys[0]).unwrap().value, keys[0] as i32);
1274                    assert_eq!(table.find(&keys[2]).unwrap().value, keys[2] as i32);
1275                    assert_eq!(table.buckets[base].iter().count(), 2);
1276
1277                    table.clear();
1278                    assert!(table.buckets[base].is_empty());
1279                }
1280
1281                #[test]
1282                fn test_insert_or_find() {
1283                    let mut factory = <$factory_type>::new();
1284                    let mut table = Table::try_new(REGULAR_BUCKETS).unwrap();
1285
1286                    let first = factory.create(7);
1287                    let first_raw = raw_of(&first);
1288                    assert!(unsafe { table.insert_or_find_raw(first) }.is_ok());
1289                    assert_eq!(table.len(), 1);
1290
1291                    // A second object with the same key collides with the first one.
1292                    let dup = factory.create(7);
1293                    let dup_raw = raw_of(&dup);
1294                    match unsafe { table.insert_or_find_raw(dup) } {
1295                        Ok(()) => panic!("insert_or_find must not insert a duplicate key"),
1296                        Err((returned, cursor)) => {
1297                            assert!(core::ptr::eq(raw_of(&returned), dup_raw));
1298                            assert!(core::ptr::eq(cursor.get().unwrap(), first_raw));
1299                        }
1300                    }
1301                    assert_eq!(table.len(), 1);
1302                    assert!(core::ptr::eq(table.find(&7).unwrap(), first_raw));
1303
1304                    // A different key is inserted.
1305                    assert!(unsafe { table.insert_or_find_raw(factory.create(8)) }.is_ok());
1306                    assert_eq!(table.len(), 2);
1307
1308                    // The cursor returned on a collision can erase the colliding element.
1309                    let dup = factory.create(8);
1310                    match unsafe { table.insert_or_find_raw(dup) } {
1311                        Ok(()) => panic!("insert_or_find must not insert a duplicate key"),
1312                        Err((_returned, cursor)) => {
1313                            assert_eq!(
1314                                cursor.erase().map(|p| PtrTraits::get_ref(&p).value),
1315                                Some(8)
1316                            );
1317                        }
1318                    }
1319                    assert_eq!(table.len(), 1);
1320                    assert!(table.find(&8).is_none());
1321
1322                    table.clear();
1323                }
1324
1325                #[test]
1326                fn test_insert_or_replace() {
1327                    let mut factory = <$factory_type>::new();
1328                    let mut table = Table::try_new(REGULAR_BUCKETS).unwrap();
1329
1330                    let first = factory.create(3);
1331                    let first_raw = raw_of(&first);
1332                    assert!(unsafe { table.insert_or_replace_raw(first) }.is_none());
1333                    assert_eq!(table.len(), 1);
1334
1335                    let second = factory.create(3);
1336                    let second_raw = raw_of(&second);
1337                    let replaced = unsafe { table.insert_or_replace_raw(second) }.unwrap();
1338                    assert!(core::ptr::eq(raw_of(&replaced), first_raw));
1339                    assert!(!PtrTraits::get_ref(&replaced).sll_node.in_container());
1340                    assert!(!PtrTraits::get_ref(&replaced).dll_node.in_container());
1341                    assert_eq!(table.len(), 1);
1342                    assert!(core::ptr::eq(table.find(&3).unwrap(), second_raw));
1343
1344                    assert!(unsafe { table.insert_or_replace_raw(factory.create(4)) }.is_none());
1345                    assert_eq!(table.len(), 2);
1346
1347                    table.clear();
1348                }
1349
1350                #[test]
1351                fn test_insert_or_replace_in_chain() {
1352                    let mut factory = <$factory_type>::new();
1353                    let mut table = Table::try_new(REGULAR_BUCKETS).unwrap();
1354
1355                    // All of these keys hash to the same bucket.
1356                    let base = 9;
1357                    let keys = [base, base + REGULAR_BUCKETS, base + 2 * REGULAR_BUCKETS];
1358                    for key in keys {
1359                        $insert(&mut table, factory.create(key as i32));
1360                    }
1361
1362                    // Replace each element of the chain, starting from the middle, then the tail,
1363                    // then the head.
1364                    for position in [1, 2, 0] {
1365                        let elements = bucket_elements(&table, base);
1366                        let old = elements[position];
1367                        // SAFETY: `old` is in `table`.
1368                        let key = unsafe { &*old }.value;
1369
1370                        let replacement = factory.create(key);
1371                        let replacement_raw = raw_of(&replacement);
1372                        let replaced = unsafe { table.insert_or_replace_raw(replacement) }.unwrap();
1373                        assert!(core::ptr::eq(raw_of(&replaced), old));
1374                        assert_eq!(table.len(), keys.len());
1375                        assert!(core::ptr::eq(
1376                            table.find(&(key as usize)).unwrap(),
1377                            replacement_raw
1378                        ));
1379
1380                        // The replacement takes the position of the replaced element.
1381                        let mut expected = elements;
1382                        expected[position] = replacement_raw;
1383                        assert_eq!(bucket_elements(&table, base), expected);
1384                    }
1385
1386                    table.clear();
1387                }
1388
1389                #[test]
1390                fn test_find_cursor() {
1391                    let mut factory = <$factory_type>::new();
1392                    let mut table = Table::try_new(REGULAR_BUCKETS).unwrap();
1393                    for i in 0..10 {
1394                        $insert(&mut table, factory.create(i));
1395                    }
1396
1397                    assert_eq!(table.find_cursor(&4).get().unwrap().value, 4);
1398                    let erased = table.find_cursor(&4).erase().unwrap();
1399                    assert_eq!(PtrTraits::get_ref(&erased).value, 4);
1400                    assert_eq!(table.len(), 9);
1401                    assert!(table.find(&4).is_none());
1402
1403                    assert!(table.find_cursor(&4).get().is_none());
1404                    assert!(table.find_cursor(&4).erase().is_none());
1405                    assert_eq!(table.len(), 9);
1406
1407                    table.clear();
1408                }
1409
1410                #[test]
1411                fn test_erase_element() {
1412                    let mut factory = <$factory_type>::new();
1413                    let mut table = Table::try_new(REGULAR_BUCKETS).unwrap();
1414                    for i in 0..10 {
1415                        $insert(&mut table, factory.create(i));
1416                    }
1417
1418                    let obj: *const _ = table.find(&6).unwrap();
1419                    // SAFETY: `obj` is in `table` and its key has not changed.
1420                    let erased = unsafe { table.erase_element(&*obj) }.unwrap();
1421                    assert!(core::ptr::eq(raw_of(&erased), obj));
1422                    assert_eq!(table.len(), 9);
1423                    assert!(table.find(&6).is_none());
1424
1425                    // Erasing an element which is no longer in any container is a no-op.
1426                    // SAFETY: `erased` is not in any container.
1427                    assert!(unsafe { table.erase_element(PtrTraits::get_ref(&erased)) }.is_none());
1428                    assert_eq!(table.len(), 9);
1429
1430                    table.clear();
1431                }
1432
1433                #[test]
1434                fn test_erase_element_in_chain() {
1435                    // Erase elements from the middle, the tail, and the head of a chain, in every
1436                    // order, through both `erase_element` and cursors.
1437                    let orders = [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]];
1438                    for (iteration, order) in orders.iter().enumerate() {
1439                        let mut factory = <$factory_type>::new();
1440                        let mut table = Table::try_new(REGULAR_BUCKETS).unwrap();
1441
1442                        // All of these keys hash to the same bucket.
1443                        let base = 11;
1444                        let keys = [base, base + REGULAR_BUCKETS, base + 2 * REGULAR_BUCKETS];
1445                        for key in keys {
1446                            $insert(&mut table, factory.create(key as i32));
1447                        }
1448
1449                        let mut remaining = bucket_elements(&table, base);
1450                        let mut positions = alloc::vec::Vec::from(*order);
1451                        for step in 0..positions.len() {
1452                            let position = positions.remove(0);
1453                            let obj = remaining.remove(position);
1454                            // Shift the positions after the removed element.
1455                            for p in positions.iter_mut() {
1456                                if *p > position {
1457                                    *p -= 1;
1458                                }
1459                            }
1460
1461                            let erased = if (iteration + step) % 2 == 0 {
1462                                // SAFETY: `obj` is in `table` and its key has not changed.
1463                                unsafe { table.erase_element(&*obj) }.unwrap()
1464                            } else {
1465                                // SAFETY: `obj` is in `table`.
1466                                let key = unsafe { &*obj }.value as usize;
1467                                table.find_cursor(&key).erase().unwrap()
1468                            };
1469                            assert!(core::ptr::eq(raw_of(&erased), obj));
1470                            assert_eq!(table.len(), remaining.len());
1471                            assert_eq!(bucket_elements(&table, base), remaining);
1472                            for &other in remaining.iter() {
1473                                // SAFETY: `other` is in `table`.
1474                                let key = unsafe { &*other }.value as usize;
1475                                assert!(core::ptr::eq(table.find(&key).unwrap(), other));
1476                            }
1477                        }
1478                        assert!(table.is_empty());
1479                        assert!(table.buckets[base].is_empty());
1480                    }
1481                }
1482
1483                #[test]
1484                fn test_iter() {
1485                    let mut factory = <$factory_type>::new();
1486                    let mut table = Table::try_new(REGULAR_BUCKETS).unwrap();
1487                    assert!(table.iter().next().is_none());
1488
1489                    const COUNT: i32 = 100;
1490                    for i in 0..COUNT {
1491                        $insert(&mut table, factory.create(i));
1492                    }
1493
1494                    let iter = table.iter();
1495                    assert_eq!(iter.len(), COUNT as usize);
1496                    let mut seen = [false; COUNT as usize];
1497                    let mut count = 0;
1498                    for obj in &table {
1499                        assert!(!seen[obj.value as usize]);
1500                        seen[obj.value as usize] = true;
1501                        count += 1;
1502                    }
1503                    assert_eq!(count, COUNT);
1504                    assert!(seen.iter().all(|&s| s));
1505
1506                    table.clear();
1507                    assert!(table.iter().next().is_none());
1508                }
1509
1510                #[test]
1511                fn test_erase_if_and_find_if() {
1512                    let mut factory = <$factory_type>::new();
1513                    let mut table = Table::try_new(REGULAR_BUCKETS).unwrap();
1514                    assert!(table.erase_if(|_| true).is_none());
1515
1516                    for i in 0..20 {
1517                        $insert(&mut table, factory.create(i));
1518                    }
1519
1520                    assert_eq!(table.find_if(|obj| obj.value == 13).unwrap().value, 13);
1521                    assert!(table.find_if(|obj| obj.value == 20).is_none());
1522
1523                    let erased = table.erase_if(|obj| obj.value == 13).unwrap();
1524                    assert_eq!(PtrTraits::get_ref(&erased).value, 13);
1525                    assert_eq!(table.len(), 19);
1526                    assert!(table.find(&13).is_none());
1527                    assert!(table.erase_if(|obj| obj.value == 13).is_none());
1528
1529                    // Drain the table using erase_if.
1530                    while table.erase_if(|_| true).is_some() {}
1531                    assert!(table.is_empty());
1532                    assert!(table.iter().next().is_none());
1533                }
1534
1535                #[test]
1536                fn test_dynamic_bucket_counts() {
1537                    for bucket_count in [REGULAR_BUCKETS, SHARED_BUCKETS] {
1538                        let mut factory = <$factory_type>::new();
1539                        let mut table = Table::try_new(bucket_count).unwrap();
1540                        assert_eq!(table.bucket_count(), bucket_count);
1541
1542                        // Insert two elements per bucket.
1543                        let count = 2 * bucket_count;
1544                        for i in 0..count {
1545                            $insert(&mut table, factory.create(i as i32));
1546                        }
1547                        assert_eq!(table.len(), count);
1548                        for bucket in table.buckets.iter() {
1549                            assert_eq!(bucket.iter().count(), 2);
1550                        }
1551
1552                        for i in 0..count {
1553                            assert_eq!(table.find(&i).unwrap().value, i as i32);
1554                        }
1555                        for i in 0..count {
1556                            assert!(table.erase(&i).is_some());
1557                            assert_eq!(table.len(), count - i - 1);
1558                        }
1559                        assert!(table.is_empty());
1560                        assert!(table.buckets.iter().all(|bucket| bucket.is_empty()));
1561                    }
1562                }
1563            }
1564        };
1565    }
1566
1567    generate_hash_table_tests!(
1568        raw_ptr_sll_tests,
1569        *mut TestObject,
1570        SinglyLinkedList<*mut TestObject>,
1571        RawFactory<TestObject>,
1572        |table: &mut Table, obj| unsafe { table.insert_raw(obj) }
1573    );
1574
1575    generate_hash_table_tests!(
1576        raw_ptr_dll_tests,
1577        *mut TestObject,
1578        DoublyLinkedList<*mut TestObject>,
1579        RawFactory<TestObject>,
1580        |table: &mut Table, obj| unsafe { table.insert_raw(obj) }
1581    );
1582
1583    generate_hash_table_tests!(
1584        unique_ptr_sll_tests,
1585        UniquePtr<UniqueTestObject>,
1586        SinglyLinkedList<UniquePtr<UniqueTestObject>>,
1587        UniqueFactory<UniqueTestObject>,
1588        |table: &mut Table, obj| table.insert(obj)
1589    );
1590
1591    generate_hash_table_tests!(
1592        unique_ptr_dll_tests,
1593        UniquePtr<UniqueTestObject>,
1594        DoublyLinkedList<UniquePtr<UniqueTestObject>>,
1595        UniqueFactory<UniqueTestObject>,
1596        |table: &mut Table, obj| table.insert(obj)
1597    );
1598
1599    generate_hash_table_tests!(
1600        ref_ptr_sll_tests,
1601        RefPtr<RefTestObject>,
1602        SinglyLinkedList<RefPtr<RefTestObject>>,
1603        RefFactory<RefTestObject>,
1604        |table: &mut Table, obj| table.insert(obj)
1605    );
1606
1607    generate_hash_table_tests!(
1608        ref_ptr_dll_tests,
1609        RefPtr<RefTestObject>,
1610        DoublyLinkedList<RefPtr<RefTestObject>>,
1611        RefFactory<RefTestObject>,
1612        |table: &mut Table, obj| table.insert(obj)
1613    );
1614
1615    #[test]
1616    fn test_delayed_init() {
1617        let mut table = HashTable::<usize, UniquePtr<UniqueTestObject>>::new();
1618        assert!(!table.is_initialized());
1619        assert_eq!(table.bucket_count(), 0);
1620        assert!(table.is_empty());
1621        assert_eq!(table.len(), 0);
1622        assert!(table.iter().next().is_none());
1623        table.clear();
1624
1625        table.try_init(REGULAR_BUCKETS).unwrap();
1626        assert!(table.is_initialized());
1627        assert_eq!(table.bucket_count(), REGULAR_BUCKETS);
1628        assert!(table.is_empty());
1629
1630        table.insert(UniquePtr::try_new(UniqueTestObject::new(1)).unwrap());
1631        assert_eq!(table.len(), 1);
1632        assert_eq!(table.find(&1).unwrap().value, 1);
1633    }
1634
1635    #[test]
1636    fn test_default_is_uninitialized() {
1637        let table: HashTable<usize, *mut TestObject> = Default::default();
1638        assert!(!table.is_initialized());
1639        assert!(table.is_empty());
1640
1641        let table: HashTable<usize, *mut TestObject, DoublyLinkedList<*mut TestObject>> =
1642            Default::default();
1643        assert!(!table.is_initialized());
1644        assert!(table.is_empty());
1645    }
1646
1647    #[test]
1648    fn test_move_table() {
1649        // The bucket storage lives on the heap, so a HashTable may be moved freely, even while
1650        // it holds elements, and even with buckets which must be pinned.
1651        type DllTable = HashTable<
1652            usize,
1653            UniquePtr<UniqueTestObject>,
1654            DoublyLinkedList<UniquePtr<UniqueTestObject>>,
1655        >;
1656        let mut table = DllTable::try_new(REGULAR_BUCKETS).unwrap();
1657        for i in 0..10 {
1658            table.insert(UniquePtr::try_new(UniqueTestObject::new(i)).unwrap());
1659        }
1660
1661        let mut moved = table;
1662        assert_eq!(moved.len(), 10);
1663        for i in 0..10 {
1664            assert_eq!(moved.find(&(i as usize)).unwrap().value, i);
1665        }
1666        assert!(moved.erase(&3).is_some());
1667        moved.insert(UniquePtr::try_new(UniqueTestObject::new(42)).unwrap());
1668        assert_eq!(moved.len(), 10);
1669        assert_eq!(moved.iter().count(), 10);
1670
1671        // Replace an uninitialized table with an initialized one.
1672        let mut late = DllTable::new();
1673        late = core::mem::replace(&mut moved, late);
1674        assert_eq!(late.len(), 10);
1675        assert!(!moved.is_initialized());
1676    }
1677
1678    struct ShiftHashTraits;
1679
1680    impl HashTraits<usize> for ShiftHashTraits {
1681        fn get_hash(key: &usize) -> usize {
1682            *key >> 4
1683        }
1684    }
1685
1686    #[test]
1687    fn test_custom_hash_traits() {
1688        let mut table = HashTable::<
1689            usize,
1690            UniquePtr<UniqueTestObject>,
1691            SinglyLinkedList<UniquePtr<UniqueTestObject>>,
1692            ShiftHashTraits,
1693        >::try_new(REGULAR_BUCKETS)
1694        .unwrap();
1695
1696        // Keys 0..16 all hash to bucket 0, and keys 16..32 all hash to bucket 1.
1697        for i in 0..32 {
1698            table.insert(UniquePtr::try_new(UniqueTestObject::new(i)).unwrap());
1699        }
1700        assert_eq!(table.buckets[0].iter().count(), 16);
1701        assert_eq!(table.buckets[1].iter().count(), 16);
1702        for i in 0..32 {
1703            assert_eq!(table.find(&i).unwrap().value, i as i32);
1704        }
1705    }
1706
1707    struct ReversedHashTraits;
1708
1709    impl HashTraits<usize> for ReversedHashTraits {
1710        fn get_hash(key: &usize) -> usize {
1711            usize::MAX - *key
1712        }
1713    }
1714
1715    #[test]
1716    fn test_hash_reduced_modulo_bucket_count() {
1717        // Hash values much larger than the bucket count are reduced modulo the bucket count.
1718        let mut table = HashTable::<
1719            usize,
1720            UniquePtr<UniqueTestObject>,
1721            DoublyLinkedList<UniquePtr<UniqueTestObject>>,
1722            ReversedHashTraits,
1723        >::try_new(REGULAR_BUCKETS)
1724        .unwrap();
1725
1726        for i in 0..100 {
1727            table.insert(UniquePtr::try_new(UniqueTestObject::new(i)).unwrap());
1728        }
1729        for (ndx, bucket) in table.buckets.iter().enumerate() {
1730            for obj in bucket.iter() {
1731                assert_eq!((usize::MAX - obj.value as usize) % REGULAR_BUCKETS, ndx);
1732            }
1733        }
1734        for i in 0..100 {
1735            assert_eq!(table.find(&i).unwrap().value, i as i32);
1736        }
1737    }
1738
1739    #[test]
1740    fn test_default_hash_traits() {
1741        assert_eq!(<DefaultHashTraits as HashTraits<usize>>::get_hash(&40), 40);
1742        assert_eq!(<DefaultHashTraits as HashTraits<u32>>::get_hash(&1031), 1031);
1743        assert_eq!(<DefaultHashTraits as HashTraits<u64>>::get_hash(&1), 1);
1744        assert_eq!(<DefaultHashTraits as HashTraits<u8>>::get_hash(&200), 200);
1745        assert_eq!(<DefaultHashTraits as HashTraits<usize>>::get_hash(&usize::MAX), usize::MAX);
1746    }
1747
1748    use alloc::sync::Arc;
1749    use core::sync::atomic::{AtomicUsize, Ordering};
1750
1751    #[derive(
1752        crate::SinglyLinkedListContainable, crate::DoublyLinkedListContainable, crate::Recyclable,
1753    )]
1754    struct LifecycleObject {
1755        key: u64,
1756        destroyed: Arc<AtomicUsize>,
1757        #[sll_node]
1758        sll_node: SinglyLinkedListNode<LifecycleObject>,
1759        #[dll_node]
1760        dll_node: DoublyLinkedListNode<LifecycleObject>,
1761    }
1762
1763    impl LifecycleObject {
1764        fn new(key: u64, destroyed: Arc<AtomicUsize>) -> Self {
1765            Self {
1766                key,
1767                destroyed,
1768                sll_node: SinglyLinkedListNode::new(),
1769                dll_node: DoublyLinkedListNode::new(),
1770            }
1771        }
1772    }
1773
1774    impl HashTableKeyable<u64> for LifecycleObject {
1775        type Key<'a> = u64;
1776        fn get_key(&self) -> u64 {
1777            self.key
1778        }
1779    }
1780
1781    impl Drop for LifecycleObject {
1782        fn drop(&mut self) {
1783            self.destroyed.fetch_add(1, Ordering::Relaxed);
1784        }
1785    }
1786
1787    type LifecycleDllBuckets = DoublyLinkedList<UniquePtr<LifecycleObject>>;
1788
1789    #[test]
1790    fn test_lifecycle_on_drop() {
1791        let destroyed = Arc::new(AtomicUsize::new(0));
1792        {
1793            let mut table =
1794                HashTable::<u64, UniquePtr<LifecycleObject>>::try_new(SHARED_BUCKETS).unwrap();
1795            let mut dll_table =
1796                HashTable::<u64, UniquePtr<LifecycleObject>, LifecycleDllBuckets>::try_new(
1797                    SHARED_BUCKETS,
1798                )
1799                .unwrap();
1800            for key in 0..5 {
1801                table.insert(
1802                    UniquePtr::try_new(LifecycleObject::new(key, destroyed.clone())).unwrap(),
1803                );
1804                dll_table.insert(
1805                    UniquePtr::try_new(LifecycleObject::new(key, destroyed.clone())).unwrap(),
1806                );
1807            }
1808            assert_eq!(destroyed.load(Ordering::Relaxed), 0);
1809        } // tables drop here
1810        assert_eq!(destroyed.load(Ordering::Relaxed), 10);
1811    }
1812
1813    #[test]
1814    fn test_lifecycle_on_clear_and_erase() {
1815        let destroyed = Arc::new(AtomicUsize::new(0));
1816        let mut table =
1817            HashTable::<u64, UniquePtr<LifecycleObject>>::try_new(REGULAR_BUCKETS).unwrap();
1818        for key in 0..5 {
1819            table.insert(UniquePtr::try_new(LifecycleObject::new(key, destroyed.clone())).unwrap());
1820        }
1821
1822        // Erased elements are handed back to the caller, not destroyed.
1823        let erased = table.erase(&2).unwrap();
1824        assert_eq!(destroyed.load(Ordering::Relaxed), 0);
1825        drop(erased);
1826        assert_eq!(destroyed.load(Ordering::Relaxed), 1);
1827
1828        table.clear();
1829        assert_eq!(destroyed.load(Ordering::Relaxed), 5);
1830        assert!(table.is_empty());
1831    }
1832
1833    #[test]
1834    fn test_move_between_table_and_free_list() {
1835        // Mirrors how FutexContext moves objects between its free list and its active hash table,
1836        // which share the same `DoublyLinkedListNode`.
1837        pin_init::stack_pin_init!(let free = LifecycleDllBuckets::new());
1838        let destroyed = Arc::new(AtomicUsize::new(0));
1839        for _ in 0..4 {
1840            free.as_mut().push_front(
1841                UniquePtr::try_new(LifecycleObject::new(0, destroyed.clone())).unwrap(),
1842            );
1843        }
1844
1845        let mut active =
1846            HashTable::<u64, UniquePtr<LifecycleObject>, LifecycleDllBuckets>::try_new(
1847                SHARED_BUCKETS,
1848            )
1849            .unwrap();
1850        let ids = [0x1000u64, 0x1000 + SHARED_BUCKETS as u64, 0x2000, 0x3000];
1851        for id in ids {
1852            let mut state = free.as_mut().pop_front().unwrap();
1853            state.key = id;
1854            active.insert(state);
1855        }
1856        assert!(free.is_empty());
1857        assert_eq!(active.len(), ids.len());
1858
1859        for id in ids {
1860            let state: *const LifecycleObject = active.find(&id).unwrap();
1861            // SAFETY: `state` is in `active` and its key has not changed.
1862            let mut removed = unsafe { active.erase_element(&*state) }.unwrap();
1863            removed.key = 0;
1864            free.as_mut().push_front(removed);
1865        }
1866        assert!(active.is_empty());
1867        assert_eq!(destroyed.load(Ordering::Relaxed), 0);
1868
1869        free.as_mut().clear();
1870        assert_eq!(destroyed.load(Ordering::Relaxed), 4);
1871    }
1872}