Skip to content

Commit 12ad6f4

Browse files
committed
Add HashTable::iter_buckets and iter_hash_buckets
1 parent 6b3687e commit 12ad6f4

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
@@ -1414,6 +1414,28 @@ impl<T, A: Allocator> RawTable<T, A> {
14141414
RawIterHash::new(self, hash)
14151415
}
14161416

1417+
/// Returns an iterator over occupied bucket indices that could match a given hash.
1418+
///
1419+
/// `RawTable` only stores 7 bits of the hash value, so this iterator may
1420+
/// return items that have a hash value different than the one provided. You
1421+
/// should always validate the returned values before using them.
1422+
///
1423+
/// It is up to the caller to ensure that the `RawTable` outlives the
1424+
/// `RawIterHashIndices`. Because we cannot make the `next` method unsafe on the
1425+
/// `RawIterHashIndices` struct, we have to make the `iter_hash_buckets` method unsafe.
1426+
#[cfg_attr(feature = "inline-more", inline)]
1427+
pub(crate) unsafe fn iter_hash_buckets(&self, hash: u64) -> RawIterHashIndices {
1428+
RawIterHashIndices::new(&self.table, hash)
1429+
}
1430+
1431+
/// Returns an iterator over full buckets indices in the table.
1432+
///
1433+
/// See [`RawTableInner::full_buckets_indices`] for safety conditions.
1434+
#[inline(always)]
1435+
pub(crate) unsafe fn full_buckets_indices(&self) -> FullBucketsIndices {
1436+
self.table.full_buckets_indices()
1437+
}
1438+
14171439
/// Returns an iterator which removes all elements from the table without
14181440
/// freeing the memory.
14191441
#[cfg_attr(feature = "inline-more", inline)]
@@ -3871,6 +3893,7 @@ impl<T> FusedIterator for RawIter<T> {}
38713893
/// created will be yielded by that iterator.
38723894
/// - The order in which the iterator yields indices of the buckets is unspecified
38733895
/// and may change in the future.
3896+
#[derive(Clone)]
38743897
pub(crate) struct FullBucketsIndices {
38753898
// Mask of full buckets in the current group. Bits are cleared from this
38763899
// mask as each element is processed.
@@ -3888,6 +3911,14 @@ pub(crate) struct FullBucketsIndices {
38883911
items: usize,
38893912
}
38903913

3914+
impl Default for FullBucketsIndices {
3915+
#[cfg_attr(feature = "inline-more", inline)]
3916+
fn default() -> Self {
3917+
// SAFETY: Because the table is static, it always outlives the iter.
3918+
unsafe { RawTableInner::NEW.full_buckets_indices() }
3919+
}
3920+
}
3921+
38913922
impl FullBucketsIndices {
38923923
/// Advances the iterator and returns the next value.
38933924
///
@@ -4153,12 +4184,12 @@ impl<T, A: Allocator> FusedIterator for RawDrain<'_, T, A> {}
41534184
/// - The order in which the iterator yields buckets is unspecified and may
41544185
/// change in the future.
41554186
pub struct RawIterHash<T> {
4156-
inner: RawIterHashInner,
4187+
inner: RawIterHashIndices,
41574188
_marker: PhantomData<T>,
41584189
}
41594190

41604191
#[derive(Clone)]
4161-
struct RawIterHashInner {
4192+
pub(crate) struct RawIterHashIndices {
41624193
// See `RawTableInner`'s corresponding fields for details.
41634194
// We can't store a `*const RawTableInner` as it would get
41644195
// invalidated by the user calling `&mut` methods on `RawTable`.
@@ -4181,7 +4212,7 @@ impl<T> RawIterHash<T> {
41814212
#[cfg_attr(feature = "inline-more", inline)]
41824213
unsafe fn new<A: Allocator>(table: &RawTable<T, A>, hash: u64) -> Self {
41834214
RawIterHash {
4184-
inner: RawIterHashInner::new(&table.table, hash),
4215+
inner: RawIterHashIndices::new(&table.table, hash),
41854216
_marker: PhantomData,
41864217
}
41874218
}
@@ -4201,22 +4232,29 @@ impl<T> Default for RawIterHash<T> {
42014232
#[cfg_attr(feature = "inline-more", inline)]
42024233
fn default() -> Self {
42034234
Self {
4204-
// SAFETY: Because the table is static, it always outlives the iter.
4205-
inner: unsafe { RawIterHashInner::new(&RawTableInner::NEW, 0) },
4235+
inner: RawIterHashIndices::default(),
42064236
_marker: PhantomData,
42074237
}
42084238
}
42094239
}
42104240

4211-
impl RawIterHashInner {
4241+
impl Default for RawIterHashIndices {
4242+
#[cfg_attr(feature = "inline-more", inline)]
4243+
fn default() -> Self {
4244+
// SAFETY: Because the table is static, it always outlives the iter.
4245+
unsafe { RawIterHashIndices::new(&RawTableInner::NEW, 0) }
4246+
}
4247+
}
4248+
4249+
impl RawIterHashIndices {
42124250
#[cfg_attr(feature = "inline-more", inline)]
42134251
unsafe fn new(table: &RawTableInner, hash: u64) -> Self {
42144252
let tag_hash = Tag::full(hash);
42154253
let probe_seq = table.probe_seq(hash);
42164254
let group = Group::load(table.ctrl(probe_seq.pos));
42174255
let bitmask = group.match_tag(tag_hash).into_iter();
42184256

4219-
RawIterHashInner {
4257+
RawIterHashIndices {
42204258
bucket_mask: table.bucket_mask,
42214259
ctrl: table.ctrl,
42224260
tag_hash,
@@ -4246,7 +4284,7 @@ impl<T> Iterator for RawIterHash<T> {
42464284
}
42474285
}
42484286

4249-
impl Iterator for RawIterHashInner {
4287+
impl Iterator for RawIterHashIndices {
42504288
type Item = usize;
42514289

42524290
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, RawDrain, RawExtractIf, RawIntoIter, RawIter, RawIterHash,
7-
RawTable,
6+
Allocator, Bucket, FullBucketsIndices, Global, RawDrain, RawExtractIf, RawIntoIter,
7+
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)