The Pedigree Project 0.1
spooky.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20// Spooky Hash
21// A 128-bit noncryptographic hash, for checksums and table lookup
22// By Bob Jenkins. Public domain.
23// Oct 31 2010: published framework, disclaimer ShortHash isn't right
24// Nov 7 2010: disabled ShortHash
25// Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again
26// April 10 2012: buffer overflow on platforms without unaligned reads
27// July 12 2012: was passing out variables in final to in/out in short
28// July 30 2012: I reintroduced the buffer overflow
29// August 5 2012: SpookyV2: d = should be d += in short hash, and remove extra mix from long hash
30
31#include "pedigree/kernel/utilities/spooky/SpookyV2.h"
32#include "pedigree/kernel/utilities/utility.h"
33
34#define memcpy MemoryCopy
35#define memset ByteSet
36
37#define ALLOW_UNALIGNED_READS 1
38
39//
40// short hash ... it could be used on any message,
41// but it's used by Spooky just for short messages.
42//
43void SpookyHash::Short(const void* message, size_t length, uint64* hash1, uint64* hash2) {
44 uint64 buf[2 * sc_numVars];
45 union {
46 const uint8* p8;
47 uint32* p32;
48 uint64* p64;
49 size_t i;
50 } u;
51
52 u.p8 = (const uint8*)message;
53
54 if (!ALLOW_UNALIGNED_READS && (u.i & 0x7)) {
55 memcpy(buf, message, length);
56 u.p64 = buf;
57 }
58
59 size_t remainder = length % 32;
60 uint64 a = *hash1;
61 uint64 b = *hash2;
62 uint64 c = sc_const;
63 uint64 d = sc_const;
64
65 if (length > 15) {
66 const uint64* end = u.p64 + (length / 32) * 4;
67
68 // handle all complete sets of 32 bytes
69 for (; u.p64 < end; u.p64 += 4) {
70 c += u.p64[0];
71 d += u.p64[1];
72 ShortMix(a, b, c, d);
73 a += u.p64[2];
74 b += u.p64[3];
75 }
76
77 // Handle the case of 16+ remaining bytes.
78 if (remainder >= 16) {
79 c += u.p64[0];
80 d += u.p64[1];
81 ShortMix(a, b, c, d);
82 u.p64 += 2;
83 remainder -= 16;
84 }
85 }
86
87 // Handle the last 0..15 bytes, and its length
88 d += ((uint64)length) << 56;
89 switch (remainder) {
90 case 15:
91 d += ((uint64)u.p8[14]) << 48;
92 [[fallthrough]];
93 case 14:
94 d += ((uint64)u.p8[13]) << 40;
95 [[fallthrough]];
96 case 13:
97 d += ((uint64)u.p8[12]) << 32;
98 [[fallthrough]];
99 case 12:
100 d += u.p32[2];
101 c += u.p64[0];
102 break;
103 case 11:
104 d += ((uint64)u.p8[10]) << 16;
105 [[fallthrough]];
106 case 10:
107 d += ((uint64)u.p8[9]) << 8;
108 [[fallthrough]];
109 case 9:
110 d += (uint64)u.p8[8];
111 [[fallthrough]];
112 case 8:
113 c += u.p64[0];
114 break;
115 case 7:
116 c += ((uint64)u.p8[6]) << 48;
117 [[fallthrough]];
118 case 6:
119 c += ((uint64)u.p8[5]) << 40;
120 [[fallthrough]];
121 case 5:
122 c += ((uint64)u.p8[4]) << 32;
123 [[fallthrough]];
124 case 4:
125 c += u.p32[0];
126 break;
127 case 3:
128 c += ((uint64)u.p8[2]) << 16;
129 [[fallthrough]];
130 case 2:
131 c += ((uint64)u.p8[1]) << 8;
132 [[fallthrough]];
133 case 1:
134 c += (uint64)u.p8[0];
135 break;
136 case 0:
137 c += sc_const;
138 d += sc_const;
139 }
140 ShortEnd(a, b, c, d);
141 *hash1 = a;
142 *hash2 = b;
143}
144
145// do the whole hash in one call
146void SpookyHash::Hash128(const void* message, size_t length, uint64* hash1, uint64* hash2) {
147 if (length < sc_bufSize) {
148 Short(message, length, hash1, hash2);
149 return;
150 }
151
152 uint64 h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11;
153 uint64 buf[sc_numVars];
154 uint64* end;
155 union {
156 const uint8* p8;
157 uint64* p64;
158 size_t i;
159 } u;
160 size_t remainder;
161
162 h0 = h3 = h6 = h9 = *hash1;
163 h1 = h4 = h7 = h10 = *hash2;
164 h2 = h5 = h8 = h11 = sc_const;
165
166 u.p8 = (const uint8*)message;
167 end = u.p64 + (length / sc_blockSize) * sc_numVars;
168
169 // handle all whole sc_blockSize blocks of bytes
170 if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0)) {
171 while (u.p64 < end) {
172 Mix(u.p64, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
173 u.p64 += sc_numVars;
174 }
175 } else {
176 while (u.p64 < end) {
177 memcpy(buf, u.p64, sc_blockSize);
178 Mix(buf, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
179 u.p64 += sc_numVars;
180 }
181 }
182
183 // handle the last partial block of sc_blockSize bytes
184 remainder = (length - ((const uint8*)end - (const uint8*)message));
185 memcpy(buf, end, remainder);
186 memset(((uint8*)buf) + remainder, 0, sc_blockSize - remainder);
187 ((uint8*)buf)[sc_blockSize - 1] = remainder;
188
189 // do some final mixing
190 End(buf, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
191 *hash1 = h0;
192 *hash2 = h1;
193}
194
195// init spooky state
196void SpookyHash::Init(uint64 seed1, uint64 seed2) {
197 m_length = 0;
198 m_remainder = 0;
199 m_state[0] = seed1;
200 m_state[1] = seed2;
201}
202
203// add a message fragment to the state
204void SpookyHash::Update(const void* message, size_t length) {
205 uint64 h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11;
206 size_t newLength = length + m_remainder;
207 uint8 remainder;
208 union {
209 const uint8* p8;
210 uint64* p64;
211 size_t i;
212 } u;
213 const uint64* end;
214
215 // Is this message fragment too short? If it is, stuff it away.
216 if (newLength < sc_bufSize) {
217 memcpy(&((uint8*)m_data)[m_remainder], message, length);
218 m_length = length + m_length;
219 m_remainder = (uint8)newLength;
220 return;
221 }
222
223 // init the variables
224 if (m_length < sc_bufSize) {
225 h0 = h3 = h6 = h9 = m_state[0];
226 h1 = h4 = h7 = h10 = m_state[1];
227 h2 = h5 = h8 = h11 = sc_const;
228 } else {
229 h0 = m_state[0];
230 h1 = m_state[1];
231 h2 = m_state[2];
232 h3 = m_state[3];
233 h4 = m_state[4];
234 h5 = m_state[5];
235 h6 = m_state[6];
236 h7 = m_state[7];
237 h8 = m_state[8];
238 h9 = m_state[9];
239 h10 = m_state[10];
240 h11 = m_state[11];
241 }
242 m_length = length + m_length;
243
244 // if we've got anything stuffed away, use it now
245 if (m_remainder) {
246 uint8 prefix = sc_bufSize - m_remainder;
247 memcpy(&(((uint8*)m_data)[m_remainder]), message, prefix);
248 u.p64 = m_data;
249 Mix(u.p64, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
250 Mix(&u.p64[sc_numVars], h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
251 u.p8 = ((const uint8*)message) + prefix;
252 length -= prefix;
253 } else {
254 u.p8 = (const uint8*)message;
255 }
256
257 // handle all whole blocks of sc_blockSize bytes
258 end = u.p64 + (length / sc_blockSize) * sc_numVars;
259 remainder = (uint8)(length - ((const uint8*)end - u.p8));
260 if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0) {
261 while (u.p64 < end) {
262 Mix(u.p64, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
263 u.p64 += sc_numVars;
264 }
265 } else {
266 while (u.p64 < end) {
267 memcpy(m_data, u.p8, sc_blockSize);
268 Mix(m_data, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
269 u.p64 += sc_numVars;
270 }
271 }
272
273 // stuff away the last few bytes
274 m_remainder = remainder;
275 memcpy(m_data, end, remainder);
276
277 // stuff away the variables
278 m_state[0] = h0;
279 m_state[1] = h1;
280 m_state[2] = h2;
281 m_state[3] = h3;
282 m_state[4] = h4;
283 m_state[5] = h5;
284 m_state[6] = h6;
285 m_state[7] = h7;
286 m_state[8] = h8;
287 m_state[9] = h9;
288 m_state[10] = h10;
289 m_state[11] = h11;
290}
291
292// report the hash for the concatenation of all message fragments so far
293void SpookyHash::Final(uint64* hash1, uint64* hash2) {
294 // init the variables
295 if (m_length < sc_bufSize) {
296 *hash1 = m_state[0];
297 *hash2 = m_state[1];
298 Short(m_data, m_length, hash1, hash2);
299 return;
300 }
301
302 uint64* data = (uint64*)m_data;
303 uint8 remainder = m_remainder;
304
305 uint64 h0 = m_state[0];
306 uint64 h1 = m_state[1];
307 uint64 h2 = m_state[2];
308 uint64 h3 = m_state[3];
309 uint64 h4 = m_state[4];
310 uint64 h5 = m_state[5];
311 uint64 h6 = m_state[6];
312 uint64 h7 = m_state[7];
313 uint64 h8 = m_state[8];
314 uint64 h9 = m_state[9];
315 uint64 h10 = m_state[10];
316 uint64 h11 = m_state[11];
317
318 if (remainder >= sc_blockSize) {
319 // m_data can contain two blocks; handle any whole first block
320 Mix(data, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
321 data += sc_numVars;
322 remainder -= sc_blockSize;
323 }
324
325 // mix in the last partial block, and the length mod sc_blockSize
326 memset(&((uint8*)data)[remainder], 0, (sc_blockSize - remainder));
327
328 ((uint8*)data)[sc_blockSize - 1] = remainder;
329
330 // do some final mixing
331 End(data, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11);
332
333 *hash1 = h0;
334 *hash2 = h1;
335}