Skip to content

Commit cc51a5f

Browse files
committed
Clean up RawTable API
1 parent eabe7f8 commit cc51a5f

File tree

3 files changed

+86
-55
lines changed

3 files changed

+86
-55
lines changed

src/map.rs

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,19 +1783,16 @@ where
17831783
#[cfg_attr(feature = "inline-more", inline)]
17841784
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
17851785
let hash = make_insert_hash::<K, S>(&self.hash_builder, &k);
1786-
self.table
1787-
.reserve(1, make_hasher::<_, V, S>(&self.hash_builder));
1788-
1789-
unsafe {
1790-
let (index, found) = self.table.find_potential(hash, equivalent_key(&k));
1791-
1792-
let bucket = self.table.bucket(index);
1793-
1794-
if found {
1795-
Some(mem::replace(&mut bucket.as_mut().1, v))
1796-
} else {
1797-
self.table.mark_inserted(index, hash);
1798-
bucket.write((k, v));
1786+
let hasher = make_hasher::<_, V, S>(&self.hash_builder);
1787+
match self
1788+
.table
1789+
.find_or_find_insert_slot(hash, equivalent_key(&k), hasher)
1790+
{
1791+
Ok(bucket) => Some(mem::replace(unsafe { &mut bucket.as_mut().1 }, v)),
1792+
Err(slot) => {
1793+
unsafe {
1794+
self.table.insert_in_slot(hash, slot, (k, v));
1795+
}
17991796
None
18001797
}
18011798
}
@@ -2178,7 +2175,7 @@ impl<K, V, S, A: Allocator + Clone> HashMap<K, V, S, A> {
21782175
/// {
21792176
/// let raw_table = map.raw_table_mut();
21802177
/// match raw_table.find(hash, is_match) {
2181-
/// Some(bucket) => Some(unsafe { raw_table.remove(bucket) }),
2178+
/// Some(bucket) => Some(unsafe { raw_table.remove(bucket).0 }),
21822179
/// None => None,
21832180
/// }
21842181
/// }
@@ -2801,7 +2798,7 @@ impl<K, V, A: Allocator + Clone> ExtractIfInner<'_, K, V, A> {
28012798
for item in &mut self.iter {
28022799
let &mut (ref key, ref mut value) = item.as_mut();
28032800
if f(key, value) {
2804-
return Some(self.table.remove(item));
2801+
return Some(self.table.remove(item).0);
28052802
}
28062803
}
28072804
}
@@ -3922,7 +3919,7 @@ impl<'a, K, V, S, A: Allocator + Clone> RawOccupiedEntryMut<'a, K, V, S, A> {
39223919
/// ```
39233920
#[cfg_attr(feature = "inline-more", inline)]
39243921
pub fn remove_entry(self) -> (K, V) {
3925-
unsafe { self.table.remove(self.elem) }
3922+
unsafe { self.table.remove(self.elem).0 }
39263923
}
39273924

39283925
/// Provides shared access to the key and owned access to the value of
@@ -5295,7 +5292,7 @@ impl<'a, K, V, S, A: Allocator + Clone> OccupiedEntry<'a, K, V, S, A> {
52955292
/// ```
52965293
#[cfg_attr(feature = "inline-more", inline)]
52975294
pub fn remove_entry(self) -> (K, V) {
5298-
unsafe { self.table.table.remove(self.elem) }
5295+
unsafe { self.table.table.remove(self.elem).0 }
52995296
}
53005297

53015298
/// Gets a reference to the value in the entry.
@@ -6017,7 +6014,7 @@ impl<'a, 'b, K, Q: ?Sized, V, S, A: Allocator + Clone> OccupiedEntryRef<'a, 'b,
60176014
/// ```
60186015
#[cfg_attr(feature = "inline-more", inline)]
60196016
pub fn remove_entry(self) -> (K, V) {
6020-
unsafe { self.table.table.remove(self.elem) }
6017+
unsafe { self.table.table.remove(self.elem).0 }
60216018
}
60226019

60236020
/// Gets a reference to the value in the entry.
@@ -8365,7 +8362,7 @@ mod test_map {
83658362
let e = map.table.find(hash_value, |q| q.0.eq(&i));
83668363
if let Some(e) = e {
83678364
it.reflect_remove(&e);
8368-
let t = map.table.remove(e);
8365+
let t = map.table.remove(e).0;
83698366
removed.push(t);
83708367
left -= 1;
83718368
} else {

src/raw/mod.rs

Lines changed: 69 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ impl TableLayout {
282282
}
283283
}
284284

285+
/// A reference to an empty bucket into which an can be inserted.
286+
pub struct InsertSlot {
287+
index: usize,
288+
}
289+
285290
/// A reference to a hash table bucket containing a `T`.
286291
///
287292
/// This is usually just a pointer to the element itself. However if the element
@@ -1001,19 +1006,26 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
10011006
}
10021007

10031008
/// Removes an element from the table, returning it.
1009+
///
1010+
/// This also returns an `InsertSlot` pointing to the newly free bucket.
10041011
#[cfg_attr(feature = "inline-more", inline)]
10051012
#[allow(clippy::needless_pass_by_value)]
1006-
pub unsafe fn remove(&mut self, item: Bucket<T>) -> T {
1013+
pub unsafe fn remove(&mut self, item: Bucket<T>) -> (T, InsertSlot) {
10071014
self.erase_no_drop(&item);
1008-
item.read()
1015+
(
1016+
item.read(),
1017+
InsertSlot {
1018+
index: self.bucket_index(&item),
1019+
},
1020+
)
10091021
}
10101022

10111023
/// Finds and removes an element from the table, returning it.
10121024
#[cfg_attr(feature = "inline-more", inline)]
10131025
pub fn remove_entry(&mut self, hash: u64, eq: impl FnMut(&T) -> bool) -> Option<T> {
10141026
// Avoid `Option::map` because it bloats LLVM IR.
10151027
match self.find(hash, eq) {
1016-
Some(bucket) => Some(unsafe { self.remove(bucket) }),
1028+
Some(bucket) => Some(unsafe { self.remove(bucket).0 }),
10171029
None => None,
10181030
}
10191031
}
@@ -1161,22 +1173,18 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
11611173
#[cfg_attr(feature = "inline-more", inline)]
11621174
pub fn insert(&mut self, hash: u64, value: T, hasher: impl Fn(&T) -> u64) -> Bucket<T> {
11631175
unsafe {
1164-
let mut index = self.table.find_insert_slot(hash);
1176+
let mut slot = self.table.find_insert_slot(hash);
11651177

11661178
// We can avoid growing the table once we have reached our load
11671179
// factor if we are replacing a tombstone. This works since the
11681180
// number of EMPTY slots does not change in this case.
1169-
let old_ctrl = *self.table.ctrl(index);
1181+
let old_ctrl = *self.table.ctrl(slot.index);
11701182
if unlikely(self.table.growth_left == 0 && special_is_empty(old_ctrl)) {
11711183
self.reserve(1, hasher);
1172-
index = self.table.find_insert_slot(hash);
1184+
slot = self.table.find_insert_slot(hash);
11731185
}
11741186

1175-
self.table.record_item_insert_at(index, old_ctrl, hash);
1176-
1177-
let bucket = self.bucket(index);
1178-
bucket.write(value);
1179-
bucket
1187+
self.insert_in_slot(hash, slot, value)
11801188
}
11811189
}
11821190

@@ -1244,7 +1252,7 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
12441252
let old_ctrl = *self.table.ctrl(index);
12451253
debug_assert!(self.is_bucket_full(index));
12461254
let old_growth_left = self.table.growth_left;
1247-
let item = self.remove(bucket);
1255+
let item = self.remove(bucket).0;
12481256
if let Some(new_item) = f(item) {
12491257
self.table.growth_left = old_growth_left;
12501258
self.table.set_ctrl(index, old_ctrl);
@@ -1256,20 +1264,47 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
12561264
}
12571265
}
12581266

1259-
/// Searches for an element in the table,
1260-
/// or a potential slot where that element could be inserted.
1267+
/// Searches for an element in the table. If the element is not found,
1268+
/// returns `Err` with the position of a slot where an element with the
1269+
/// same hash could be inserted.
1270+
///
1271+
/// This function may resize the table if additional space is required for
1272+
/// inserting an element.
12611273
#[inline]
1262-
pub fn find_potential(&self, hash: u64, mut eq: impl FnMut(&T) -> bool) -> (usize, bool) {
1263-
self.table.find_potential_inner(hash, &mut |index| unsafe {
1264-
eq(self.bucket(index).as_ref())
1265-
})
1274+
pub fn find_or_find_insert_slot(
1275+
&mut self,
1276+
hash: u64,
1277+
mut eq: impl FnMut(&T) -> bool,
1278+
hasher: impl Fn(&T) -> u64,
1279+
) -> Result<Bucket<T>, InsertSlot> {
1280+
self.reserve(1, hasher);
1281+
1282+
match self
1283+
.table
1284+
.find_or_find_insert_slot_inner(hash, &mut |index| unsafe {
1285+
eq(self.bucket(index).as_ref())
1286+
}) {
1287+
Ok(index) => Ok(unsafe { self.bucket(index) }),
1288+
Err(slot) => Err(slot),
1289+
}
12661290
}
12671291

1268-
/// Marks an element in the table as inserted.
1292+
/// Inserts a new element into the table in the given slot, and returns its
1293+
/// raw bucket.
1294+
///
1295+
/// # Safety
1296+
///
1297+
/// `slot` must point to a slot previously returned by
1298+
/// `find_or_find_insert_slot`, and no mutation of the table must have
1299+
/// occurred since that call.
12691300
#[inline]
1270-
pub unsafe fn mark_inserted(&mut self, index: usize, hash: u64) {
1271-
let old_ctrl = *self.table.ctrl(index);
1272-
self.table.record_item_insert_at(index, old_ctrl, hash);
1301+
pub unsafe fn insert_in_slot(&mut self, hash: u64, slot: InsertSlot, value: T) -> Bucket<T> {
1302+
let old_ctrl = *self.table.ctrl(slot.index);
1303+
self.table.record_item_insert_at(slot.index, old_ctrl, hash);
1304+
1305+
let bucket = self.bucket(slot.index);
1306+
bucket.write(value);
1307+
bucket
12731308
}
12741309

12751310
/// Searches for an element in the table.
@@ -1608,7 +1643,7 @@ impl<A: Allocator + Clone> RawTableInner<A> {
16081643
/// Fixes up an insertion slot due to false positives for groups smaller than the group width.
16091644
/// This must only be used on insertion slots found by `find_insert_slot_in_group`.
16101645
#[inline]
1611-
unsafe fn fix_insert_slot(&self, index: usize) -> usize {
1646+
unsafe fn fix_insert_slot(&self, mut index: usize) -> InsertSlot {
16121647
// In tables smaller than the group width
16131648
// (self.buckets() < Group::WIDTH), trailing control
16141649
// bytes outside the range of the table are filled with
@@ -1636,12 +1671,11 @@ impl<A: Allocator + Clone> RawTableInner<A> {
16361671
// with EMPTY bytes, so this second scan either finds an empty slot (due to the
16371672
// load factor) or hits the trailing control bytes (containing EMPTY). See
16381673
// `intrinsics::cttz_nonzero` for more information.
1639-
Group::load_aligned(self.ctrl(0))
1674+
index = Group::load_aligned(self.ctrl(0))
16401675
.match_empty_or_deleted()
1641-
.lowest_set_bit_nonzero()
1642-
} else {
1643-
index
1676+
.lowest_set_bit_nonzero();
16441677
}
1678+
InsertSlot { index }
16451679
}
16461680

16471681
/// Finds the position to insert something in a group.
@@ -1663,11 +1697,11 @@ impl<A: Allocator + Clone> RawTableInner<A> {
16631697
/// This uses dynamic dispatch to reduce the amount of code generated, but that is
16641698
/// eliminated by LLVM optimizations.
16651699
#[inline]
1666-
pub fn find_potential_inner(
1700+
fn find_or_find_insert_slot_inner(
16671701
&self,
16681702
hash: u64,
16691703
eq: &mut dyn FnMut(usize) -> bool,
1670-
) -> (usize, bool) {
1704+
) -> Result<usize, InsertSlot> {
16711705
let mut insert_slot = None;
16721706

16731707
let h2_hash = h2(hash);
@@ -1680,7 +1714,7 @@ impl<A: Allocator + Clone> RawTableInner<A> {
16801714
let index = (probe_seq.pos + bit) & self.bucket_mask;
16811715

16821716
if likely(eq(index)) {
1683-
return (index, true);
1717+
return Ok(index);
16841718
}
16851719
}
16861720

@@ -1697,7 +1731,7 @@ impl<A: Allocator + Clone> RawTableInner<A> {
16971731
// least one. For tables smaller than the group width, there will still be an
16981732
// empty element in the current (and only) group due to the load factor.
16991733
unsafe {
1700-
return (self.fix_insert_slot(insert_slot.unwrap_unchecked()), false);
1734+
return Err(self.fix_insert_slot(insert_slot.unwrap_unchecked()));
17011735
}
17021736
}
17031737

@@ -1711,7 +1745,7 @@ impl<A: Allocator + Clone> RawTableInner<A> {
17111745
/// There must be at least 1 empty bucket in the table.
17121746
#[inline]
17131747
unsafe fn prepare_insert_slot(&self, hash: u64) -> (usize, u8) {
1714-
let index = self.find_insert_slot(hash);
1748+
let index = self.find_insert_slot(hash).index;
17151749
let old_ctrl = *self.ctrl(index);
17161750
self.set_ctrl_h2(index, hash);
17171751
(index, old_ctrl)
@@ -1739,7 +1773,7 @@ impl<A: Allocator + Clone> RawTableInner<A> {
17391773
///
17401774
/// [`undefined behavior`]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
17411775
#[inline]
1742-
fn find_insert_slot(&self, hash: u64) -> usize {
1776+
fn find_insert_slot(&self, hash: u64) -> InsertSlot {
17431777
let mut probe_seq = self.probe_seq(hash);
17441778
loop {
17451779
// SAFETY:
@@ -1922,7 +1956,7 @@ impl<A: Allocator + Clone> RawTableInner<A> {
19221956
#[cfg(feature = "raw")]
19231957
#[inline]
19241958
unsafe fn prepare_insert_no_grow(&mut self, hash: u64) -> Result<usize, ()> {
1925-
let index = self.find_insert_slot(hash);
1959+
let index = self.find_insert_slot(hash).index;
19261960
let old_ctrl = *self.ctrl(index);
19271961
if unlikely(self.growth_left == 0 && special_is_empty(old_ctrl)) {
19281962
Err(())
@@ -2293,7 +2327,7 @@ impl<A: Allocator + Clone> RawTableInner<A> {
22932327
let hash = hasher(*guard, i);
22942328

22952329
// Search for a suitable place to put it
2296-
let new_i = guard.find_insert_slot(hash);
2330+
let new_i = guard.find_insert_slot(hash).index;
22972331

22982332
// Probing works by scanning through all of the control
22992333
// bytes in groups, which may not be aligned to the group

src/rustc_entry.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ impl<'a, K, V, A: Allocator + Clone> RustcOccupiedEntry<'a, K, V, A> {
330330
/// ```
331331
#[cfg_attr(feature = "inline-more", inline)]
332332
pub fn remove_entry(self) -> (K, V) {
333-
unsafe { self.table.remove(self.elem) }
333+
unsafe { self.table.remove(self.elem).0 }
334334
}
335335

336336
/// Gets a reference to the value in the entry.

0 commit comments

Comments
 (0)