seahash/
stream.rs

1use std::hash::Hasher;
2use std::slice;
3
4use helper;
5
6/// The streaming version of the algorithm.
7#[derive(Clone, Copy)]
8pub struct SeaHasher {
9    /// The state of the hasher.
10    state: (u64, u64, u64, u64),
11    /// The number of bytes we have written in total
12    written: u64,
13    /// Our tail
14    tail: u64,
15    /// The number of bytes in the tail
16    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    /// Create a new `SeaHasher` with default state.
32    pub fn new() -> SeaHasher {
33        SeaHasher::default()
34    }
35
36    /// Construct a new `SeaHasher` given some seed.
37    ///
38    /// For maximum quality, these seeds should be chosen at random.
39    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        // The start of the bytes that aren't in the tail
61        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            // It will be at most 8
67            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                // We've done the existing tail, now just do the rest in chunks of 4 x u64.
76                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                        // input was a multiple of 4 x u64 bytes long; no new tail bytes.
91                    }
92                    1..=7 => {
93                        self.tail =
94                            helper::read_int(slice::from_raw_parts(ptr as *const u8, excessive));
95                        self.ntail = excessive;
96                        // self.written does not need to be updated as we only gathered self.tail
97                        // bytes after larger chunks.
98                    }
99                    8 => {
100                        self.push(helper::read_u64(ptr));
101                        // self.written is updated by self.push
102                    }
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                        // self.written is updated by self.push
110                    }
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                        // rotate
115                        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                        // rotate
125                        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}