Skip to content

Commit

Permalink
Improve usability of HashSet/Map type aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
virtualritz authored and ogxd committed Dec 25, 2023
1 parent df7660e commit 61b9fdd
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 8 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 🚀
Expand Down
2 changes: 1 addition & 1 deletion benches/hashset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn benchmark<T>(c: &mut Criterion, name: &str, value: T)
iterate(b, &value, &mut set);
});

let mut set: HashSet::<T, GxBuildHasher> = GxHashSet::<T>::default();
let mut set: HashSet::<T, GxBuildHasher> = gxhash::HashSet::<T>::default();
group.bench_function("GxHash", |b| {
iterate(b, &value, &mut set);
});
Expand Down
75 changes: 68 additions & 7 deletions src/hasher.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::collections::{HashMap, HashSet};
use std::hash::{BuildHasher, Hasher};
use std::mem::MaybeUninit;

Expand Down Expand Up @@ -158,10 +157,55 @@ impl BuildHasher for GxBuildHasher {
}

/// A `HashMap` using a (DOS-resistant) [`GxBuildHasher`].
pub type GxHashMap<K, V> = HashMap<K, V, GxBuildHasher>;
pub type HashMap<K, V> = std::collections::HashMap<K, V, GxBuildHasher>;

/// 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<K, V, S> HashMapExt for std::collections::HashMap<K, V, S>
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<T> = HashSet<T, GxBuildHasher>;
pub type HashSet<T> = std::collections::HashSet<T, GxBuildHasher>;

/// 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<K, S> HashSetExt for std::collections::HashSet<K, S>
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 {
Expand All @@ -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<i32, GxBuildHasher> = 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"));
Expand All @@ -198,8 +259,8 @@ mod tests {
// This is important for DOS resistance
#[test]
fn gxhashset_uses_default_gxhasherbuilder() {
let hashset_1 = GxHashSet::<u32>::default();
let hashset_2 = GxHashSet::<u32>::default();
let hashset_1 = HashSet::<u32>::default();
let hashset_2 = HashSet::<u32>::default();

let mut hasher_1 = hashset_1.hasher().build_hasher();
let mut hasher_2 = hashset_2.hasher().build_hasher();
Expand Down

0 comments on commit 61b9fdd

Please sign in to comment.