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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// ----------------------------------------------------------------------------

use std::hash::BuildHasher;

/// 64-bit hash.
///
/// 10^-12 collision risk with   6k values.
/// 10^-9  collision risk with 190k values.
/// 10^-6  collision risk with   6M values.
/// 10^-3  collision risk with 200M values.
#[derive(Copy, Clone, Eq, PartialOrd, Ord)]
pub struct Hash64(u64);

impl re_types_core::SizeBytes for Hash64 {
    #[inline]
    fn heap_size_bytes(&self) -> u64 {
        0
    }
}

impl Hash64 {
    pub const ZERO: Hash64 = Hash64(0);

    pub fn hash(value: impl std::hash::Hash + Copy) -> Self {
        Self(hash(value))
    }

    /// From an existing u64. Use this only for data conversions.
    #[inline]
    pub fn from_u64(i: u64) -> Self {
        Self(i)
    }

    #[inline]
    pub fn hash64(&self) -> u64 {
        self.0
    }
}

impl std::hash::Hash for Hash64 {
    #[inline]
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        state.write_u64(self.0);
    }
}

impl std::cmp::PartialEq for Hash64 {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl nohash_hasher::IsEnabled for Hash64 {}

impl std::fmt::Debug for Hash64 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&format!("Hash64({:016X})", self.0))
    }
}

// ----------------------------------------------------------------------------

/// 128-bit hash. Negligible risk for collision.
#[derive(Copy, Clone, Eq)]
pub struct Hash128([u64; 2]);

impl Hash128 {
    pub const ZERO: Hash128 = Hash128([0; 2]);

    pub fn hash(value: impl std::hash::Hash + Copy) -> Self {
        Self(double_hash(value))
    }

    #[inline]
    pub fn hash64(&self) -> u64 {
        self.0[0]
    }

    #[inline]
    pub fn first64(&self) -> u64 {
        self.0[0]
    }

    #[inline]
    pub fn second64(&self) -> u64 {
        self.0[1]
    }
}

impl std::hash::Hash for Hash128 {
    #[inline]
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        state.write_u64(self.0[0]);
    }
}

impl std::cmp::PartialEq for Hash128 {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl nohash_hasher::IsEnabled for Hash128 {}

impl std::fmt::Debug for Hash128 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&format!("Hash128({:016X}{:016X})", self.0[0], self.0[1]))
    }
}

// ----------------------------------------------------------------------------

pub const HASH_RANDOM_STATE: ahash::RandomState = ahash::RandomState::with_seeds(0, 1, 2, 3);

#[inline]
fn double_hash(value: impl std::hash::Hash + Copy) -> [u64; 2] {
    [hash_with_seed(value, 123), hash_with_seed(value, 456)]
}

/// Hash the given value.
#[inline]
fn hash_with_seed(value: impl std::hash::Hash, seed: u128) -> u64 {
    use std::hash::Hash as _;
    use std::hash::Hasher as _;

    // Don't use ahash::AHasher::default() since it uses a random number for seeding the hasher on every application start.
    let mut hasher = HASH_RANDOM_STATE.build_hasher();
    seed.hash(&mut hasher);
    value.hash(&mut hasher);
    hasher.finish()
}

/// Hash the given value.
#[inline]
fn hash(value: impl std::hash::Hash) -> u64 {
    // Don't use ahash::AHasher::default() since it uses a random number for seeding the hasher on every application start.
    HASH_RANDOM_STATE.hash_one(value)
}