]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
1a4d82fc JJ |
10 | |
11 | //! An implementation of SipHash 2-4. | |
12 | ||
e9174d1e SL |
13 | use prelude::v1::*; |
14 | ||
c1a9b12d | 15 | use ptr; |
85aaf69f | 16 | use super::Hasher; |
1a4d82fc JJ |
17 | |
18 | /// An implementation of SipHash 2-4. | |
19 | /// | |
54a0048b | 20 | /// See: https://131002.net/siphash/ |
1a4d82fc | 21 | /// |
54a0048b SL |
22 | /// This is currently the default hashing function used by standard library |
23 | /// (eg. `collections::HashMap` uses it by default). | |
1a4d82fc | 24 | /// |
54a0048b SL |
25 | /// SipHash is a general-purpose hashing function: it runs at a good |
26 | /// speed (competitive with Spooky and City) and permits strong _keyed_ | |
27 | /// hashing. This lets you key your hashtables from a strong RNG, such as | |
28 | /// [`rand::os::OsRng`](https://doc.rust-lang.org/rand/rand/os/struct.OsRng.html). | |
29 | /// | |
30 | /// Although the SipHash algorithm is considered to be generally strong, | |
31 | /// it is not intended for cryptographic purposes. As such, all | |
32 | /// cryptographic uses of this implementation are _strongly discouraged_. | |
33 | #[derive(Debug)] | |
85aaf69f | 34 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
35 | pub struct SipHasher { |
36 | k0: u64, | |
37 | k1: u64, | |
c34b1796 | 38 | length: usize, // how many bytes we've processed |
c1a9b12d SL |
39 | // v0, v2 and v1, v3 show up in pairs in the algorithm, |
40 | // and simd implementations of SipHash will use vectors | |
41 | // of v02 and v13. By placing them in this order in the struct, | |
42 | // the compiler can pick up on just a few simd optimizations by itself. | |
b039eaaf | 43 | v0: u64, // hash state |
1a4d82fc | 44 | v2: u64, |
c1a9b12d | 45 | v1: u64, |
1a4d82fc JJ |
46 | v3: u64, |
47 | tail: u64, // unprocessed bytes le | |
b039eaaf | 48 | ntail: usize, // how many bytes in tail are valid |
1a4d82fc JJ |
49 | } |
50 | ||
51 | // sadly, these macro definitions can't appear later, | |
52 | // because they're needed in the following defs; | |
53 | // this design could be improved. | |
54 | ||
55 | macro_rules! u8to64_le { | |
56 | ($buf:expr, $i:expr) => | |
57 | ($buf[0+$i] as u64 | | |
58 | ($buf[1+$i] as u64) << 8 | | |
59 | ($buf[2+$i] as u64) << 16 | | |
60 | ($buf[3+$i] as u64) << 24 | | |
61 | ($buf[4+$i] as u64) << 32 | | |
62 | ($buf[5+$i] as u64) << 40 | | |
63 | ($buf[6+$i] as u64) << 48 | | |
64 | ($buf[7+$i] as u64) << 56); | |
65 | ($buf:expr, $i:expr, $len:expr) => | |
66 | ({ | |
67 | let mut t = 0; | |
c34b1796 | 68 | let mut out = 0; |
1a4d82fc JJ |
69 | while t < $len { |
70 | out |= ($buf[t+$i] as u64) << t*8; | |
71 | t += 1; | |
72 | } | |
73 | out | |
74 | }); | |
75 | } | |
76 | ||
c1a9b12d SL |
77 | /// Load a full u64 word from a byte stream, in LE order. Use |
78 | /// `copy_nonoverlapping` to let the compiler generate the most efficient way | |
79 | /// to load u64 from a possibly unaligned address. | |
80 | /// | |
81 | /// Unsafe because: unchecked indexing at i..i+8 | |
82 | #[inline] | |
83 | unsafe fn load_u64_le(buf: &[u8], i: usize) -> u64 { | |
84 | debug_assert!(i + 8 <= buf.len()); | |
85 | let mut data = 0u64; | |
b039eaaf | 86 | ptr::copy_nonoverlapping(buf.get_unchecked(i), &mut data as *mut _ as *mut u8, 8); |
c1a9b12d SL |
87 | data.to_le() |
88 | } | |
89 | ||
1a4d82fc JJ |
90 | macro_rules! rotl { |
91 | ($x:expr, $b:expr) => | |
c34b1796 | 92 | (($x << $b) | ($x >> (64_i32.wrapping_sub($b)))) |
1a4d82fc JJ |
93 | } |
94 | ||
95 | macro_rules! compress { | |
96 | ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => | |
97 | ({ | |
c34b1796 | 98 | $v0 = $v0.wrapping_add($v1); $v1 = rotl!($v1, 13); $v1 ^= $v0; |
1a4d82fc | 99 | $v0 = rotl!($v0, 32); |
c34b1796 AL |
100 | $v2 = $v2.wrapping_add($v3); $v3 = rotl!($v3, 16); $v3 ^= $v2; |
101 | $v0 = $v0.wrapping_add($v3); $v3 = rotl!($v3, 21); $v3 ^= $v0; | |
102 | $v2 = $v2.wrapping_add($v1); $v1 = rotl!($v1, 17); $v1 ^= $v2; | |
1a4d82fc JJ |
103 | $v2 = rotl!($v2, 32); |
104 | }) | |
105 | } | |
106 | ||
107 | impl SipHasher { | |
108 | /// Creates a new `SipHasher` with the two initial keys set to 0. | |
109 | #[inline] | |
85aaf69f | 110 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
111 | pub fn new() -> SipHasher { |
112 | SipHasher::new_with_keys(0, 0) | |
113 | } | |
114 | ||
115 | /// Creates a `SipHasher` that is keyed off the provided keys. | |
116 | #[inline] | |
85aaf69f | 117 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
118 | pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher { |
119 | let mut state = SipHasher { | |
120 | k0: key0, | |
121 | k1: key1, | |
122 | length: 0, | |
123 | v0: 0, | |
124 | v1: 0, | |
125 | v2: 0, | |
126 | v3: 0, | |
127 | tail: 0, | |
128 | ntail: 0, | |
129 | }; | |
130 | state.reset(); | |
131 | state | |
132 | } | |
133 | ||
d9579d0f | 134 | #[inline] |
85aaf69f SL |
135 | fn reset(&mut self) { |
136 | self.length = 0; | |
137 | self.v0 = self.k0 ^ 0x736f6d6570736575; | |
138 | self.v1 = self.k1 ^ 0x646f72616e646f6d; | |
139 | self.v2 = self.k0 ^ 0x6c7967656e657261; | |
140 | self.v3 = self.k1 ^ 0x7465646279746573; | |
141 | self.ntail = 0; | |
142 | } | |
e9174d1e | 143 | } |
85aaf69f | 144 | |
e9174d1e SL |
145 | #[stable(feature = "rust1", since = "1.0.0")] |
146 | impl Hasher for SipHasher { | |
d9579d0f | 147 | #[inline] |
1a4d82fc JJ |
148 | fn write(&mut self, msg: &[u8]) { |
149 | let length = msg.len(); | |
150 | self.length += length; | |
151 | ||
85aaf69f | 152 | let mut needed = 0; |
1a4d82fc JJ |
153 | |
154 | if self.ntail != 0 { | |
155 | needed = 8 - self.ntail; | |
156 | if length < needed { | |
b039eaaf | 157 | self.tail |= u8to64_le!(msg, 0, length) << 8 * self.ntail; |
1a4d82fc JJ |
158 | self.ntail += length; |
159 | return | |
160 | } | |
161 | ||
b039eaaf | 162 | let m = self.tail | u8to64_le!(msg, 0, needed) << 8 * self.ntail; |
1a4d82fc JJ |
163 | |
164 | self.v3 ^= m; | |
165 | compress!(self.v0, self.v1, self.v2, self.v3); | |
166 | compress!(self.v0, self.v1, self.v2, self.v3); | |
167 | self.v0 ^= m; | |
168 | ||
169 | self.ntail = 0; | |
170 | } | |
171 | ||
172 | // Buffered tail is now flushed, process new input. | |
173 | let len = length - needed; | |
1a4d82fc JJ |
174 | let left = len & 0x7; |
175 | ||
176 | let mut i = needed; | |
c1a9b12d SL |
177 | while i < len - left { |
178 | let mi = unsafe { load_u64_le(msg, i) }; | |
1a4d82fc JJ |
179 | |
180 | self.v3 ^= mi; | |
181 | compress!(self.v0, self.v1, self.v2, self.v3); | |
182 | compress!(self.v0, self.v1, self.v2, self.v3); | |
183 | self.v0 ^= mi; | |
184 | ||
185 | i += 8; | |
186 | } | |
187 | ||
188 | self.tail = u8to64_le!(msg, i, left); | |
189 | self.ntail = left; | |
190 | } | |
1a4d82fc | 191 | |
d9579d0f | 192 | #[inline] |
1a4d82fc JJ |
193 | fn finish(&self) -> u64 { |
194 | let mut v0 = self.v0; | |
195 | let mut v1 = self.v1; | |
196 | let mut v2 = self.v2; | |
197 | let mut v3 = self.v3; | |
198 | ||
199 | let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail; | |
200 | ||
201 | v3 ^= b; | |
202 | compress!(v0, v1, v2, v3); | |
203 | compress!(v0, v1, v2, v3); | |
204 | v0 ^= b; | |
205 | ||
206 | v2 ^= 0xff; | |
207 | compress!(v0, v1, v2, v3); | |
208 | compress!(v0, v1, v2, v3); | |
209 | compress!(v0, v1, v2, v3); | |
210 | compress!(v0, v1, v2, v3); | |
211 | ||
212 | v0 ^ v1 ^ v2 ^ v3 | |
213 | } | |
214 | } | |
215 | ||
85aaf69f | 216 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
217 | impl Clone for SipHasher { |
218 | #[inline] | |
219 | fn clone(&self) -> SipHasher { | |
220 | SipHasher { | |
221 | k0: self.k0, | |
222 | k1: self.k1, | |
223 | length: self.length, | |
224 | v0: self.v0, | |
225 | v1: self.v1, | |
226 | v2: self.v2, | |
227 | v3: self.v3, | |
228 | tail: self.tail, | |
229 | ntail: self.ntail, | |
230 | } | |
231 | } | |
232 | } | |
233 | ||
85aaf69f | 234 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
235 | impl Default for SipHasher { |
236 | fn default() -> SipHasher { | |
237 | SipHasher::new() | |
238 | } | |
239 | } |