1#[must_use]
12#[inline]
13pub fn xxh3_64(bytes: &[u8]) -> u64 {
14 xxhash_rust::xxh3::xxh3_64(bytes)
15}
16
17#[must_use]
19#[inline]
20pub fn xxh3_64_seeded(bytes: &[u8], seed: u64) -> u64 {
21 xxhash_rust::xxh3::xxh3_64_with_seed(bytes, seed)
22}
23
24#[must_use]
26#[inline]
27pub fn xxh3_128(bytes: &[u8]) -> u128 {
28 xxhash_rust::xxh3::xxh3_128(bytes)
29}
30
31#[must_use]
34#[inline]
35pub fn fmix64(mut k: u64) -> u64 {
36 k ^= k >> 33;
37 k = k.wrapping_mul(0xFF51_AFD7_ED55_8CCD);
38 k ^= k >> 33;
39 k = k.wrapping_mul(0xC4CE_B9FE_1A85_EC53);
40 k ^ (k >> 33)
41}
42
43#[must_use]
46#[inline]
47pub fn trigram(w: &[u8]) -> u32 {
48 u32::from(w[0]) << 16 | u32::from(w[1]) << 8 | u32::from(w[2])
49}
50
51#[derive(Clone)]
54pub struct Hasher(xxhash_rust::xxh3::Xxh3);
55
56impl std::fmt::Debug for Hasher {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.write_str("Hasher")
59 }
60}
61
62impl Default for Hasher {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68impl Hasher {
69 #[must_use]
71 pub fn new() -> Self {
72 Self(xxhash_rust::xxh3::Xxh3::new())
73 }
74
75 #[must_use]
77 pub fn with_seed(seed: u64) -> Self {
78 Self(xxhash_rust::xxh3::Xxh3::with_seed(seed))
79 }
80
81 #[inline]
83 pub fn update(&mut self, bytes: &[u8]) {
84 self.0.update(bytes);
85 }
86
87 #[inline]
90 pub fn field(&mut self, bytes: &[u8]) {
91 self.update(&(bytes.len() as u64).to_le_bytes());
92 self.update(bytes);
93 }
94
95 #[must_use]
97 pub fn digest64(&self) -> u64 {
98 self.0.digest()
99 }
100
101 #[must_use]
103 pub fn digest128(&self) -> u128 {
104 self.0.digest128()
105 }
106
107 pub fn reset(&mut self) {
109 self.0.reset();
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn one_shot_matches_url_key() {
119 assert_eq!(xxh3_64(b"https://x.dev/a"), crate::UrlKey::of("https://x.dev/a").raw());
120 }
121
122 #[test]
123 fn streaming_equals_one_shot() {
124 let mut h = Hasher::new();
125 h.update(b"hello ");
126 h.update(b"world");
127 assert_eq!(h.digest64(), xxh3_64(b"hello world"));
128 assert_eq!(h.digest128(), xxh3_128(b"hello world"));
129 h.reset();
130 assert_eq!(h.digest64(), xxh3_64(b""));
131 }
132
133 #[test]
134 fn seeds_give_independent_functions() {
135 assert_ne!(xxh3_64_seeded(b"v", 1), xxh3_64_seeded(b"v", 2));
136 let mut h = Hasher::with_seed(7);
137 h.update(b"v");
138 assert_eq!(h.digest64(), xxh3_64_seeded(b"v", 7));
139 }
140}