Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

insert_unique_unchecked operation #293

Merged
merged 2 commits into from
Sep 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions benches/insert_unique_unchecked.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//! Compare `insert` and `insert_unique_unchecked` operations performance.
#![feature(test)]

extern crate test;

use hashbrown::HashMap;
use test::Bencher;

#[bench]
fn insert(b: &mut Bencher) {
let keys: Vec<String> = (0..1000).map(|i| format!("xxxx{}yyyy", i)).collect();
b.iter(|| {
let mut m = HashMap::with_capacity(1000);
for k in &keys {
m.insert(k, k);
}
m
});
}

#[bench]
fn insert_unique_unchecked(b: &mut Bencher) {
let keys: Vec<String> = (0..1000).map(|i| format!("xxxx{}yyyy", i)).collect();
b.iter(|| {
let mut m = HashMap::with_capacity(1000);
for k in &keys {
m.insert_unique_unchecked(k, k);
}
m
});
}
42 changes: 42 additions & 0 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,36 @@ where
}
}

/// Insert a key-value pair into the map without checking
/// if the key already exists in the map.
///
/// Returns a reference to the key and value just inserted.
///
/// This operation is safe if a key does not exist in the map.
///
/// However, if a key exists in the map already, the behavior is unspecified:
/// this operation may panic, loop forever, or any following operation with the map
/// may panic, loop forever or return arbitrary result.
///
/// That said, this operation (and following operations) are guaranteed to
/// not violate memory safety.
///
/// This operation is faster than regular insert, because it does not perform
/// lookup before insertion.
///
/// This operation is useful during initial population of the map.
/// For example, when constructing a map from another map, we know
/// that keys are unique.
#[cfg_attr(feature = "inline-more", inline)]
pub fn insert_unique_unchecked(&mut self, k: K, v: V) -> (&K, &mut V) {
let hash = make_insert_hash::<K, S>(&self.hash_builder, &k);
let bucket = self
.table
.insert(hash, (k, v), make_hasher::<K, _, V, S>(&self.hash_builder));
let (k_ref, v_ref) = unsafe { bucket.as_mut() };
(k_ref, v_ref)
}

/// Tries to insert a key-value pair into the map, and returns
/// a mutable reference to the value in the entry.
///
Expand Down Expand Up @@ -3898,6 +3928,18 @@ mod test_map {
assert_eq!(*m.get(&5).unwrap(), 3);
}

#[test]
fn test_insert_unique_unchecked() {
let mut map = HashMap::new();
let (k1, v1) = map.insert_unique_unchecked(10, 11);
assert_eq!((&10, &mut 11), (k1, v1));
let (k2, v2) = map.insert_unique_unchecked(20, 21);
assert_eq!((&20, &mut 21), (k2, v2));
assert_eq!(Some(&11), map.get(&10));
assert_eq!(Some(&21), map.get(&20));
assert_eq!(None, map.get(&30));
}

#[test]
fn test_is_empty() {
let mut m = HashMap::with_capacity(4);
Expand Down
24 changes: 24 additions & 0 deletions src/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,30 @@ where
self.map.insert(value, ()).is_none()
}

/// Insert a value the set without checking if the value already exists in the set.
///
/// Returns a reference to the value just inserted.
///
/// This operation is safe if a value does not exist in the set.
///
/// However, if a value exists in the set already, the behavior is unspecified:
/// this operation may panic, loop forever, or any following operation with the set
/// may panic, loop forever or return arbitrary result.
///
/// That said, this operation (and following operations) are guaranteed to
/// not violate memory safety.
///
/// This operation is faster than regular insert, because it does not perform
/// lookup before insertion.
///
/// This operation is useful during initial population of the set.
/// For example, when constructing a set from another set, we know
/// that values are unique.
#[cfg_attr(feature = "inline-more", inline)]
pub fn insert_unique_unchecked(&mut self, value: T) -> &T {
self.map.insert_unique_unchecked(value, ()).0
}

/// Adds a value to the set, replacing the existing value, if any, that is equal to the given
/// one. Returns the replaced value.
///
Expand Down