Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ edition = "2018"

[dependencies]
# For the default hasher
ahash = { version = "0.4.4", optional = true, default-features = false }
ahash = { version = "0.5.1", default-features = false, optional = true }

# For external trait impls
rayon = { version = "1.0", optional = true }
Expand All @@ -25,7 +25,7 @@ compiler_builtins = { version = "0.1.2", optional = true }
alloc = { version = "1.0.0", optional = true, package = "rustc-std-workspace-alloc" }

[dev-dependencies]
lazy_static = "1.2"
lazy_static = "1.4"
rand = { version = "0.7.3", features = ["small_rng"] }
rayon = "1.0"
rustc-hash = "=1.0"
Expand All @@ -34,9 +34,9 @@ doc-comment = "0.3.1"

[features]
default = ["ahash", "inline-more"]

std = ["ahash/std", "serde/std"]
ahash-compile-time-rng = ["ahash/compile-time-rng"]
nightly = []
nightly = ["ahash/specialize"]
rustc-internal-api = []
rustc-dep-of-std = [
"nightly",
Expand Down
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ in environments without `std`, such as embedded systems and kernels.
## Features

- Drop-in replacement for the standard library `HashMap` and `HashSet` types.
- Uses `AHash` as the default hasher, which is much faster than SipHash.
- Uses [AHash](https://github.com/tkaitchuck/aHash) as the default hasher, which is much faster than SipHash.
- Around 2x faster than the previous standard library `HashMap`.
- Lower memory usage: only 1 byte of overhead per entry instead of 8.
- Compatible with `#[no_std]` (but requires a global allocator with the `alloc` crate).
Expand All @@ -37,7 +37,7 @@ in environments without `std`, such as embedded systems and kernels.

Compared to the previous implementation of `std::collections::HashMap` (Rust 1.35).

With the hashbrown default AHash hasher (not HashDoS-resistant):
With the hashbrown default AHash hasher ([without HashDoS-resistance](#Flags)):

```text
name oldstdhash ns/iter hashbrown ns/iter diff ns/iter diff % speedup
Expand Down Expand Up @@ -96,19 +96,22 @@ use hashbrown::HashMap;
let mut map = HashMap::new();
map.insert(1, "one");
```

## Flags
This crate has the following Cargo features:

- `inline-more`: Adds inline hints to most functions, improving run-time performance at the cost
of compilation time. (enabled by default)
- `ahash`: Compiles with ahash as default hasher. (enabled by default)
- `std`: Enables use of features that depend on the standard library.
If `ahash` is used this includes using random keys to provide DOS resistance. (disabled by default)
- `ahash-compile-time-rng`: This is an alternative to `std` which still allows for some degree of DOS-resistance.
However, it can result in issues for certain platforms.
See details in [issue#124](https://github.com/rust-lang/hashbrown/issues/124). (disabled by default)
- `nightly`: Enables nightly-only features: `#[may_dangle]`.
- `serde`: Enables serde serialization support.
- `rayon`: Enables rayon parallel iterator support.
- `raw`: Enables access to the experimental and unsafe `RawTable` API.
- `inline-more`: Adds inline hints to most functions, improving run-time performance at the cost
of compilation time. (enabled by default)
- `ahash`: Compiles with ahash as default hasher. (enabled by default)
- `ahash-compile-time-rng`: Activates the `compile-time-rng` feature of ahash, to increase the
DOS-resistance, but can result in issues for `no_std` builds. More details in
[issue#124](https://github.com/rust-lang/hashbrown/issues/124). (enabled by default)


## License

Expand Down
57 changes: 35 additions & 22 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@ use crate::raw::{Bucket, RawDrain, RawIntoIter, RawIter, RawTable};
use crate::TryReserveError;
use core::borrow::Borrow;
use core::fmt::{self, Debug};
use core::hash::{BuildHasher, Hash, Hasher};
use core::hash::{BuildHasher, Hash};
use core::iter::{FromIterator, FusedIterator};
use core::marker::PhantomData;
use core::mem;
use core::ops::Index;

/// Default hasher for `HashMap`.
#[cfg(feature = "ahash")]
#[cfg(all(
feature = "ahash",
any(feature = "std", feature = "ahash-compile-time-rng")
))]
pub type DefaultHashBuilder = ahash::RandomState;

/// Default hasher for `HashMap`.
#[cfg(all(
feature = "ahash",
not(any(feature = "std", feature = "ahash-compile-time-rng"))
))]
pub type DefaultHashBuilder = core::hash::BuildHasherDefault<ahash::AHasher>;

/// Dummy default hasher for `HashMap`.
#[cfg(not(feature = "ahash"))]
pub enum DefaultHashBuilder {}
Expand Down Expand Up @@ -239,9 +249,19 @@ where

#[cfg_attr(feature = "inline-more", inline)]
pub(crate) fn make_hash<K: Hash + ?Sized>(hash_builder: &impl BuildHasher, val: &K) -> u64 {
let mut state = hash_builder.build_hasher();
val.hash(&mut state);
state.finish()
#[cfg(feature = "ahash")]
{
use ahash::CallHasher;
let state = hash_builder.build_hasher();
val.get_hash(state)
}
#[cfg(not(feature = "ahash"))]
{
use core::hash::Hasher;
let mut state = hash_builder.build_hasher();
val.hash(&mut state);
state.finish()
}
}

#[cfg(feature = "ahash")]
Expand Down Expand Up @@ -1541,9 +1561,8 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> {
K: Borrow<Q>,
Q: Hash + Eq,
{
let mut hasher = self.map.hash_builder.build_hasher();
k.hash(&mut hasher);
self.from_key_hashed_nocheck(hasher.finish(), k)
let hash = make_hash(&self.map.hash_builder, k);
self.from_key_hashed_nocheck(hash, k)
}

/// Creates a `RawEntryMut` from the given key and its hash.
Expand Down Expand Up @@ -1598,9 +1617,8 @@ impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> {
K: Borrow<Q>,
Q: Hash + Eq,
{
let mut hasher = self.map.hash_builder.build_hasher();
k.hash(&mut hasher);
self.from_key_hashed_nocheck(hasher.finish(), k)
let hash = make_hash(&self.map.hash_builder, k);
self.from_key_hashed_nocheck(hash, k)
}

/// Access an entry by a key and its hash.
Expand Down Expand Up @@ -1957,9 +1975,8 @@ impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> {
K: Hash,
S: BuildHasher,
{
let mut hasher = self.hash_builder.build_hasher();
key.hash(&mut hasher);
self.insert_hashed_nocheck(hasher.finish(), key, value)
let hash = make_hash(self.hash_builder, &key);
self.insert_hashed_nocheck(hash, key, value)
}

/// Sets the value of the entry with the VacantEntry's key,
Expand Down Expand Up @@ -2001,14 +2018,10 @@ impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> {
K: Hash,
S: BuildHasher,
{
let mut hasher = self.hash_builder.build_hasher();
key.hash(&mut hasher);

let elem = self.table.insert(
hasher.finish(),
(key, value),
make_hasher(self.hash_builder),
);
let hash = make_hash(self.hash_builder, &key);
let elem = self
.table
.insert(hash, (key, value), make_hasher(self.hash_builder));
RawOccupiedEntryMut {
elem,
table: self.table,
Expand Down