Skip to content

Commit

Permalink
Add a way to seed FxHasher with random seeds
Browse files Browse the repository at this point in the history
This commit adds an optional dependency on `rand` (behind a `rand`)
feature that allows seeding `FxHasher` with random seeds.

This is done via `FxRandomState` that implemented similar to
`std::collections::hash_map::RandomState`.

`FxHashMapRnd` and `FxHashSetRnd` are also introduced as type aliases
to `HashMap` and `HashSet` with `S = FxRandomState`.
  • Loading branch information
WaffleLapkin committed Jan 14, 2024
1 parent 2d6826e commit f990ea2
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 1 deletion.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,9 @@ keywords = ["hash", "fxhash", "rustc"]
repository = "https://github.com/rust-lang-nursery/rustc-hash"

[features]
std = []
default = ["std"]
std = []
rand = ["dep:rand", "std"]

[dependencies]
rand = { version = "0.8", optional = true }
56 changes: 56 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
#[cfg(feature = "std")]
extern crate std;

#[cfg(feature = "rand")]
extern crate rand;

use core::convert::TryInto;
use core::default::Default;
#[cfg(feature = "std")]
Expand All @@ -46,6 +49,14 @@ pub type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>;
#[cfg(feature = "std")]
pub type FxHashSet<V> = HashSet<V, BuildHasherDefault<FxHasher>>;

/// Type alias for a hashmap using the `fx` hash algorithm with [`FxRandomState`].
#[cfg(feature = "rand")]
pub type FxHashMapRand<K, V> = HashMap<K, V, FxRandomState>;

/// Type alias for a hashmap using the `fx` hash algorithm with [`FxRandomState`].
#[cfg(feature = "rand")]
pub type FxHashSetRand<V> = HashSet<V, FxRandomState>;

/// A speedy hash algorithm for use within rustc. The hashmap in liballoc
/// by default uses SipHash which isn't quite as speedy as we want. In the
/// compiler we're not really worried about DOS attempts, so we use a fast
Expand All @@ -61,6 +72,16 @@ pub struct FxHasher {
hash: usize,
}

/// `FxRandomState` is an alternative state for `HashMap` types.
///
/// A particular instance `FxRandomState` will create the same instances of
/// [`Hasher`], but the hashers created by two different `FxRandomState`
/// instances are unlikely to produce the same result for the same values.
#[cfg(feature = "rand")]
pub struct FxRandomState {
seed: usize,
}

#[cfg(target_pointer_width = "32")]
const K: usize = 0x9e3779b9;
#[cfg(target_pointer_width = "64")]
Expand Down Expand Up @@ -153,3 +174,38 @@ impl Hasher for FxHasher {
self.hash as u64
}
}

#[cfg(feature = "rand")]
impl FxRandomState {
/// Constructs a new `FxRandomState` that is initialized with random seed.
pub fn new() -> FxRandomState {
use rand::Rng;
use std::{cell::Cell, thread_local};

// This mirrors what `std::collections::hash_map::RandomState` does, as of 2024-01-14.
//
// Basically
// 1. Cache result of the rng in a thread local, so repeatedly
// creating maps is cheaper
// 2. Change the cached result on every creation, so maps created
// on the same thread don't have the same iteration order
thread_local!(static SEED: Cell<usize> = {
Cell::new(rand::thread_rng().gen())
});

SEED.with(|seed| {
let s = seed.get();
seed.set(s.wrapping_add(1));
FxRandomState { seed: s }
})
}
}

#[cfg(feature = "rand")]
impl core::hash::BuildHasher for FxRandomState {
type Hasher = FxHasher;

fn build_hasher(&self) -> Self::Hasher {
FxHasher { hash: self.seed }
}
}

0 comments on commit f990ea2

Please sign in to comment.