Skip to content

Commit 21be06c

Browse files
authored
Merge pull request #657 from cuviper/table-bucket
Add `HashTable` methods related to the raw bucket index
2 parents 9b170c3 + af971f3 commit 21be06c

File tree

2 files changed

+699
-28
lines changed

2 files changed

+699
-28
lines changed

src/raw/mod.rs

Lines changed: 119 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,19 @@ impl<T, A: Allocator> RawTable<T, A> {
838838
(item.read(), self.bucket_index(&item))
839839
}
840840

841+
/// Removes an element from the table, returning it.
842+
///
843+
/// This also returns an index to the newly free bucket
844+
/// and the former `Tag` for that bucket.
845+
#[cfg_attr(feature = "inline-more", inline)]
846+
#[allow(clippy::needless_pass_by_value)]
847+
pub(crate) unsafe fn remove_tagged(&mut self, item: Bucket<T>) -> (T, usize, Tag) {
848+
let index = self.bucket_index(&item);
849+
let tag = *self.table.ctrl(index);
850+
self.table.erase(index);
851+
(item.read(), index, tag)
852+
}
853+
841854
/// Finds and removes an element from the table, returning it.
842855
#[cfg_attr(feature = "inline-more", inline)]
843856
pub fn remove_entry(&mut self, hash: u64, eq: impl FnMut(&T) -> bool) -> Option<T> {
@@ -1172,8 +1185,8 @@ impl<T, A: Allocator> RawTable<T, A> {
11721185
}
11731186
}
11741187

