Skip to content

Commit 2132999

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 2d6826e commit 2132999

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", "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 FxHashMapRnd<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 FxHashSetRnd<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
@@ -61,6 +72,16 @@ pub struct FxHasher {
6172
hash: usize,
6273
}
6374

75+
/// `FxRandomState` is an alternative state for `HashMap` types.
76+
///
77+
/// A particular instance `FxRandomState` will create the same instances of
78+
/// [`Hasher`], but the hashers created by two different `FxRandomState`
79+
/// instances are unlikely to produce the same result for the same values.
80+
#[cfg(feature = "rand")]
81+
pub struct FxRandomState {
82+
seed: usize,
83+
}
84+
6485
#[cfg(target_pointer_width = "32")]
6586
const K: usize = 0x9e3779b9;
6687
#[cfg(target_pointer_width = "64")]
@@ -153,3 +174,38 @@ impl Hasher for FxHasher {
153174
self.hash as u64
154175
}
155176
}
177+
178+
#[cfg(feature = "rand")]
179+
impl FxRandomState {
180+
// Constructs a new `FxRandomState` that is initialized with random seed.
181+
pub fn new() -> FxRandomState {
182+
use rand::Rng;
183+
use std::{cell::Cell, thread_local};
184+
185+
// This mirrors what `std::collections::hash_map::RandomState` does.
186+
//
187+
// Basically
188+
// 1. Cache result of the rng in a thread local, so repeatedly
189+
// creating maps is cheaper
190+
// 2. Change the cached result on every creation, so maps created
191+
// on the same thread don't have the same iteration order
192+
thread_local!(static SEED: Cell<usize> = {
193+
Cell::new(rand::thread_rng().gen())
194+
});
195+
196+
SEED.with(|seed| {
197+
let s = seed.get();
198+
seed.set(s.wrapping_add(1));
199+
FxRandomState { seed: s }
200+
})
201+
}
202+
}
203+
204+
#[cfg(feature = "rand")]
205+
impl core::hash::BuildHasher for FxRandomState {
206+
type Hasher = FxHasher;
207+
208+
fn build_hasher(&self) -> Self::Hasher {
209+
FxHasher { hash: self.seed }
210+
}
211+
}

0 commit comments

Comments
 (0)