1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
use crate::AHasher;
use core::hash::BuildHasher;
use core::sync::atomic::AtomicUsize;
use core::sync::atomic::Ordering;
#[cfg(feature = "compile-time-rng")]
use const_random::const_random;
///This constant come from Kunth's prng
pub(crate) const MULTIPLE: u64 = 6364136223846793005;
pub(crate) const INCREMENT: u64 = 1442695040888963407;
// Const random provides randomized starting key with no runtime cost.
#[cfg(feature = "compile-time-rng")]
const INIT_SEED: u64 = const_random!(u64);
#[cfg(not(feature = "compile-time-rng"))]
const INIT_SEED: u64 = INCREMENT;
static SEED: AtomicUsize = AtomicUsize::new(INIT_SEED as usize);
/// Provides a [Hasher] factory. This is typically used (e.g. by [`HashMap`]) to create
/// [AHasher]s in order to hash the keys of the map. See `build_hasher` below.
///
/// [build_hasher]: ahash::
/// [Hasher]: std::hash::Hasher
/// [BuildHasher]: std::hash::BuildHasher
/// [HashMap]: std::collections::HashMap
#[derive(Clone)]
pub struct RandomState {
pub(crate) k0: u64,
pub(crate) k1: u64,
}
impl RandomState {
#[inline]
pub fn new() -> RandomState {
//Using a self pointer. When running with ASLR this is a random value.
let previous = SEED.load(Ordering::Relaxed) as u64;
let stack_mem_loc = &previous as *const _ as u64;
//This is similar to the update function in the fallback.
//only one multiply is needed because memory locations are not under an attackers control.
let current_seed = previous
.wrapping_add(stack_mem_loc)
.wrapping_mul(MULTIPLE)
.rotate_right(31);
SEED.store(current_seed as usize, Ordering::Relaxed);
let (k0, k1) = scramble_keys(&SEED as *const _ as u64, current_seed);
RandomState { k0, k1 }
}
/// Allows for explicetly setting the seeds to used.
pub fn with_seeds(k0: u64, k1: u64) -> RandomState {
RandomState { k0, k1 }
}
}
pub(crate) fn scramble_keys(k0: u64, k1: u64) -> (u64, u64) {
//Scramble seeds (based on xoroshiro128+)
//This is intentionally not similar the hash algorithm
let result1 = k0.wrapping_add(k1);
let k1 = k1 ^ k0;
let k0 = k0.rotate_left(24) ^ k1 ^ (k1.wrapping_shl(16));
let result2 = k0.wrapping_add(k1.rotate_left(37));
(result2, result1)
}
impl Default for RandomState {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl BuildHasher for RandomState {
type Hasher = AHasher;
/// Constructs a new [AHasher] with keys based on compile time generated constants** and the location
/// this object was constructed at in memory. This means that two different [BuildHasher]s will will generate
/// [AHasher]s that will return different hashcodes, but [Hasher]s created from the same [BuildHasher]
/// will generate the same hashes for the same input data.
///
/// ** - only if the `compile-time-rng` feature is enabled.
///
/// # Examples
///
/// ```
/// use ahash::{AHasher, RandomState};
/// use std::hash::{Hasher, BuildHasher};
///
/// let build_hasher = RandomState::new();
/// let mut hasher_1 = build_hasher.build_hasher();
/// let mut hasher_2 = build_hasher.build_hasher();
///
/// hasher_1.write_u32(1234);
/// hasher_2.write_u32(1234);
///
/// assert_eq!(hasher_1.finish(), hasher_2.finish());
///
/// let other_build_hasher = RandomState::new();
/// let mut different_hasher = other_build_hasher.build_hasher();
/// different_hasher.write_u32(1234);
/// assert_ne!(different_hasher.finish(), hasher_1.finish());
/// ```
/// [Hasher]: std::hash::Hasher
/// [BuildHasher]: std::hash::BuildHasher
/// [HashMap]: std::collections::HashMap
#[inline]
fn build_hasher(&self) -> AHasher {
AHasher::new_with_keys(self.k0, self.k1)
}
}