@@ -311,6 +311,55 @@ where
311311 }
312312 }
313313
314+ /// Returns the bucket index in the table for an entry with the given hash
315+ /// and which satisfies the equality function passed.
316+ ///
317+ /// This can be used to store a borrow-free "reference" to the entry, later using
318+ /// [`get_bucket`][Self::get_bucket], [`get_bucket_mut`][Self::get_bucket_mut], or
319+ /// [`get_bucket_entry`][Self::get_bucket_entry] to access it again without hash probing.
320+ ///
321+ /// The index is only meaningful as long as the table is not resized and no entries are added
322+ /// or removed. After such changes, it may end up pointing to a different entry or none at all.
323+ ///
324+ /// # Examples
325+ ///
326+ /// ```
327+ /// # #[cfg(feature = "nightly")]
328+ /// # fn test() {
329+ /// use hashbrown::{HashTable, DefaultHashBuilder};
330+ /// use std::hash::BuildHasher;
331+ ///
332+ /// let mut table = HashTable::new();
333+ /// let hasher = DefaultHashBuilder::default();
334+ /// let hasher = |val: &_| hasher.hash_one(val);
335+ /// table.insert_unique(hasher(&1), (1, 1), |val| hasher(&val.0));
336+ /// table.insert_unique(hasher(&2), (2, 2), |val| hasher(&val.0));
337+ /// table.insert_unique(hasher(&3), (3, 3), |val| hasher(&val.0));
338+ ///
339+ /// let index = table.find_bucket_index(hasher(&2), |val| val.0 == 2).unwrap();
340+ /// assert_eq!(table.get_bucket(index), Some(&(2, 2)));
341+ ///
342+ /// // Mutation would invalidate any normal reference
343+ /// for (_key, value) in &mut table {
344+ /// *value *= 11;
345+ /// }
346+ ///
347+ /// // The index still reaches the same key with the updated value
348+ /// assert_eq!(table.get_bucket(index), Some(&(2, 22)));
349+ /// # }
350+ /// # fn main() {
351+ /// # #[cfg(feature = "nightly")]
352+ /// # test()
353+ /// # }
354+ /// ```
355+ #[ cfg_attr( feature = "inline-more" , inline) ]
356+ pub fn find_bucket_index ( & self , hash : u64 , eq : impl FnMut ( & T ) -> bool ) -> Option < usize > {
357+ match self . raw . find ( hash, eq) {
358+ Some ( bucket) => Some ( unsafe { self . raw . bucket_index ( & bucket) } ) ,
359+ None => None ,
360+ }
361+ }
362+
314363 /// Returns an `Entry` for an entry in the table with the given hash
315364 /// and which satisfies the equality function passed.
316365 ///
@@ -376,6 +425,121 @@ where
376425 }
377426 }
378427
428+ /// Returns an `OccupiedEntry` for a bucket index in the table with the given hash,
429+ /// or `None` if the index is out of bounds or if its hash doesn't match.
430+ ///
431+ /// However, note that the hash is only compared for the few bits that are directly stored in
432+ /// the table, and even in full this could not guarantee equality. Use [`OccupiedEntry::get`]
433+ /// if you need to further validate a match.
434+ ///
435+ /// # Examples
436+ ///
437+ /// ```
438+ /// # #[cfg(feature = "nightly")]
439+ /// # fn test() {
440+ /// use hashbrown::{HashTable, DefaultHashBuilder};
441+ /// use std::hash::BuildHasher;
442+ ///
443+ /// let mut table = HashTable::new();
444+ /// let hasher = DefaultHashBuilder::default();
445+ /// let hasher = |val: &_| hasher.hash_one(val);
446+ /// table.insert_unique(hasher(&1), (1, 'a'), |val| hasher(&val.0));
447+ /// table.insert_unique(hasher(&2), (2, 'b'), |val| hasher(&val.0));
448+ /// table.insert_unique(hasher(&3), (3, 'c'), |val| hasher(&val.0));
449+ ///
450+ /// let hash = hasher(&2);
451+ /// let index = table.find_bucket_index(hash, |val| val.0 == 2).unwrap();
452+ ///
453+ /// let bad_hash = !hash;
454+ /// assert!(table.get_bucket_entry(bad_hash, index).is_none());
455+ /// assert!(table.get_bucket_entry(hash, usize::MAX).is_none());
456+ ///
457+ /// let occupied_entry = table.get_bucket_entry(hash, index).unwrap();
458+ /// assert_eq!(occupied_entry.get(), &(2, 'b'));
459+ /// assert_eq!(occupied_entry.remove().0, (2, 'b'));
460+ ///
461+ /// assert!(table.find(hash, |val| val.0 == 2).is_none());
462+ /// # }
463+ /// # fn main() {
464+ /// # #[cfg(feature = "nightly")]
465+ /// # test()
466+ /// # }
467+ /// ```
468+ #[ inline]
469+ pub fn get_bucket_entry ( & mut self , hash : u64 , index : usize ) -> Option < OccupiedEntry < ' _ , T , A > > {
470+ Some ( OccupiedEntry {
471+ hash,
472+ bucket : self . raw . checked_bucket ( hash, index) ?,
473+ table : self ,
474+ } )
475+ }
476+
477+ /// Gets a reference to an entry in the table at the given bucket index,
478+ /// or `None` if it is unoccupied or out of bounds.
479+ ///
480+ /// # Examples
481+ ///
482+ /// ```
483+ /// # #[cfg(feature = "nightly")]
484+ /// # fn test() {
485+ /// use hashbrown::{HashTable, DefaultHashBuilder};
486+ /// use std::hash::BuildHasher;
487+ ///
488+ /// let mut table = HashTable::new();
489+ /// let hasher = DefaultHashBuilder::default();
490+ /// let hasher = |val: &_| hasher.hash_one(val);
491+ /// table.insert_unique(hasher(&1), (1, 'a'), |val| hasher(&val.0));
492+ /// table.insert_unique(hasher(&2), (2, 'b'), |val| hasher(&val.0));
493+ /// table.insert_unique(hasher(&3), (3, 'c'), |val| hasher(&val.0));
494+ ///
495+ /// let index = table.find_bucket_index(hasher(&2), |val| val.0 == 2).unwrap();
496+ /// assert_eq!(table.get_bucket(index), Some(&(2, 'b')));
497+ /// # }
498+ /// # fn main() {
499+ /// # #[cfg(feature = "nightly")]
500+ /// # test()
501+ /// # }
502+ /// ```
503+ #[ inline]
504+ pub fn get_bucket ( & self , index : usize ) -> Option < & T > {
505+ self . raw . get_bucket ( index)
506+ }
507+
508+ /// Gets a mutable reference to an entry in the table at the given bucket index,
509+ /// or `None` if it is unoccupied or out of bounds.
510+ ///
511+ /// # Examples
512+ ///
513+ /// ```
514+ /// # #[cfg(feature = "nightly")]
515+ /// # fn test() {
516+ /// use hashbrown::{HashTable, DefaultHashBuilder};
517+ /// use std::hash::BuildHasher;
518+ ///
519+ /// let mut table = HashTable::new();
520+ /// let hasher = DefaultHashBuilder::default();
521+ /// let hasher = |val: &_| hasher.hash_one(val);
522+ /// table.insert_unique(hasher(&1), (1, 'a'), |val| hasher(&val.0));
523+ /// table.insert_unique(hasher(&2), (2, 'b'), |val| hasher(&val.0));
524+ /// table.insert_unique(hasher(&3), (3, 'c'), |val| hasher(&val.0));
525+ ///
526+ /// let index = table.find_bucket_index(hasher(&2), |val| val.0 == 2).unwrap();
527+ /// assert_eq!(table.get_bucket(index), Some(&(2, 'b')));
528+ /// if let Some((_key, value)) = table.get_bucket_mut(index) {
529+ /// *value = 'B';
530+ /// }
531+ /// assert_eq!(table.get_bucket(index), Some(&(2, 'B')));
532+ /// # }
533+ /// # fn main() {
534+ /// # #[cfg(feature = "nightly")]
535+ /// # test()
536+ /// # }
537+ /// ```
538+ #[ inline]
539+ pub fn get_bucket_mut ( & mut self , index : usize ) -> Option < & mut T > {
540+ self . raw . get_bucket_mut ( index)
541+ }
542+
379543 /// Inserts an element into the `HashTable` with the given hash value, but
380544 /// without checking whether an equivalent element already exists within the
381545 /// table.
@@ -591,6 +755,44 @@ where
591755 self . raw . try_reserve ( additional, hasher)
592756 }
593757
758+ /// Returns the raw number of buckets allocated in the table.
759+ ///
760+ /// This is an upper bound on any methods that take or return a bucket index,
761+ /// as opposed to the usable [`capacity`](Self::capacity) for entries which is
762+ /// reduced by an unspecified load factor.
763+ ///
764+ /// # Examples
765+ ///
766+ /// ```
767+ /// # #[cfg(feature = "nightly")]
768+ /// # fn test() {
769+ /// use hashbrown::{HashTable, DefaultHashBuilder};
770+ /// use std::hash::BuildHasher;
771+ ///
772+ /// let mut table = HashTable::new();
773+ /// let hasher = DefaultHashBuilder::default();
774+ /// let hasher = |val: &_| hasher.hash_one(val);
775+ /// table.insert_unique(hasher(&1), (1, 'a'), |val| hasher(&val.0));
776+ /// table.insert_unique(hasher(&2), (2, 'b'), |val| hasher(&val.0));
777+ /// table.insert_unique(hasher(&3), (3, 'c'), |val| hasher(&val.0));
778+ ///
779+ /// // Each entry is available at some index in the bucket range.
780+ /// let count = (0..table.buckets())
781+ /// .filter_map(|i| table.get_bucket(i))
782+ /// .count();
783+ /// assert_eq!(count, 3);
784+ ///
785+ /// assert_eq!(table.get_bucket(table.buckets()), None);
786+ /// # }
787+ /// # fn main() {
788+ /// # #[cfg(feature = "nightly")]
789+ /// # test()
790+ /// # }
791+ /// ```
792+ pub fn buckets ( & self ) -> usize {
793+ self . raw . buckets ( )
794+ }
795+
594796 /// Returns the number of elements the table can hold without reallocating.
595797 ///
596798 /// # Examples
@@ -1789,6 +1991,53 @@ where
17891991 pub fn into_table ( self ) -> & ' a mut HashTable < T , A > {
17901992 self . table
17911993 }
1994+
1995+ /// Returns the bucket index in the table for this entry.
1996+ ///
1997+ /// This can be used to store a borrow-free "reference" to the entry, later using
1998+ /// [`HashTable::get_bucket`], [`HashTable::get_bucket_mut`], or
1999+ /// [`HashTable::get_bucket_entry`] to access the it again without hash probing.
2000+ ///
2001+ /// The index is only meaningful as long as the table is not resized and no entries are added
2002+ /// or removed. After such changes, it may end up pointing to a different entry or none at all.
2003+ ///
2004+ /// # Examples
2005+ ///
2006+ /// ```
2007+ /// # #[cfg(feature = "nightly")]
2008+ /// # fn test() {
2009+ /// use hashbrown::{HashTable, DefaultHashBuilder};
2010+ /// use std::hash::BuildHasher;
2011+ ///
2012+ /// let mut table = HashTable::new();
2013+ /// let hasher = DefaultHashBuilder::default();
2014+ /// let hasher = |val: &_| hasher.hash_one(val);
2015+ /// table.insert_unique(hasher(&1), (1, 1), |val| hasher(&val.0));
2016+ /// table.insert_unique(hasher(&2), (2, 2), |val| hasher(&val.0));
2017+ /// table.insert_unique(hasher(&3), (3, 3), |val| hasher(&val.0));
2018+ ///
2019+ /// let index = table
2020+ /// .entry(hasher(&2), |val| val.0 == 2, |val| hasher(&val.0))
2021+ /// .or_insert((2, -2))
2022+ /// .bucket_index();
2023+ /// assert_eq!(table.get_bucket(index), Some(&(2, 2)));
2024+ ///
2025+ /// // Full mutation would invalidate any normal reference
2026+ /// for (_key, value) in &mut table {
2027+ /// *value *= 11;
2028+ /// }
2029+ ///
2030+ /// // The index still reaches the same key with the updated value
2031+ /// assert_eq!(table.get_bucket(index), Some(&(2, 22)));
2032+ /// # }
2033+ /// # fn main() {
2034+ /// # #[cfg(feature = "nightly")]
2035+ /// # test()
2036+ /// # }
2037+ /// ```
2038+ pub fn bucket_index ( & self ) -> usize {
2039+ unsafe { self . table . raw . bucket_index ( & self . bucket ) }
2040+ }
17922041}
17932042
17942043/// A view into a vacant entry in a `HashTable`.
0 commit comments