1const P1: u64 = 0x9E37_79B1_85EB_CA87;
3const P2: u64 = 0xC2B2_AE3D_27D4_EB4F;
4const P3: u64 = 0x1656_67B1_9E37_79F9;
5const P4: u64 = 0x85EB_CA77_C2B2_AE63;
6const P5: u64 = 0x27D4_EB2F_1656_67C5;
7
8#[inline(always)]
9fn round(acc: u64, lane: u64) -> u64 {
10 acc.wrapping_add(lane.wrapping_mul(P2)).rotate_left(31).wrapping_mul(P1)
11}
12
13#[inline(always)]
14fn merge(acc: u64, v: u64) -> u64 {
15 (acc ^ round(0, v)).wrapping_mul(P1).wrapping_add(P4)
16}
17
18#[inline(always)]
19fn le64(b: &[u8]) -> u64 {
20 let mut a = [0u8; 8];
21 a.copy_from_slice(&b[..8]);
22 u64::from_le_bytes(a)
23}
24
25#[derive(Clone)]
27pub struct Xxh64 {
28 v: [u64; 4],
29 buf: [u8; 32],
30 buf_len: usize,
31 total: u64,
32 seed: u64,
33}
34
35impl Xxh64 {
36 pub fn new(seed: u64) -> Self {
38 Xxh64 {
39 v: [seed.wrapping_add(P1).wrapping_add(P2), seed.wrapping_add(P2), seed, seed.wrapping_sub(P1)],
40 buf: [0; 32],
41 buf_len: 0,
42 total: 0,
43 seed,
44 }
45 }
46
47 fn stripe(v: &mut [u64; 4], s: &[u8]) {
48 v[0] = round(v[0], le64(&s[0..]));
49 v[1] = round(v[1], le64(&s[8..]));
50 v[2] = round(v[2], le64(&s[16..]));
51 v[3] = round(v[3], le64(&s[24..]));
52 }
53
54 pub fn update(&mut self, mut data: &[u8]) {
56 self.total = self.total.wrapping_add(data.len() as u64);
57 if self.buf_len > 0 {
58 let take = (32 - self.buf_len).min(data.len());
59 self.buf[self.buf_len..self.buf_len + take].copy_from_slice(&data[..take]);
60 self.buf_len += take;
61 data = &data[take..];
62 if self.buf_len < 32 {
63 return;
64 }
65 let b = self.buf;
66 Self::stripe(&mut self.v, &b);
67 self.buf_len = 0;
68 }
69 let mut chunks = data.chunks_exact(32);
70 for c in &mut chunks {
71 Self::stripe(&mut self.v, c);
72 }
73 let rest = chunks.remainder();
74 self.buf[..rest.len()].copy_from_slice(rest);
75 self.buf_len = rest.len();
76 }
77
78 pub fn digest(&self) -> u64 {
80 let mut h = if self.total >= 32 {
81 let v = self.v;
82 let mut h =
83 v[0].rotate_left(1).wrapping_add(v[1].rotate_left(7)).wrapping_add(v[2].rotate_left(12)).wrapping_add(v[3].rotate_left(18));
84 for x in v {
85 h = merge(h, x);
86 }
87 h
88 } else {
89 self.seed.wrapping_add(P5)
90 };
91 h = h.wrapping_add(self.total);
92 let mut rest = &self.buf[..self.buf_len];
93 while rest.len() >= 8 {
94 h ^= round(0, le64(rest));
95 h = h.rotate_left(27).wrapping_mul(P1).wrapping_add(P4);
96 rest = &rest[8..];
97 }
98 if rest.len() >= 4 {
99 let w = u32::from_le_bytes([rest[0], rest[1], rest[2], rest[3]]) as u64;
100 h ^= w.wrapping_mul(P1);
101 h = h.rotate_left(23).wrapping_mul(P2).wrapping_add(P3);
102 rest = &rest[4..];
103 }
104 for &b in rest {
105 h ^= (b as u64).wrapping_mul(P5);
106 h = h.rotate_left(11).wrapping_mul(P1);
107 }
108 h ^= h >> 33;
109 h = h.wrapping_mul(P2);
110 h ^= h >> 29;
111 h = h.wrapping_mul(P3);
112 h ^ (h >> 32)
113 }
114}
115
116pub fn xxh64(data: &[u8], seed: u64) -> u64 {
118 let mut h = Xxh64::new(seed);
119 h.update(data);
120 h.digest()
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn known_vectors() {
129 assert_eq!(xxh64(b"", 0), 0xEF46_DB37_51D8_E999);
130 assert_eq!(xxh64(b"a", 0), 0xD24E_C4F1_A98C_6E5B);
131 assert_eq!(xxh64(b"abc", 0), 0x44BC_2CF5_AD77_0999);
132 let long = b"Nobody inspects the spammish repetition";
133 assert_eq!(xxh64(long, 0), 0xFBCE_A83C_8A37_8BF1);
134 }
135
136 #[test]
137 fn streaming_matches_one_shot() {
138 let data: alloc::vec::Vec<u8> = (0..1000u32).map(|i| (i * 7 + 3) as u8).collect();
139 for split in [0, 1, 7, 31, 32, 33, 500, 999, 1000] {
140 let mut h = Xxh64::new(0);
141 h.update(&data[..split]);
142 h.update(&data[split..]);
143 assert_eq!(h.digest(), xxh64(&data, 0), "split {split}");
144 }
145 }
146}