Skip to content

Commit

Permalink
Add type-alias feature to make it more of a drop in replacement for s…
Browse files Browse the repository at this point in the history
…td Hash[Set|Map] (tkaitchuck#105)

* Add type-alias feature
* Add extension traits
* Use RandomState as hasher instead

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
  • Loading branch information
Nehliin and emilk committed Feb 26, 2022
1 parent c069bdc commit 5e55048
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 7 deletions.
107 changes: 100 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,24 @@
//! let mut map: HashMap<i32, i32, RandomState> = HashMap::default();
//! map.insert(12, 34);
//! ```
//! For convinence wrappers called `AHashMap` and `AHashSet` are also provided.
//! These to the same thing with slightly less typing.
//! For convenience, both new-type wrappers and type aliases are provided. The new type wrappers are called called `AHashMap` and `AHashSet`. These do the same thing with slightly less typing.
//! The type aliases are called `ahash::HashMap`, `ahash::HashSet` are also provided and alias the
//! std::[HashMap] and std::[HashSet]. Why are there two options? The wrappers are convenient but
//! can't be used where a generic `std::collection::HashMap<K, V, S>` is required.
//!
//! ```ignore
//! use ahash::AHashMap;
//!
//! let mut map: AHashMap<i32, i32> = AHashMap::with_capacity(4);
//! map.insert(12, 34);
//! map.insert(56, 78);
//! // There are also type aliases provieded together with some extension traits to make
//! // it more of a drop in replacement for the std::HashMap/HashSet
//! use ahash::{HashMapExt, HashSetExt}; // Used to get with_capacity()
//! let mut map = ahash::HashMap::with_capacity(10);
//! map.insert(12, 34);
//! let mut set = ahash::HashSet::with_capacity(10);
//! set.insert(10);
//! ```
#![deny(clippy::correctness, clippy::complexity, clippy::perf)]
#![allow(clippy::pedantic, clippy::cast_lossless, clippy::unreadable_literal)]
Expand All @@ -37,13 +47,27 @@ mod convert;

#[cfg(any(
all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd")
all(
any(target_arch = "arm", target_arch = "aarch64"),
target_feature = "crypto",
not(miri),
feature = "stdsimd"
)
))]
mod aes_hash;
mod fallback_hash;
#[cfg(test)]
mod hash_quality_test;

#[cfg(feature = "std")]
/// [Hasher]: std::hash::Hasher
/// [HashMap]: std::collections::HashMap
/// Type alias for [HashMap]<K, V, ahash::RandomState>
pub type HashMap<K, V> = std::collections::HashMap<K, V, crate::RandomState>;
#[cfg(feature = "std")]
/// Type alias for [HashSet]<K, ahash::RandomState>
pub type HashSet<K> = std::collections::HashSet<K, crate::RandomState>;

#[cfg(feature = "std")]
mod hash_map;
#[cfg(feature = "std")]
Expand All @@ -54,13 +78,23 @@ mod specialize;

#[cfg(any(
all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd")
all(
any(target_arch = "arm", target_arch = "aarch64"),
target_feature = "crypto",
not(miri),
feature = "stdsimd"
)
))]
pub use crate::aes_hash::AHasher;

#[cfg(not(any(
all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd")
all(
any(target_arch = "arm", target_arch = "aarch64"),
target_feature = "crypto",
not(miri),
feature = "stdsimd"
)
)))]
pub use crate::fallback_hash::AHasher;
pub use crate::random_state::RandomState;
Expand All @@ -75,6 +109,54 @@ use core::hash::BuildHasher;
use core::hash::Hash;
use core::hash::Hasher;

#[cfg(feature = "std")]
/// 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;
}

#[cfg(feature = "std")]
/// 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 aliases.
pub trait HashSetExt {
/// Constructs a new HashSet
fn new() -> Self;
/// Constructs a new HashSet with a given initial capacity
fn with_capacity(capacity: usize) -> Self;
}

#[cfg(feature = "std")]
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())
}
}

#[cfg(feature = "std")]
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())
}
}

/// Provides a default [Hasher] with fixed keys.
/// This is typically used in conjunction with [BuildHasherDefault] to create
/// [AHasher]s in order to hash the keys of the map.
Expand Down Expand Up @@ -198,6 +280,18 @@ mod test {
use std::collections::HashMap;
use std::hash::Hash;

#[test]
fn test_ahash_alias_map_construction() {
let mut map = super::HashMap::with_capacity(1234);
map.insert(1, "test");
}

#[test]
fn test_ahash_alias_set_construction() {
let mut set = super::HashSet::with_capacity(1234);
set.insert(1);
}

#[test]
fn test_default_builder() {
use core::hash::BuildHasherDefault;
Expand All @@ -219,7 +313,6 @@ mod test {
assert_eq!(bytes, 0x6464646464646464);
}


#[test]
fn test_non_zero() {
let mut hasher1 = AHasher::new_with_keys(0, 0);
Expand All @@ -241,7 +334,7 @@ mod test {

#[test]
fn test_non_zero_specialized() {
let hasher_build = RandomState::with_seeds(0,0,0,0);
let hasher_build = RandomState::with_seeds(0, 0, 0, 0);

let h1 = str::get_hash("foo", &hasher_build);
let h2 = str::get_hash("bar", &hasher_build);
Expand Down
21 changes: 21 additions & 0 deletions tests/map_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,27 @@ fn test_bucket_distribution() {
check_for_collisions(&build_hasher, &sequence, 256);
}

#[cfg(feature = "std")]
#[test]
fn test_ahash_alias_map_construction() {
let mut map = ahash::HashMap::default();
map.insert(1, "test");
use ahash::HashMapExt;
let mut map = ahash::HashMap::with_capacity(1234);
map.insert(1, "test");
}

#[cfg(feature = "std")]
#[test]
fn test_ahash_alias_set_construction() {
let mut set = ahash::HashSet::default();
set.insert(1);

use ahash::HashSetExt;
let mut set = ahash::HashSet::with_capacity(1235);
set.insert(1);
}

fn ahash_vec<H: Hash>(b: &Vec<H>) -> u64 {
let mut total: u64 = 0;
for item in b {
Expand Down

0 comments on commit 5e55048

Please sign in to comment.