Skip to main content

rapidhash/inner/state/
global_state.rs

1use core::hash::BuildHasher;
2use core::fmt::Formatter;
3use crate::inner::RapidHasher;
4use crate::inner::seeding::secrets::GlobalSecrets;
5
6/// A [`BuildHasher`] that uses a global seed and secrets, randomized only once on startup.
7///
8/// The global secrets are randomized on the first instantiation, and then every subsequent instance
9/// of GlobalState will re-use the same seed and secrets, ensuring consistent hash outputs for the
10/// duration of the program.
11#[derive(Copy, Clone, Eq, PartialEq)]
12pub struct GlobalState<const AVALANCHE: bool, const SPONGE: bool, const COMPACT: bool, const PROTECTED: bool> {
13    /// The global secrets is a zero-sized type to keep HashMap<K, V, RandomState> small.
14    secrets: GlobalSecrets,
15}
16
17impl<const AVALANCHE: bool, const SPONGE: bool, const COMPACT: bool, const PROTECTED: bool> GlobalState<AVALANCHE, SPONGE, COMPACT, PROTECTED> {
18    /// Create a new global state with a global seed and secrets.
19    ///
20    /// The seed and secrets will be randomized on the first instantiation of `GlobalState`, but all
21    /// subsequent instances will share the same seed and secrets.
22    ///
23    /// On platforms which do not support atomic pointers, the secrets will be the default rapidhash
24    /// secrets, which are not randomized. Therefore, **targets without atomic pointer support will
25    /// not have minimal HashDoS resistance guarantees**.
26    #[inline(always)]
27    pub fn new() -> Self {
28        Self {
29            secrets: GlobalSecrets::new(),
30        }
31    }
32}
33
34/// Warning that `GlobalState` only randomizes the seed on platforms that support atomic pointers.
35impl<const AVALANCHE: bool, const SPONGE: bool, const COMPACT: bool, const PROTECTED: bool> Default for GlobalState<AVALANCHE, SPONGE, COMPACT, PROTECTED> {
36    #[inline(always)]
37    fn default() -> Self {
38        Self::new()
39    }
40}
41
42impl<const AVALANCHE: bool, const SPONGE: bool, const COMPACT: bool, const PROTECTED: bool>  BuildHasher for GlobalState<AVALANCHE, SPONGE, COMPACT, PROTECTED> {
43    type Hasher = RapidHasher<'static, AVALANCHE, SPONGE, COMPACT, PROTECTED>;
44
45    #[inline(always)]
46    fn build_hasher(&self) -> Self::Hasher {
47        RapidHasher::new_precomputed_seed(
48            self.secrets.get_global_seed(),
49            self.secrets.get()
50        )
51    }
52}
53
54impl<const AVALANCHE: bool, const SPONGE: bool, const COMPACT: bool, const PROTECTED: bool> core::fmt::Debug for GlobalState<AVALANCHE, SPONGE, COMPACT, PROTECTED> {
55    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
56        f.debug_struct("GlobalState").finish_non_exhaustive()
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use core::hash::BuildHasher;
63
64    type GlobalState = super::GlobalState<false, true, false, false>;
65
66    #[test]
67    fn test_global_state() {
68        assert_eq!(core::mem::size_of::<GlobalState>(), 0);
69
70        let state1 = GlobalState::new();
71        let state2 = GlobalState::new();
72
73        let finish1a = state1.hash_one(b"hello");
74        let finish1b = state1.hash_one(b"hello");
75        let finish2a = state2.hash_one(b"hello");
76
77        assert_eq!(finish1a, finish1b);
78        assert_eq!(finish1a, finish2a);
79    }
80
81    #[test]
82    fn test_debug() {
83        extern crate alloc;
84        let state = GlobalState::new();
85        let debug_str = alloc::format!("{:?}", state);
86        assert_eq!(debug_str, "GlobalState { .. }");
87    }
88}