Skip to content

Commit b4184d0

Browse files
committed
feat: add HashTable::insert_unique_within_capacity
1 parent 6263458 commit b4184d0

File tree

2 files changed

+70
-9
lines changed

2 files changed

+70
-9
lines changed

src/raw/mod.rs

+32-9
Original file line numberDiff line numberDiff line change
@@ -1045,18 +1045,15 @@ impl<T, A: Allocator> RawTable<T, A> {
10451045
)
10461046
}
10471047

1048-
/// Inserts a new element into the table, and returns its raw bucket.
1049-
///
1050-
/// This does not check if the given element already exists in the table.
1051-
#[cfg_attr(feature = "inline-more", inline)]
1052-
pub fn insert(&mut self, hash: u64, value: T, hasher: impl Fn(&T) -> u64) -> Bucket<T> {
1048+
#[inline(always)]
1049+
fn find_insert_slot(&self, hash: u64) -> Option<InsertSlot> {
10531050
unsafe {
10541051
// SAFETY:
10551052
// 1. The [`RawTableInner`] must already have properly initialized control bytes since
10561053
// we will never expose `RawTable::new_uninitialized` in a public API.
10571054
//
10581055
// 2. We reserve additional space (if necessary) right after calling this function.
1059-
let mut slot = self.table.find_insert_slot(hash);
1056+
let slot = self.table.find_insert_slot(hash);
10601057

10611058
// We can avoid growing the table once we have reached our load factor if we are replacing
10621059
// a tombstone. This works since the number of EMPTY slots does not change in this case.
@@ -1065,14 +1062,40 @@ impl<T, A: Allocator> RawTable<T, A> {
10651062
// in the range `0..=self.buckets()`.
10661063
let old_ctrl = *self.table.ctrl(slot.index);
10671064
if unlikely(self.table.growth_left == 0 && old_ctrl.special_is_empty()) {
1065+
None
1066+
} else {
1067+
Some(slot)
1068+
}
1069+
}
1070+
}
1071+
1072+
/// Inserts a new element into the table, and returns its raw bucket.
1073+
///
1074+
/// This does not check if the given element already exists in the table.
1075+
#[cfg_attr(feature = "inline-more", inline)]
1076+
pub fn insert(&mut self, hash: u64, value: T, hasher: impl Fn(&T) -> u64) -> Bucket<T> {
1077+
let slot = match self.find_insert_slot(hash) {
1078+
None => {
10681079
self.reserve(1, hasher);
10691080
// SAFETY: We know for sure that `RawTableInner` has control bytes
10701081
// initialized and that there is extra space in the table.
1071-
slot = self.table.find_insert_slot(hash);
1082+
unsafe { self.table.find_insert_slot(hash) }
10721083
}
1084+
Some(slot) => slot,
1085+
};
1086+
// SAFETY: todo
1087+
unsafe { self.insert_in_slot(hash, slot, value) }
1088+
}
10731089

1074-
self.insert_in_slot(hash, slot, value)
1075-
}
1090+
/// Inserts a new element into the table if there is capacity, and returns
1091+
/// its raw bucket.
1092+
///
1093+
/// This does not check if the given element already exists in the table.
1094+
#[inline]
1095+
pub(crate) fn insert_within_capacity(&mut self, hash: u64, value: T) -> Option<Bucket<T>> {
1096+
let slot = self.find_insert_slot(hash)?;
1097+
// SAFETY: todo
1098+
Some(unsafe { self.insert_in_slot(hash, slot, value) })
10761099
}
10771100

10781101
/// Inserts a new element into the table, and returns a mutable reference to it.

src/table.rs

+38
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,44 @@ where
415415
}
416416
}
417417

418+
/// Inserts an element into the `HashTable` with the given hash value, but
419+
/// without checking whether an equivalent element already exists within
420+
/// the table. If there is insufficient capacity, then this returns None.
421+
///
422+
/// # Examples
423+
///
424+
/// ```
425+
/// # #[cfg(feature = "nightly")]
426+
/// # fn test() {
427+
/// use hashbrown::{HashTable, DefaultHashBuilder};
428+
/// use std::hash::BuildHasher;
429+
///
430+
/// let mut v = HashTable::new();
431+
/// let hasher = DefaultHashBuilder::default();
432+
/// let hasher = |val: &_| hasher.hash_one(val);
433+
/// assert_eq!(None, v.insert_unique_within_capacity(hasher(&1), 1));
434+
/// v.reserve(1, hasher);
435+
/// v.insert_unique_within_capacity(hasher(&1), 1);
436+
/// assert!(!v.is_empty());
437+
/// # }
438+
/// # fn main() {
439+
/// # #[cfg(feature = "nightly")]
440+
/// # test()
441+
/// # }
442+
/// ```
443+
pub fn insert_unique_within_capacity(
444+
&mut self,
445+
hash: u64,
446+
value: T,
447+
) -> Option<OccupiedEntry<'_, T, A>> {
448+
let bucket = self.raw.insert_within_capacity(hash, value)?;
449+
Some(OccupiedEntry {
450+
hash,
451+
bucket,
452+
table: self,
453+
})
454+
}
455+
418456
/// Clears the table, removing all values.
419457
///
420458
/// # Examples

0 commit comments

Comments
 (0)