Skip to content

Commit 1deac0d

Browse files
committed
Add a way to seed FxHasher with random seeds
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`.
1 parent 8deba44 commit 1deac0d

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,9 @@ keywords = ["hash", "hasher", "fxhash", "rustc"]
99
repository = "https://github.com/rust-lang-nursery/rustc-hash"
1010

1111
[features]
12-
std = []
1312
default = ["std"]
13+
std = []
14+
rand = ["dep:rand", "std"]
15+
16+
[dependencies]
17+
rand = { version = "0.8", optional = true }

src/lib.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
#[cfg(feature = "std")]
2929
extern crate std;
3030

31+
#[cfg(feature = "rand")]
32+
extern crate rand;
33+
3134
use core::convert::TryInto;
3235
use core::default::Default;
3336
#[cfg(feature = "std")]
@@ -46,6 +49,14 @@ pub type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>;
4649
#[cfg(feature = "std")]
4750
pub type FxHashSet<V> = HashSet<V, BuildHasherDefault<FxHasher>>;
4851

52+
/// Type alias for a hashmap using the `fx` hash algorithm with [`FxRandomState`].
53+
#[cfg(feature = "rand")]
54+
pub type FxHashMapRand<K, V> = HashMap<K, V, FxRandomState>;
55+
56+
/// Type alias for a hashmap using the `fx` hash algorithm with [`FxRandomState`].
57+
#[cfg(feature = "rand")]
58+
pub type FxHashSetRand<V> = HashSet<V, FxRandomState>;
59+
4960
/// A speedy hash algorithm for use within rustc. The hashmap in liballoc
5061
/// by default uses SipHash which isn't quite as speedy as we want. In the
5162
/// compiler we're not really worried about DOS attempts, so we use a fast
@@ -62,6 +73,16 @@ pub struct FxHasher {
6273
hash: usize,
6374
}
6475

76+
/// `FxRandomState` is an alternative state for `HashMap` types.
77+
///
78+
/// A particular instance `FxRandomState` will create the same instances of
79+
/// [`Hasher`], but the hashers created by two different `FxRandomState`
80+
/// instances are unlikely to produce the same result for the same values.
81+
#[cfg(feature = "rand")]
82+
pub struct FxRandomState {
83+
seed: usize,
84+
}
85+
6586
#[cfg(target_pointer_width = "32")]
6687
const K: usize = 0x9e3779b9;
6788
#[cfg(target_pointer_width = "64")]
@@ -155,6 +176,41 @@ impl Hasher for FxHasher {
155176
}
156177
}
157178

179+
#[cfg(feature = "rand")]
180+
impl FxRandomState {
181+
/// Constructs a new `FxRandomState` that is initialized with random seed.
182+
pub fn new() -> FxRandomState {
183+
use rand::Rng;
184+
use std::{cell::Cell, thread_local};
185+
186+
// This mirrors what `std::collections::hash_map::RandomState` does, as of 2024-01-14.
187+
//
188+
// Basically
189+
// 1. Cache result of the rng in a thread local, so repeatedly
190+
// creating maps is cheaper
191+
// 2. Change the cached result on every creation, so maps created
192+
// on the same thread don't have the same iteration order
193+
thread_local!(static SEED: Cell<usize> = {
194+
Cell::new(rand::thread_rng().gen())
195+
});
196+
197+
SEED.with(|seed| {
198+
let s = seed.get();
199+
seed.set(s.wrapping_add(1));
200+
FxRandomState { seed: s }
201+
})
202+
}
203+
}
204+
205+
#[cfg(feature = "rand")]
206+
impl core::hash::BuildHasher for FxRandomState {
207+
type Hasher = FxHasher;
208+
209+
fn build_hasher(&self) -> Self::Hasher {
210+
FxHasher { hash: self.seed }
211+
}
212+
}
213+
158214
#[cfg(test)]
159215
mod tests {
160216
#[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]

0 commit comments

Comments
 (0)