1175-
/// Inserts a new element into the table at the given index, and returns its
1176-
/// raw bucket.
1188+
/// Inserts a new element into the table at the given index with the given hash,
1189+
/// and returns its raw bucket.
11771190
///
11781191
/// # Safety
11791192
///
@@ -1182,8 +1195,26 @@ impl<T, A: Allocator> RawTable<T, A> {
11821195
/// occurred since that call.
11831196
#[inline]
11841197
pub unsafe fn insert_at_index(&mut self, hash: u64, index: usize, value: T) -> Bucket<T> {
1198+
self.insert_tagged_at_index(Tag::full(hash), index, value)
1199+
}
1200+
1201+
/// Inserts a new element into the table at the given index with the given tag,
1202+
/// and returns its raw bucket.
1203+
///
1204+
/// # Safety
1205+
///
1206+
/// `index` must point to a slot previously returned by
1207+
/// `find_or_find_insert_index`, and no mutation of the table must have
1208+
/// occurred since that call.
1209+
#[inline]
1210+
pub(crate) unsafe fn insert_tagged_at_index(
1211+
&mut self,
1212+
tag: Tag,
1213+
index: usize,
1214+
value: T,
1215+
) -> Bucket<T> {
11851216
let old_ctrl = *self.table.ctrl(index);
1186-
self.table.record_item_insert_at(index, old_ctrl, hash);
1217+
self.table.record_item_insert_at(index, old_ctrl, tag);
11871218

11881219
let bucket = self.bucket(index);
11891220
bucket.write(value);
@@ -1233,6 +1264,43 @@ impl<T, A: Allocator> RawTable<T, A> {
12331264
}
12341265
}
12351266

1267+
/// Gets a reference to an element in the table at the given bucket index.
1268+
#[inline]
1269+
pub fn get_bucket(&self, index: usize) -> Option<&T> {
1270+
unsafe {
1271+
if index < self.buckets() && self.is_bucket_full(index) {
1272+
Some(self.bucket(index).as_ref())
1273+
} else {
1274+
None
1275+
}
1276+
}
1277+
}
1278+
1279+
/// Gets a mutable reference to an element in the table at the given bucket index.
1280+
#[inline]
1281+
pub fn get_bucket_mut(&mut self, index: usize) -> Option<&mut T> {
1282+
unsafe {
1283+
if index < self.buckets() && self.is_bucket_full(index) {
1284+
Some(self.bucket(index).as_mut())
1285+
} else {
1286+
None
1287+
}
1288+
}
1289+
}
1290+
1291+
/// Returns a pointer to an element in the table, but only after verifying that
1292+
/// the index is in-bounds and the bucket is occupied.
1293+
#[inline]
1294+
pub fn checked_bucket(&self, index: usize) -> Option<Bucket<T>> {
1295+
unsafe {
1296+
if index < self.buckets() && self.is_bucket_full(index) {
1297+
Some(self.bucket(index))
1298+
} else {
1299+
None
1300+
}
1301+
}
1302+
}
1303+
12361304
/// Attempts to get mutable references to `N` entries in the table at once.
12371305
///
12381306
/// Returns an array of length `N` with the results of each query.
@@ -1346,6 +1414,28 @@ impl<T, A: Allocator> RawTable<T, A> {
13461414
RawIterHash::new(self, hash)
13471415
}
13481416

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+
13491439
/// Returns an iterator which removes all elements from the table without
13501440
/// freeing the memory.
13511441
#[cfg_attr(feature = "inline-more", inline)]
@@ -2405,9 +2495,9 @@ impl RawTableInner {
24052495
}
24062496

24072497
#[inline]
2408-
unsafe fn record_item_insert_at(&mut self, index: usize, old_ctrl: Tag, hash: u64) {
2498+
unsafe fn record_item_insert_at(&mut self, index: usize, old_ctrl: Tag, new_ctrl: Tag) {
24092499
self.growth_left -= usize::from(old_ctrl.special_is_empty());
2410-
self.set_ctrl_hash(index, hash);
2500+
self.set_ctrl(index, new_ctrl);
24112501
self.items += 1;
24122502
}
24132503

@@ -3803,6 +3893,7 @@ impl<T> FusedIterator for RawIter<T> {}
38033893
/// created will be yielded by that iterator.
38043894
/// - The order in which the iterator yields indices of the buckets is unspecified
38053895
/// and may change in the future.
3896+
#[derive(Clone)]
38063897
pub(crate) struct FullBucketsIndices {
38073898
// Mask of full buckets in the current group. Bits are cleared from this
38083899
// mask as each element is processed.
@@ -3820,6 +3911,14 @@ pub(crate) struct FullBucketsIndices {
38203911
items: usize,
38213912
}
38223913

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+
38233922
impl FullBucketsIndices {
38243923
/// Advances the iterator and returns the next value.
38253924
///
@@ -4085,12 +4184,12 @@ impl<T, A: Allocator> FusedIterator for RawDrain<'_, T, A> {}
40854184
/// - The order in which the iterator yields buckets is unspecified and may
40864185
/// change in the future.
40874186
pub struct RawIterHash<T> {
4088-
inner: RawIterHashInner,
4187+
inner: RawIterHashIndices,
40894188
_marker: PhantomData<T>,
40904189
}
40914190

40924191
#[derive(Clone)]
4093-
struct RawIterHashInner {
4192+
pub(crate) struct RawIterHashIndices {
40944193
// See `RawTableInner`'s corresponding fields for details.
40954194
// We can't store a `*const RawTableInner` as it would get
40964195
// invalidated by the user calling `&mut` methods on `RawTable`.
@@ -4113,7 +4212,7 @@ impl<T> RawIterHash<T> {
41134212
#[cfg_attr(feature = "inline-more", inline)]
41144213
unsafe fn new<A: Allocator>(table: &RawTable<T, A>, hash: u64) -> Self {
41154214
RawIterHash {
4116-
inner: RawIterHashInner::new(&table.table, hash),
4215+
inner: RawIterHashIndices::new(&table.table, hash),
41174216
_marker: PhantomData,
41184217
}
41194218
}
@@ -4133,22 +4232,29 @@ impl<T> Default for RawIterHash<T> {
41334232
#[cfg_attr(feature = "inline-more", inline)]
41344233
fn default() -> Self {
41354234
Self {
4136-
// SAFETY: Because the table is static, it always outlives the iter.
4137-
inner: unsafe { RawIterHashInner::new(&RawTableInner::NEW, 0) },
4235+
inner: RawIterHashIndices::default(),
41384236
_marker: PhantomData,
41394237
}
41404238
}
41414239
}
41424240

4143-
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 {
41444250
#[cfg_attr(feature = "inline-more", inline)]
41454251
unsafe fn new(table: &RawTableInner, hash: u64) -> Self {
41464252
let tag_hash = Tag::full(hash);
41474253
let probe_seq = table.probe_seq(hash);
41484254
let group = Group::load(table.ctrl(probe_seq.pos));
41494255
let bitmask = group.match_tag(tag_hash).into_iter();
41504256

4151-
RawIterHashInner {
4257+
RawIterHashIndices {
41524258
bucket_mask: table.bucket_mask,
41534259
ctrl: table.ctrl,
41544260
tag_hash,
@@ -4178,7 +4284,7 @@ impl<T> Iterator for RawIterHash<T> {
41784284
}
41794285
}
41804286

4181-
impl Iterator for RawIterHashInner {
4287+
impl Iterator for RawIterHashIndices {
41824288
type Item = usize;
41834289

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

0 commit comments

Comments
 (0)