1use std::hash::Hasher;
2use std::slice;
3
4use helper;
5
6#[derive(Clone, Copy)]
8pub struct SeaHasher {
9 state: (u64, u64, u64, u64),
11 written: u64,
13 tail: u64,
15 ntail: usize,
17}
18
19impl Default for SeaHasher {
20 fn default() -> SeaHasher {
21 SeaHasher::with_seeds(
22 0x16f11fe89b0d677c,
23 0xb480a793d8e6c86c,
24 0x6fe2e5aaf078ebc9,
25 0x14f994a4c5259381,
26 )
27 }
28}
29
30impl SeaHasher {
31 pub fn new() -> SeaHasher {
33 SeaHasher::default()
34 }
35
36 pub fn with_seeds(k1: u64, k2: u64, k3: u64, k4: u64) -> SeaHasher {
40 SeaHasher {
41 state: (k1, k2, k3, k4),
42 written: 0,
43 tail: 0,
44 ntail: 0,
45 }
46 }
47
48 #[inline(always)]
49 fn push(&mut self, x: u64) {
50 let a = helper::diffuse(self.state.0 ^ x);
51 self.state.0 = self.state.1;
52 self.state.1 = self.state.2;
53 self.state.2 = self.state.3;
54 self.state.3 = a;
55 self.written += 8;
56 }
57
58 #[inline(always)]
59 fn push_bytes(&mut self, bytes: &[u8]) {
60 let copied = core::cmp::min(8 - self.ntail, bytes.len());
62 unsafe {
63 let mut this = self.tail.to_le_bytes();
64 let mut ptr = bytes.as_ptr();
65 ptr.copy_to_nonoverlapping(this.as_mut_ptr().add(self.ntail), copied);
66 if copied + self.ntail != 8 {
68 self.ntail += copied;
69 self.tail = u64::from_le_bytes(this);
70 } else {
71 self.push(u64::from_le_bytes(this));
72 self.ntail = 0;
73 self.tail = 0;
74
75 ptr = ptr.offset(copied as isize);
77 let end_ptr = ptr.offset((bytes.len() - copied) as isize & !0x1F);
78 while end_ptr > ptr {
79 self.state.0 = helper::diffuse(self.state.0 ^ helper::read_u64(ptr));
80 self.state.1 = helper::diffuse(self.state.1 ^ helper::read_u64(ptr.offset(8)));
81 self.state.2 = helper::diffuse(self.state.2 ^ helper::read_u64(ptr.offset(16)));
82 self.state.3 = helper::diffuse(self.state.3 ^ helper::read_u64(ptr.offset(24)));
83
84 ptr = ptr.offset(32);
85 self.written += 32;
86 }
87 let mut excessive = bytes.len() + bytes.as_ptr() as usize - ptr as usize;
88 match excessive {
89 0 => {
90 }
92 1..=7 => {
93 self.tail =
94 helper::read_int(slice::from_raw_parts(ptr as *const u8, excessive));
95 self.ntail = excessive;
96 }
99 8 => {
100 self.push(helper::read_u64(ptr));
101 }
103 9..=15 => {
104 self.push(helper::read_u64(ptr));
105 excessive -= 8;
106 self.tail =
107 helper::read_int(slice::from_raw_parts(ptr.offset(8), excessive));
108 self.ntail = excessive;
109 }
111 16 => {
112 let a = helper::diffuse(self.state.0 ^ helper::read_u64(ptr));
113 let b = helper::diffuse(self.state.1 ^ helper::read_u64(ptr.offset(8)));
114 self.state.0 = self.state.2;
116 self.state.1 = self.state.3;
117 self.state.2 = a;
118 self.state.3 = b;
119 self.written += 16;
120 }
121 17..=23 => {
122 let a = helper::diffuse(self.state.0 ^ helper::read_u64(ptr));
123 let b = helper::diffuse(self.state.1 ^ helper::read_u64(ptr.offset(8)));
124 self.state.0 = self.state.2;
126 self.state.1 = self.state.3;
127 self.state.2 = a;
128 self.state.3 = b;
129 excessive -= 16;
130 self.tail =
131 helper::read_int(slice::from_raw_parts(ptr.offset(16), excessive));
132 self.ntail = excessive;
133 self.written += 16;
134 }
135 24 => {
136 let a = helper::diffuse(self.state.0 ^ helper::read_u64(ptr));
137 let b = helper::diffuse(self.state.1 ^ helper::read_u64(ptr.offset(8)));
138 let c = helper::diffuse(self.state.2 ^ helper::read_u64(ptr.offset(16)));
139 self.state.0 = self.state.3;
140 self.state.1 = a;
141 self.state.2 = b;
142 self.state.3 = c;
143 self.written += 24;
144 }
145 _ => {
146 let a = helper::diffuse(self.state.0 ^ helper::read_u64(ptr));
147 let b = helper::diffuse(self.state.1 ^ helper::read_u64(ptr.offset(8)));
148 let c = helper::diffuse(self.state.2 ^ helper::read_u64(ptr.offset(16)));
149 self.state.0 = self.state.3;
150 self.state.1 = a;
151 self.state.2 = b;
152 self.state.3 = c;
153 excessive -= 24;
154 self.tail =
155 helper::read_int(slice::from_raw_parts(ptr.offset(24), excessive));
156 self.ntail = excessive;
157 self.written += 24;
158 }
159 }
160 }
161 }
162 }
163}
164
165impl Hasher for SeaHasher {
166 fn finish(&self) -> u64 {
167 let a = if self.ntail > 0 {
168 let tail = helper::read_int(&self.tail.to_le_bytes()[..self.ntail]);
169 helper::diffuse(self.state.0 ^ tail)
170 } else {
171 self.state.0
172 };
173 helper::diffuse(
174 a ^ self.state.1 ^ self.state.2 ^ self.state.3 ^ self.written + self.ntail as u64,
175 )
176 }
177
178 fn write(&mut self, bytes: &[u8]) {
179 self.push_bytes(bytes)
180 }
181
182 fn write_u64(&mut self, n: u64) {
183 self.write(&n.to_le_bytes())
184 }
185
186 fn write_u8(&mut self, n: u8) {
187 self.write(&n.to_le_bytes())
188 }
189
190 fn write_u16(&mut self, n: u16) {
191 self.write(&n.to_le_bytes())
192 }
193
194 fn write_u32(&mut self, n: u32) {
195 self.write(&n.to_le_bytes())
196 }
197
198 fn write_usize(&mut self, n: usize) {
199 self.write(&n.to_le_bytes())
200 }
201
202 fn write_i64(&mut self, n: i64) {
203 self.write(&n.to_le_bytes())
204 }
205
206 fn write_i8(&mut self, n: i8) {
207 self.write(&n.to_le_bytes())
208 }
209
210 fn write_i16(&mut self, n: i16) {
211 self.write(&n.to_le_bytes())
212 }
213
214 fn write_i32(&mut self, n: i32) {
215 self.write(&n.to_le_bytes())
216 }
217
218 fn write_isize(&mut self, n: isize) {
219 self.write(&n.to_le_bytes())
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226 use crate::hash_seeded;
227 use std::hash::Hasher;
228
229 #[test]
230 fn chunked_equiv() {
231 let test_buf: &[u8] = &[
232 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 0x00, 0x00,
234 ];
235
236 let mut stream_hasher1 = SeaHasher::default();
237 Hasher::write(&mut stream_hasher1, test_buf);
238
239 let mut stream_hasher2 = SeaHasher::default();
240 Hasher::write(&mut stream_hasher2, &test_buf[..8]);
241 Hasher::write(&mut stream_hasher2, &test_buf[8..]);
242
243 let mut stream_hasher3 = SeaHasher::default();
244 Hasher::write(&mut stream_hasher3, &test_buf[..3]);
245 Hasher::write(&mut stream_hasher3, &test_buf[3..]);
246
247 let mut stream_hasher4 = SeaHasher::default();
248 Hasher::write_u16(&mut stream_hasher4, 0xffff);
249 Hasher::write_u16(&mut stream_hasher4, 0xffff);
250 Hasher::write_u32(&mut stream_hasher4, 0xffffffff);
251 Hasher::write_u64(&mut stream_hasher4, 0);
252
253 assert_eq!(stream_hasher1.finish(), stream_hasher2.finish());
254 assert_eq!(stream_hasher1.finish(), stream_hasher3.finish());
255 assert_eq!(stream_hasher1.finish(), stream_hasher4.finish());
256 }
257
258 #[test]
259 fn match_optimized() {
260 let test_buf: &[u8] = &[
261 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
262 0x00, 0x00,
263 ];
264
265 let mut sea_hasher = SeaHasher::with_seeds(
266 0xe7b0c93ca8525013,
267 0x011d02b854ae8182,
268 0x7bcc5cf9c39cec76,
269 0xfa336285d102d083,
270 );
271 sea_hasher.write(test_buf);
272 let stream_hash = sea_hasher.finish();
273
274 let buffer_hash = hash_seeded(
275 test_buf,
276 0xe7b0c93ca8525013,
277 0x011d02b854ae8182,
278 0x7bcc5cf9c39cec76,
279 0xfa336285d102d083,
280 );
281
282 assert_eq!(buffer_hash, stream_hash)
283 }
284}