• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
2 use crate::convert::Convert;
3 #[cfg(feature = "specialize")]
4 use crate::BuildHasherExt;
5 
6 #[cfg(any(
7     all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
8     all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd")
9 ))]
10 pub use crate::aes_hash::*;
11 
12 #[cfg(not(any(
13     all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
14     all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd")
15 )))]
16 pub use crate::fallback_hash::*;
17 
18 #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))]
19 use const_random::const_random;
20 use core::any::{Any, TypeId};
21 use core::fmt;
22 use core::hash::BuildHasher;
23 #[cfg(feature = "specialize")]
24 use core::hash::Hash;
25 use core::hash::Hasher;
26 
27 #[cfg(not(feature = "std"))]
28 extern crate alloc;
29 #[cfg(feature = "std")]
30 extern crate std as alloc;
31 
32 use alloc::boxed::Box;
33 use core::sync::atomic::{AtomicUsize, Ordering};
34 #[cfg(not(all(target_arch = "arm", target_os = "none")))]
35 use once_cell::race::OnceBox;
36 
37 #[cfg(any(
38     all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
39     all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd")
40 ))]
41 use crate::aes_hash::*;
42 #[cfg(not(any(
43     all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
44     all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd")
45 )))]
46 use crate::fallback_hash::*;
47 
48 #[cfg(not(all(target_arch = "arm", target_os = "none")))]
49 static RAND_SOURCE: OnceBox<Box<dyn RandomSource + Send + Sync>> = OnceBox::new();
50 
51 #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
read_urandom(dest: &mut [u8]) -> Result<(), std::io::Error>52 fn read_urandom(dest: &mut [u8]) -> Result<(), std::io::Error> {
53     use std::fs::File;
54     use std::io::Read;
55 
56     let mut f = File::open("/dev/urandom")?;
57     f.read_exact(dest)
58 }
59 
60 /// A supplier of Randomness used for different hashers.
61 /// See [RandomState.set_random_source].
62 pub trait RandomSource {
63 
get_fixed_seeds(&self) -> &'static [[u64; 4]; 2]64     fn get_fixed_seeds(&self) -> &'static [[u64; 4]; 2];
65 
gen_hasher_seed(&self) -> usize66     fn gen_hasher_seed(&self) -> usize;
67 
68 }
69 
70 pub(crate) const PI: [u64; 4] = [
71     0x243f_6a88_85a3_08d3,
72     0x1319_8a2e_0370_7344,
73     0xa409_3822_299f_31d0,
74     0x082e_fa98_ec4e_6c89,
75 ];
76 
77 pub(crate) const PI2: [u64; 4] = [
78     0x4528_21e6_38d0_1377,
79     0xbe54_66cf_34e9_0c6c,
80     0xc0ac_29b7_c97c_50dd,
81     0x3f84_d5b5_b547_0917,
82 ];
83 
84 struct DefaultRandomSource {
85     counter: AtomicUsize,
86 }
87 
88 impl DefaultRandomSource {
new() -> DefaultRandomSource89     fn new() -> DefaultRandomSource {
90         DefaultRandomSource {
91             counter: AtomicUsize::new(&PI as *const _ as usize),
92         }
93     }
94 
default() -> DefaultRandomSource95     const fn default() -> DefaultRandomSource {
96         DefaultRandomSource {
97             counter: AtomicUsize::new(PI[3] as usize),
98         }
99     }
100 }
101 
102 impl RandomSource for DefaultRandomSource {
103 
104     #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
get_fixed_seeds(&self) -> &'static [[u64; 4]; 2]105     fn get_fixed_seeds(&self) -> &'static [[u64; 4]; 2] {
106         static SEEDS: OnceBox<[[u64; 4]; 2]> = OnceBox::new();
107 
108         SEEDS.get_or_init(|| {
109             let mut result: [u8; 64] = [0; 64];
110             if read_urandom(&mut result).is_err() {
111                 getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed.");
112             }
113             Box::new(result.convert())
114         })
115     }
116 
117     #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))]
get_fixed_seeds(&self) -> &'static [[u64; 4]; 2]118     fn get_fixed_seeds(&self) -> &'static [[u64; 4]; 2] {
119         const RAND: [[u64; 4]; 2] = [
120             [
121                 const_random!(u64),
122                 const_random!(u64),
123                 const_random!(u64),
124                 const_random!(u64),
125             ], [
126                 const_random!(u64),
127                 const_random!(u64),
128                 const_random!(u64),
129                 const_random!(u64),
130             ]
131         ];
132         &RAND
133     }
134 
135     #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))]
get_fixed_seeds(&self) -> &'static [[u64; 4]; 2]136     fn get_fixed_seeds(&self) -> &'static [[u64; 4]; 2] {
137         &[PI, PI2]
138     }
139 
140     #[cfg(not(all(target_arch = "arm", target_os = "none")))]
gen_hasher_seed(&self) -> usize141     fn gen_hasher_seed(&self) -> usize {
142         let stack = self as *const _ as usize;
143         self.counter.fetch_add(stack, Ordering::Relaxed)
144     }
145 
146     #[cfg(all(target_arch = "arm", target_os = "none"))]
gen_hasher_seed(&self) -> usize147     fn gen_hasher_seed(&self) -> usize {
148         let stack = self as *const _ as usize;
149         let previous = self.counter.load(Ordering::Relaxed);
150         let new = previous.wrapping_add(stack);
151         self.counter.store(new, Ordering::Relaxed);
152         new
153     }
154 }
155 
156 /// Provides a [Hasher] factory. This is typically used (e.g. by [HashMap]) to create
157 /// [AHasher]s in order to hash the keys of the map. See `build_hasher` below.
158 ///
159 /// [build_hasher]: ahash::
160 /// [Hasher]: std::hash::Hasher
161 /// [BuildHasher]: std::hash::BuildHasher
162 /// [HashMap]: std::collections::HashMap
163 #[derive(Clone)]
164 pub struct RandomState {
165     pub(crate) k0: u64,
166     pub(crate) k1: u64,
167     pub(crate) k2: u64,
168     pub(crate) k3: u64,
169 }
170 
171 impl fmt::Debug for RandomState {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result172     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173         f.pad("RandomState { .. }")
174     }
175 }
176 
177 impl RandomState {
178 
179     /// Provides an optional way to manually supply a source of randomness for Hasher keys.
180     ///
181     /// The provided [RandomSource] will be used to be used as a source of randomness by [RandomState] to generate new states.
182     /// If this method is not invoked the standard source of randomness is used as described in the Readme.
183     ///
184     /// The source of randomness can only be set once, and must be set before the first RandomState is created.
185     /// If the source has already been specified `Err` is returned with a `bool` indicating if the set failed because
186     /// method was previously invoked (true) or if the default source is already being used (false).
187     #[cfg(not(all(target_arch = "arm", target_os = "none")))]
set_random_source(source: impl RandomSource + Send + Sync + 'static) -> Result<(), bool>188     pub fn set_random_source(source: impl RandomSource + Send + Sync + 'static) -> Result<(), bool> {
189         RAND_SOURCE.set(Box::new(Box::new(source))).map_err(|s| s.as_ref().type_id() != TypeId::of::<&DefaultRandomSource>())
190     }
191 
192     #[inline]
193     #[cfg(not(all(target_arch = "arm", target_os = "none")))]
get_src() -> &'static dyn RandomSource194     fn get_src() -> &'static dyn RandomSource {
195         RAND_SOURCE.get_or_init(|| Box::new(Box::new(DefaultRandomSource::new()))).as_ref()
196     }
197 
198     #[inline]
199     #[cfg(all(target_arch = "arm", target_os = "none"))]
get_src() -> &'static dyn RandomSource200     fn get_src() -> &'static dyn RandomSource {
201         static RAND_SOURCE: DefaultRandomSource = DefaultRandomSource::default();
202         &RAND_SOURCE
203     }
204 
205     /// Use randomly generated keys
206     #[inline]
new() -> RandomState207     pub fn new() -> RandomState {
208         let src = Self::get_src();
209         let fixed = src.get_fixed_seeds();
210         Self::from_keys(&fixed[0], &fixed[1], src.gen_hasher_seed())
211     }
212 
213     /// Allows for supplying seeds, but each time it is called the resulting state will be different.
214     /// This is done using a static counter, so it can safely be used with a fixed keys.
215     #[inline]
generate_with(k0: u64, k1: u64, k2: u64, k3: u64) -> RandomState216     pub fn generate_with(k0: u64, k1: u64, k2: u64, k3: u64) -> RandomState {
217         let src = Self::get_src();
218         let fixed = src.get_fixed_seeds();
219         RandomState::from_keys(&fixed[0], &[k0, k1, k2, k3], src.gen_hasher_seed())
220     }
221 
from_keys(a: &[u64; 4], b: &[u64; 4], c: usize) -> RandomState222     fn from_keys(a: &[u64; 4], b: &[u64; 4], c: usize) -> RandomState {
223         let &[k0, k1, k2, k3] = a;
224         let mut hasher = AHasher::from_random_state(&RandomState { k0, k1, k2, k3 });
225         hasher.write_usize(c);
226         let mix = |k: u64| {
227             let mut h = hasher.clone();
228             h.write_u64(k);
229             h.finish()
230         };
231         RandomState {
232             k0: mix(b[0]),
233             k1: mix(b[1]),
234             k2: mix(b[2]),
235             k3: mix(b[3]),
236         }
237     }
238 
239     /// Internal. Used by Default.
240     #[inline]
with_fixed_keys() -> RandomState241     pub(crate) fn with_fixed_keys() -> RandomState {
242         let [k0, k1, k2, k3] = Self::get_src().get_fixed_seeds()[0];
243         RandomState { k0, k1, k2, k3 }
244     }
245 
246     /// Allows for explicitly setting a seed to used.
247     ///
248     /// Note: This method does not require the provided seed to be strong.
249     #[inline]
with_seed(key: usize) -> RandomState250     pub fn with_seed(key: usize) -> RandomState {
251         let fixed = Self::get_src().get_fixed_seeds();
252         RandomState::from_keys(&fixed[0], &fixed[1], key)
253     }
254 
255     /// Allows for explicitly setting the seeds to used.
256     ///
257     /// Note: This method is robust against 0s being passed for one or more of the parameters
258     /// or the same value being passed for more than one parameter.
259     #[inline]
with_seeds(k0: u64, k1: u64, k2: u64, k3: u64) -> RandomState260     pub const fn with_seeds(k0: u64, k1: u64, k2: u64, k3: u64) -> RandomState {
261         RandomState { k0: k0 ^ PI2[0], k1: k1 ^ PI2[1], k2: k2 ^ PI2[2], k3: k3 ^ PI2[3] }
262     }
263 }
264 
265 impl Default for RandomState {
266     #[inline]
default() -> Self267     fn default() -> Self {
268         Self::new()
269     }
270 }
271 
272 impl BuildHasher for RandomState {
273     type Hasher = AHasher;
274 
275     /// Constructs a new [AHasher] with keys based on this [RandomState] object.
276     /// This means that two different [RandomState]s will will generate
277     /// [AHasher]s that will return different hashcodes, but [Hasher]s created from the same [BuildHasher]
278     /// will generate the same hashes for the same input data.
279     ///
280     /// # Examples
281     ///
282     /// ```
283     /// use ahash::{AHasher, RandomState};
284     /// use std::hash::{Hasher, BuildHasher};
285     ///
286     /// let build_hasher = RandomState::new();
287     /// let mut hasher_1 = build_hasher.build_hasher();
288     /// let mut hasher_2 = build_hasher.build_hasher();
289     ///
290     /// hasher_1.write_u32(1234);
291     /// hasher_2.write_u32(1234);
292     ///
293     /// assert_eq!(hasher_1.finish(), hasher_2.finish());
294     ///
295     /// let other_build_hasher = RandomState::new();
296     /// let mut different_hasher = other_build_hasher.build_hasher();
297     /// different_hasher.write_u32(1234);
298     /// assert_ne!(different_hasher.finish(), hasher_1.finish());
299     /// ```
300     /// [Hasher]: std::hash::Hasher
301     /// [BuildHasher]: std::hash::BuildHasher
302     /// [HashMap]: std::collections::HashMap
303     #[inline]
build_hasher(&self) -> AHasher304     fn build_hasher(&self) -> AHasher {
305         AHasher::from_random_state(self)
306     }
307 }
308 
309 #[cfg(feature = "specialize")]
310 impl BuildHasherExt for RandomState {
311     #[inline]
hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64312     fn hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64 {
313         let mut hasher = AHasherU64 {
314             buffer: self.k0,
315             pad: self.k1,
316         };
317         value.hash(&mut hasher);
318         hasher.finish()
319     }
320 
321     #[inline]
hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64322     fn hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64 {
323         let mut hasher = AHasherFixed(self.build_hasher());
324         value.hash(&mut hasher);
325         hasher.finish()
326     }
327 
328     #[inline]
hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64329     fn hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64 {
330         let mut hasher = AHasherStr(self.build_hasher());
331         value.hash(&mut hasher);
332         hasher.finish()
333     }
334 }
335 
336 #[cfg(test)]
337 mod test {
338     use super::*;
339 
340     #[test]
test_unique()341     fn test_unique() {
342         let a = RandomState::new();
343         let b = RandomState::new();
344         assert_ne!(a.build_hasher().finish(), b.build_hasher().finish());
345     }
346 
347     #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
348     #[test]
test_not_pi()349     fn test_not_pi() {
350         assert_ne!(PI, RandomState::get_src().get_fixed_seeds()[0]);
351     }
352 
353     #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))]
354     #[test]
test_not_pi_const()355     fn test_not_pi_const() {
356         assert_ne!(PI, RandomState::get_src().get_fixed_seeds()[0]);
357     }
358 
359     #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))]
360     #[test]
test_pi()361     fn test_pi() {
362         assert_eq!(PI, RandomState::get_src().get_fixed_seeds()[0]);
363     }
364 
365     #[test]
test_with_seeds_const()366     fn test_with_seeds_const() {
367         const _CONST_RANDOM_STATE: RandomState = RandomState::with_seeds(17, 19, 21, 23);
368     }
369 }
370