Skip to content
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
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@ exclude = ["fuzz"]
rust-version = "1.71"

[features]
default = ["ahash", "parking_lot"]
default = ["custom-hasher", "parking_lot"]
# Use the crate's bundled fast default hasher (currently foldhash) behind the
# opaque `DefaultHashBuilder`. The concrete hasher is an implementation detail.
custom-hasher = ["dep:foldhash"]
sharded-lock = ["dep:crossbeam-utils"]
shuttle = ["dep:shuttle"]
stats = []

[dependencies]
ahash = { optional = true, version = "0.8" }
crossbeam-utils = { optional = true, version = "0.8" }
equivalent = "1.0"
foldhash = { optional = true, version = "0.2" }
hashbrown = { version = "0.16", default-features = false, features = ["inline-more"] }
parking_lot = { optional = true, version = "0.12" }
shuttle = { version = "0.8", optional = true }
Expand Down
43 changes: 35 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@
//!
//! # Hasher
//!
//! By default the crate uses [ahash](https://crates.io/crates/ahash), which is enabled (by default) via
//! a crate feature with the same name. If the `ahash` feature is disabled the crate defaults to the std lib
//! implementation instead (currently Siphash13). Note that a custom hasher can also be provided if desirable.
//! By default the crate uses a fast non-cryptographic hasher (currently [foldhash](https://crates.io/crates/foldhash))
//! exposed through the opaque [`DefaultHashBuilder`] type and enabled via the `custom-hasher` feature (on by
//! default). The concrete algorithm is an implementation detail and may change between releases. If the
//! `custom-hasher` feature is disabled the crate falls back to the std lib implementation instead (currently
//! Siphash13). Note that a custom hasher can also be provided via the cache's `B` type parameter if desirable.
//!
//! # Synchronization primitives
//!
Expand All @@ -75,7 +77,7 @@
//!
//! | Feature | Default | Description |
//! |---------|---------|-------------|
//! | `ahash` | ✓ | Use [ahash](https://crates.io/crates/ahash) as the default hasher. When disabled, falls back to std lib's `RandomState` (currently SipHash-1-3). |
//! | `custom-hasher` | ✓ | Use the crate's bundled fast non-cryptographic default hasher (currently [foldhash](https://crates.io/crates/foldhash)) behind the opaque [`DefaultHashBuilder`]. When disabled, falls back to std lib's `RandomState` (currently SipHash-1-3). |
//! | `parking_lot` | ✓ | Use [parking_lot](https://crates.io/crates/parking_lot) for synchronization primitives. Mutually exclusive with `sharded-lock`. |
//! | `sharded-lock` | | Use [`crossbeam_utils::sync::ShardedLock`](https://docs.rs/crossbeam-utils/latest/crossbeam_utils/sync/struct.ShardedLock.html) for synchronization primitives. Mutually exclusive with `parking_lot`. |
//! | `shuttle` | | Enable [shuttle](https://crates.io/crates/shuttle) testing support for concurrency testing. |
Expand Down Expand Up @@ -107,10 +109,35 @@ mod shuttle_tests;

pub use options::{Options, OptionsBuilder};

#[cfg(feature = "ahash")]
pub type DefaultHashBuilder = ahash::RandomState;
#[cfg(not(feature = "ahash"))]
pub type DefaultHashBuilder = std::collections::hash_map::RandomState;
#[cfg(feature = "custom-hasher")]
type DefaultHasherImpl = foldhash::quality::RandomState;
#[cfg(not(feature = "custom-hasher"))]
type DefaultHasherImpl = std::collections::hash_map::RandomState;

/// The default [`BuildHasher`](std::hash::BuildHasher) used by the cache.
///
/// The hashing algorithm behind this type is an implementation detail and may
/// change in any release. Name this type — don't rely on the concrete hasher
/// it wraps. The underlying hasher is selected by the `custom-hasher` feature
/// (a fast non-cryptographic hasher when enabled, the std library's
/// `RandomState` otherwise).
#[derive(Clone, Default)]
pub struct DefaultHashBuilder(DefaultHasherImpl);

impl std::hash::BuildHasher for DefaultHashBuilder {
type Hasher = <DefaultHasherImpl as std::hash::BuildHasher>::Hasher;

#[inline]
fn build_hasher(&self) -> Self::Hasher {
self.0.build_hasher()
}
}

impl std::fmt::Debug for DefaultHashBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DefaultHashBuilder").finish_non_exhaustive()
}
}

/// Defines the weight of a cache entry.
///
Expand Down
6 changes: 6 additions & 0 deletions src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,12 @@ impl<
// shard, rotate the hash by usize::BITS / 2 so we avoid the lower bits and
// the highest 7 bits that hashbrown uses internally for probing, improving
// the real entropy available to each hashbrown shard.
//
// The rotation is deliberately tied to usize::BITS, not u64::BITS: on 32-bit
// targets a usize-width hasher (e.g. FxHash) leaves the top 32 hash bits zero
// (hashbrown guards against this too; see its h1 / Tag::full), so we keep the
// shard window in the low word where the entropy actually lives. Rotating by
// u64::BITS / 2 there would send every key to shard 0.
hash.rotate_right(usize::BITS / 2) & self.shards_mask
}

Expand Down
Loading