Skip to content

Commit bb48357

Browse files
authored
Merge pull request #263 from cuviper/insert_in_slot
Use hashbrown's new single-lookup insertion
2 parents ee71507 + c37dae6 commit bb48357

File tree

3 files changed

+46
-24
lines changed

3 files changed

+46
-24
lines changed

src/map/core.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -275,18 +275,14 @@ impl<K, V> IndexMapCore<K, V> {
275275
}
276276
}
277277

278-
/// Append a key-value pair, *without* checking whether it already exists,
279-
/// and return the pair's new index.
280-
fn push(&mut self, hash: HashValue, key: K, value: V) -> usize {
281-
let i = self.entries.len();
282-
self.indices.insert(hash.get(), i, get_hash(&self.entries));
283-
if i == self.entries.capacity() {
278+
/// Append a key-value pair to `entries`, *without* checking whether it already exists.
279+
fn push_entry(&mut self, hash: HashValue, key: K, value: V) {
280+
if self.entries.len() == self.entries.capacity() {
284281
// Reserve our own capacity synced to the indices,
285282
// rather than letting `Vec::push` just double it.
286283
self.reserve_entries(1);
287284
}
288285
self.entries.push(Bucket { hash, key, value });
289-
i
290286
}
291287

292288
/// Return the index in `entries` where an equivalent key can be found
@@ -302,9 +298,13 @@ impl<K, V> IndexMapCore<K, V> {
302298
where
303299
K: Eq,
304300
{
305-
match self.get_index_of(hash, &key) {
306-
Some(i) => (i, Some(mem::replace(&mut self.entries[i].value, value))),
307-
None => (self.push(hash, key, value), None),
301+
match self.find_or_insert(hash, &key) {
302+
Ok(i) => (i, Some(mem::replace(&mut self.entries[i].value, value))),
303+
Err(i) => {
304+
debug_assert_eq!(i, self.entries.len());
305+
self.push_entry(hash, key, value);
306+
(i, None)
307+
}
308308
}
309309
}
310310

@@ -712,14 +712,18 @@ impl<'a, K, V> VacantEntry<'a, K, V> {
712712

713713
/// Return the index where the key-value pair will be inserted.
714714
pub fn index(&self) -> usize {
715-
self.map.len()
715+
self.map.indices.len()
716716
}
717717

718718
/// Inserts the entry's key and the given value into the map, and returns a mutable reference
719719
/// to the value.
720720
pub fn insert(self, value: V) -> &'a mut V {
721-
let i = self.map.push(self.hash, self.key, value);
722-
&mut self.map.entries[i].value
721+
let i = self.index();
722+
let Self { map, hash, key } = self;
723+
map.indices.insert(hash.get(), i, get_hash(&map.entries));
724+
debug_assert_eq!(i, map.entries.len());
725+
map.push_entry(hash, key, value);
726+
&mut map.entries[i].value
723727
}
724728
}
725729

src/map/core/raw.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! This module encapsulates the `unsafe` access to `hashbrown::raw::RawTable`,
33
//! mostly in dealing with its bucket "pointers".
44
5-
use super::{equivalent, Bucket, Entry, HashValue, IndexMapCore, VacantEntry};
5+
use super::{equivalent, get_hash, Bucket, Entry, HashValue, IndexMapCore, VacantEntry};
66
use core::fmt;
77
use core::mem::replace;
88
use hashbrown::raw::RawTable;
@@ -48,6 +48,32 @@ impl<K, V> IndexMapCore<K, V> {
4848
}
4949
}
5050

51+
/// Search for a key in the table and return `Ok(entry_index)` if found.
52+
/// Otherwise, insert the key and return `Err(new_index)`.
53+
///
54+
/// Note that hashbrown may resize the table to reserve space for insertion,
55+
/// even before checking if it's already present, so this is somewhat biased
56+
/// towards new items.
57+
pub(crate) fn find_or_insert(&mut self, hash: HashValue, key: &K) -> Result<usize, usize>
58+
where
59+
K: Eq,
60+
{
61+
let hash = hash.get();
62+
let eq = equivalent(key, &self.entries);
63+
let hasher = get_hash(&self.entries);
64+
// SAFETY: We're not mutating between find and read/insert.
65+
unsafe {
66+
match self.indices.find_or_find_insert_slot(hash, eq, hasher) {
67+
Ok(raw_bucket) => Ok(*raw_bucket.as_ref()),
68+
Err(slot) => {
69+
let index = self.indices.len();
70+
self.indices.insert_in_slot(hash, slot, index);
71+
Err(index)
72+
}
73+
}
74+
}
75+
}
76+
5177
pub(crate) fn entry(&mut self, hash: HashValue, key: K) -> Entry<'_, K, V>
5278
where
5379
K: Eq,

src/set.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -335,16 +335,8 @@ where
335335
///
336336
/// Computes in **O(1)** time (amortized average).
337337
pub fn insert_full(&mut self, value: T) -> (usize, bool) {
338-
use super::map::Entry::*;
339-
340-
match self.map.entry(value) {
341-
Occupied(e) => (e.index(), false),
342-
Vacant(e) => {
343-
let index = e.index();
344-
e.insert(());
345-
(index, true)
346-
}
347-
}
338+
let (index, existing) = self.map.insert_full(value, ());
339+
(index, existing.is_none())
348340
}
349341

350342
/// Return an iterator over the values that are in `self` but not `other`.

0 commit comments

Comments
 (0)