From 61b9fdd91a313e4fbb632b3538612132f96b804a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20M=C5=93ller?= Date: Mon, 25 Dec 2023 22:02:25 +0100 Subject: [PATCH] Improve usability of HashSet/Map type aliases --- README.md | 10 +++++++ benches/hashset.rs | 2 +- src/hasher.rs | 75 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a782d1e..3326dbe 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,16 @@ let mut hashset = gxhash::GxHashSet::default(); hashset.insert("hello world"); ``` +GxHash provides an implementation of the [`Hasher`](core::hash::Hasher) trait. +For convenience, this crate also provides the type aliases `gxhash::HashMap` and `gxhash::HashSet`. + +```rust +use gxhash::{HashMap, HashMapExt}; + +let mut map: HashMap<&str, i32> = HashMap::new(); +map.insert("answer", 42); +``` + ## Features ### Blazingly Fast 🚀 diff --git a/benches/hashset.rs b/benches/hashset.rs index cd252ac..a247a3a 100644 --- a/benches/hashset.rs +++ b/benches/hashset.rs @@ -27,7 +27,7 @@ fn benchmark(c: &mut Criterion, name: &str, value: T) iterate(b, &value, &mut set); }); - let mut set: HashSet:: = GxHashSet::::default(); + let mut set: HashSet:: = gxhash::HashSet::::default(); group.bench_function("GxHash", |b| { iterate(b, &value, &mut set); }); diff --git a/src/hasher.rs b/src/hasher.rs index 01341d1..403b016 100644 --- a/src/hasher.rs +++ b/src/hasher.rs @@ -1,4 +1,3 @@ -use std::collections::{HashMap, HashSet}; use std::hash::{BuildHasher, Hasher}; use std::mem::MaybeUninit; @@ -158,10 +157,55 @@ impl BuildHasher for GxBuildHasher { } /// A `HashMap` using a (DOS-resistant) [`GxBuildHasher`]. -pub type GxHashMap = HashMap; +pub type HashMap = std::collections::HashMap; + +/// A convenience trait that can be used together with the type aliases defined +/// to get access to the `new()` and `with_capacity()` methods for the +/// [`HashMap`] type alias. +pub trait HashMapExt { + /// Constructs a new HashMap. + fn new() -> Self; + /// Constructs a new HashMap with a given initial capacity. + fn with_capacity(capacity: usize) -> Self; +} + +impl HashMapExt for std::collections::HashMap +where + S: BuildHasher + Default, +{ + fn new() -> Self { + std::collections::HashMap::with_hasher(S::default()) + } + + fn with_capacity(capacity: usize) -> Self { + std::collections::HashMap::with_capacity_and_hasher(capacity, S::default()) + } +} /// A `HashSet` using a (DOS-resistant) [`GxBuildHasher`]. -pub type GxHashSet = HashSet; +pub type HashSet = std::collections::HashSet; + +/// A convenience trait that can be used together with the type aliases defined +/// to get access to the `new()` and `with_capacity()` methods for the +/// [`HashSet`] type alias. +pub trait HashSetExt { + /// Constructs a new HashMap. + fn new() -> Self; + /// Constructs a new HashMap with a given initial capacity. + fn with_capacity(capacity: usize) -> Self; +} + +impl HashSetExt for std::collections::HashSet + where S: BuildHasher + Default, +{ + fn new() -> Self { + std::collections::HashSet::with_hasher(S::default()) + } + + fn with_capacity(capacity: usize) -> Self { + std::collections::HashSet::with_capacity_and_hasher(capacity, S::default()) + } +} #[cfg(test)] mod tests { @@ -170,14 +214,31 @@ mod tests { use super::*; + #[test] + fn contructors_work() { + let mut map: std::collections::HashMap<&str, i32, GxBuildHasher> = HashMap::new(); + assert_eq!(true, map.insert("foo", 1).is_none()); + + let mut map = HashMap::with_capacity(3); + assert_eq!(3, map.capacity()); + assert_eq!(true, map.insert("bar", 2).is_none()); + + let mut set: std::collections::HashSet = HashSet::new(); + assert_eq!(true, set.insert(42)); + + let mut set = HashSet::with_capacity(3); + assert_eq!(true, set.insert(42)); + assert_eq!(3, set.capacity()); + } + #[test] fn hasher_produces_stable_hashes() { - let mut hashset = GxHashSet::default(); + let mut hashset = HashSet::default(); assert!(hashset.insert(1234)); assert!(!hashset.insert(1234)); assert!(hashset.insert(42)); - let mut hashset = GxHashSet::default(); + let mut hashset = HashSet::default(); assert!(hashset.insert("hello")); assert!(hashset.insert("world")); assert!(!hashset.insert("hello")); @@ -198,8 +259,8 @@ mod tests { // This is important for DOS resistance #[test] fn gxhashset_uses_default_gxhasherbuilder() { - let hashset_1 = GxHashSet::::default(); - let hashset_2 = GxHashSet::::default(); + let hashset_1 = HashSet::::default(); + let hashset_2 = HashSet::::default(); let mut hasher_1 = hashset_1.hasher().build_hasher(); let mut hasher_2 = hashset_2.hasher().build_hasher();