Skip to content

Commit 5796895

Browse files
committed
Add HashTable::iter_buckets and iter_hash_buckets
1 parent c425b28 commit 5796895

File tree

2 files changed

+192
-11
lines changed

2 files changed

+192
-11
lines changed

src/raw/mod.rs

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,28 @@ impl<T, A: Allocator> RawTable<T, A> {
14251425
RawIterHash::new(self, hash)
14261426
}
14271427

1428+
/// Returns an iterator over occupied bucket indices that could match a given hash.
1429+
///
1430+
/// `RawTable` only stores 7 bits of the hash value, so this iterator may
1431+
/// return items that have a hash value different than the one provided. You
1432+
/// should always validate the returned values before using them.
1433+
///
1434+
/// It is up to the caller to ensure that the `RawTable` outlives the
1435+
/// `RawIterHashIndices`. Because we cannot make the `next` method unsafe on the
1436+
/// `RawIterHashIndices` struct, we have to make the `iter_hash_buckets` method unsafe.
1437+
#[cfg_attr(feature = "inline-more", inline)]
1438+
pub unsafe fn iter_hash_buckets(&self, hash: u64) -> RawIterHashIndices {
1439+
RawIterHashIndices::new(&self.table, hash)
1440+
}
1441+
1442+
/// Returns an iterator over full buckets indices in the table.
1443+
///
1444+
/// See [`RawTableInner::full_buckets_indices`] for safety conditions.
1445+
#[inline(always)]
1446+
pub(crate) unsafe fn full_buckets_indices(&self) -> FullBucketsIndices {
1447+
self.table.full_buckets_indices()
1448+
}
1449+
14281450
/// Returns an iterator which removes all elements from the table without
14291451
/// freeing the memory.
14301452
#[cfg_attr(feature = "inline-more", inline)]
@@ -3885,6 +3907,7 @@ impl<T> FusedIterator for RawIter<T> {}
38853907
/// created will be yielded by that iterator.
38863908
/// - The order in which the iterator yields indices of the buckets is unspecified
38873909
/// and may change in the future.
3910+
#[derive(Clone)]
38883911
pub(crate) struct FullBucketsIndices {
38893912
// Mask of full buckets in the current group. Bits are cleared from this
38903913
// mask as each element is processed.
@@ -3902,6 +3925,14 @@ pub(crate) struct FullBucketsIndices {
39023925
items: usize,
39033926
}
39043927

3928+
impl Default for FullBucketsIndices {
3929+
#[cfg_attr(feature = "inline-more", inline)]
3930+
fn default() -> Self {
3931+
// SAFETY: Because the table is static, it always outlives the iter.
3932+
unsafe { RawTableInner::NEW.full_buckets_indices() }
3933+
}
3934+
}
3935+
39053936
impl FullBucketsIndices {
39063937
/// Advances the iterator and returns the next value.
39073938
///
@@ -4167,12 +4198,12 @@ impl<T, A: Allocator> FusedIterator for RawDrain<'_, T, A> {}
41674198
/// - The order in which the iterator yields buckets is unspecified and may
41684199
/// change in the future.
41694200
pub struct RawIterHash<T> {
4170-
inner: RawIterHashInner,
4201+
inner: RawIterHashIndices,
41714202
_marker: PhantomData<T>,
41724203
}
41734204

41744205
#[derive(Clone)]
4175-
struct RawIterHashInner {
4206+
pub(crate) struct RawIterHashIndices {
41764207
// See `RawTableInner`'s corresponding fields for details.
41774208
// We can't store a `*const RawTableInner` as it would get
41784209
// invalidated by the user calling `&mut` methods on `RawTable`.
@@ -4195,7 +4226,7 @@ impl<T> RawIterHash<T> {
41954226
#[cfg_attr(feature = "inline-more", inline)]
41964227
unsafe fn new<A: Allocator>(table: &RawTable<T, A>, hash: u64) -> Self {
41974228
RawIterHash {
4198-
inner: RawIterHashInner::new(&table.table, hash),
4229+
inner: RawIterHashIndices::new(&table.table, hash),
41994230
_marker: PhantomData,
42004231
}
42014232
}
@@ -4215,22 +4246,29 @@ impl<T> Default for RawIterHash<T> {
42154246
#[cfg_attr(feature = "inline-more", inline)]
42164247
fn default() -> Self {
42174248
Self {
4218-
// SAFETY: Because the table is static, it always outlives the iter.
4219-
inner: unsafe { RawIterHashInner::new(&RawTableInner::NEW, 0) },
4249+
inner: RawIterHashIndices::default(),
42204250
_marker: PhantomData,
42214251
}
42224252
}
42234253
}
42244254

4225-
impl RawIterHashInner {
4255+
impl Default for RawIterHashIndices {
4256+
#[cfg_attr(feature = "inline-more", inline)]
4257+
fn default() -> Self {
4258+
// SAFETY: Because the table is static, it always outlives the iter.
4259+
unsafe { RawIterHashIndices::new(&RawTableInner::NEW, 0) }
4260+
}
4261+
}
4262+
4263+
impl RawIterHashIndices {
42264264
#[cfg_attr(feature = "inline-more", inline)]
42274265
unsafe fn new(table: &RawTableInner, hash: u64) -> Self {
42284266
let tag_hash = Tag::full(hash);
42294267
let probe_seq = table.probe_seq(hash);
42304268
let group = Group::load(table.ctrl(probe_seq.pos));
42314269
let bitmask = group.match_tag(tag_hash).into_iter();
42324270

4233-
RawIterHashInner {
4271+
RawIterHashIndices {
42344272
bucket_mask: table.bucket_mask,
42354273
ctrl: table.ctrl,
42364274
tag_hash,
@@ -4260,7 +4298,7 @@ impl<T> Iterator for RawIterHash<T> {
42604298
}
42614299
}
42624300

4263-
impl Iterator for RawIterHashInner {
4301+
impl Iterator for RawIterHashIndices {
42644302
type Item = usize;
42654303

42664304
fn next(&mut self) -> Option<Self::Item> {

src/table.rs

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use core::{fmt, iter::FusedIterator, marker::PhantomData};
33
use crate::{
44
control::Tag,
55
raw::{
6-
Allocator, Bucket, Global, InsertSlot, RawDrain, RawExtractIf, RawIntoIter, RawIter,
7-
RawIterHash, RawTable,
6+
Allocator, Bucket, FullBucketsIndices, Global, InsertSlot, RawDrain, RawExtractIf,
7+
RawIntoIter, RawIter, RawIterHash, RawIterHashIndices, RawTable,
88
},
99
TryReserveError,
1010
};
@@ -1000,7 +1000,7 @@ where
10001000
/// let mut table = HashTable::new();
10011001
/// let hasher = DefaultHashBuilder::default();
10021002
/// let hasher = |val: &_| hasher.hash_one(val);
1003-
/// table.insert_unique(hasher(&"a"), "b", hasher);
1003+
/// table.insert_unique(hasher(&"a"), "a", hasher);
10041004
/// table.insert_unique(hasher(&"b"), "b", hasher);
10051005
///
10061006
/// // Will print in an arbitrary order.
@@ -1071,6 +1071,42 @@ where
10711071
}
10721072
}
10731073

1074+
/// An iterator producing the `usize` indices of all occupied buckets.
1075+
///
1076+
/// The order in which the iterator yields indices is unspecified
1077+
/// and may change in the future.
1078+
///
1079+
/// # Examples
1080+
///
1081+
/// ```
1082+
/// # #[cfg(feature = "nightly")]
1083+
/// # fn test() {
1084+
/// use hashbrown::{HashTable, DefaultHashBuilder};
1085+
/// use std::hash::BuildHasher;
1086+
///
1087+
/// let mut table = HashTable::new();
1088+
/// let hasher = DefaultHashBuilder::default();
1089+
/// let hasher = |val: &_| hasher.hash_one(val);
1090+
/// table.insert_unique(hasher(&"a"), "a", hasher);
1091+
/// table.insert_unique(hasher(&"b"), "b", hasher);
1092+
///
1093+
/// // Will print in an arbitrary order.
1094+
/// for index in table.iter_buckets() {
1095+
/// println!("{index}: {}", table.get_bucket(index).unwrap());
1096+
/// }
1097+
/// # }
1098+
/// # fn main() {
1099+
/// # #[cfg(feature = "nightly")]
1100+
/// # test()
1101+
/// # }
1102+
/// ```
1103+
pub fn iter_buckets(&self) -> IterBuckets<'_> {
1104+
IterBuckets {
1105+
inner: unsafe { self.raw.full_buckets_indices() },
1106+
marker: PhantomData,
1107+
}
1108+
}
1109+
10741110
/// An iterator visiting all elements which may match a hash.
10751111
/// The iterator element type is `&'a T`.
10761112
///
@@ -1163,6 +1199,47 @@ where
11631199
}
11641200
}
11651201

1202+
/// An iterator producing the `usize` indices of all buckets which may match a hash.
1203+
///
1204+
/// This iterator may return indices from the table that have a hash value
1205+
/// different than the one provided. You should always validate the returned
1206+
/// values before using them.
1207+
///
1208+
/// The order in which the iterator yields indices is unspecified
1209+
/// and may change in the future.
1210+
///
1211+
/// # Examples
1212+
///
1213+
/// ```
1214+
/// # #[cfg(feature = "nightly")]
1215+
/// # fn test() {
1216+
/// use hashbrown::{HashTable, DefaultHashBuilder};
1217+
/// use std::hash::BuildHasher;
1218+
///
1219+
/// let mut table = HashTable::new();
1220+
/// let hasher = DefaultHashBuilder::default();
1221+
/// let hasher = |val: &_| hasher.hash_one(val);
1222+
/// table.insert_unique(hasher(&"a"), "a", hasher);
1223+
/// table.insert_unique(hasher(&"a"), "b", hasher);
1224+
/// table.insert_unique(hasher(&"b"), "c", hasher);
1225+
///
1226+
/// // Will print the indices with "a" and "b" (and possibly "c") in an arbitrary order.
1227+
/// for index in table.iter_hash_buckets(hasher(&"a")) {
1228+
/// println!("{index}: {}", table.get_bucket(index).unwrap());
1229+
/// }
1230+
/// # }
1231+
/// # fn main() {
1232+
/// # #[cfg(feature = "nightly")]
1233+
/// # test()
1234+
/// # }
1235+
/// ```
1236+
pub fn iter_hash_buckets(&self, hash: u64) -> IterHashBuckets<'_> {
1237+
IterHashBuckets {
1238+
inner: unsafe { self.raw.iter_hash_buckets(hash) },
1239+
marker: PhantomData,
1240+
}
1241+
}
1242+
11661243
/// Retains only the elements specified by the predicate.
11671244
///
11681245
/// In other words, remove all elements `e` such that `f(&e)` returns `false`.
@@ -2484,6 +2561,46 @@ where
24842561
}
24852562
}
24862563

2564+
/// An iterator producing the `usize` indices of all occupied buckets,
2565+
/// within the range `0..table.num_buckets()`.
2566+
///
2567+
/// The order in which the iterator yields indices is unspecified
2568+
/// and may change in the future.
2569+
///
2570+
/// This `struct` is created by the [`HashTable::iter_buckets`] method. See its
2571+
/// documentation for more.
2572+
#[derive(Clone, Default)]
2573+
pub struct IterBuckets<'a> {
2574+
inner: FullBucketsIndices,
2575+
marker: PhantomData<&'a ()>,
2576+
}
2577+
2578+
impl Iterator for IterBuckets<'_> {
2579+
type Item = usize;
2580+
2581+
fn next(&mut self) -> Option<usize> {
2582+
self.inner.next()
2583+
}
2584+
2585+
fn size_hint(&self) -> (usize, Option<usize>) {
2586+
self.inner.size_hint()
2587+
}
2588+
}
2589+
2590+
impl ExactSizeIterator for IterBuckets<'_> {
2591+
fn len(&self) -> usize {
2592+
self.inner.len()
2593+
}
2594+
}
2595+
2596+
impl FusedIterator for IterBuckets<'_> {}
2597+
2598+
impl fmt::Debug for IterBuckets<'_> {
2599+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2600+
f.debug_list().entries(self.clone()).finish()
2601+
}
2602+
}
2603+
24872604
/// An iterator over the entries of a `HashTable` that could match a given hash.
24882605
/// The iterator element type is `&'a T`.
24892606
///
@@ -2610,6 +2727,32 @@ where
26102727
}
26112728
}
26122729

2730+
/// An iterator producing the `usize` indices of all buckets which may match a hash.
2731+
///
2732+
/// This `struct` is created by the [`HashTable::iter_hash_buckets`] method. See its
2733+
/// documentation for more.
2734+
#[derive(Clone, Default)]
2735+
pub struct IterHashBuckets<'a> {
2736+
inner: RawIterHashIndices,
2737+
marker: PhantomData<&'a ()>,
2738+
}
2739+
2740+
impl Iterator for IterHashBuckets<'_> {
2741+
type Item = usize;
2742+
2743+
fn next(&mut self) -> Option<Self::Item> {
2744+
self.inner.next()
2745+
}
2746+
}
2747+
2748+
impl FusedIterator for IterHashBuckets<'_> {}
2749+
2750+
impl fmt::Debug for IterHashBuckets<'_> {
2751+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2752+
f.debug_list().entries(self.clone()).finish()
2753+
}
2754+
}
2755+
26132756
/// An owning iterator over the entries of a `HashTable` in arbitrary order.
26142757
/// The iterator element type is `T`.
26152758
///

0 commit comments

Comments
 (0)