authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-07-13 20:08:18-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-07-13 20:08:18-04:00
log7d9ee5d6d53011e9edc7ca10b76d64af807a833f
tree36ad24d05bbd2384748a4028a92a59922e64e74c
parent1547692d188ddc0c6f2dfbe4a1f943b380717306
parent69129c2e932bd51dff02578e67ea79799380a3cc
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #2882 from andersfr/hash-branch

Implementation of MurmurHash and CityHash

4 files changed, 745 insertions(+), 0 deletions(-)

CMakeLists.txt+2
...@@ -523,6 +523,8 @@ set(ZIG_STD_FILES...@@ -523,6 +523,8 @@ set(ZIG_STD_FILES
523 "hash/crc.zig"523 "hash/crc.zig"
524 "hash/fnv.zig"524 "hash/fnv.zig"
525 "hash/siphash.zig"525 "hash/siphash.zig"
526 "hash/murmur.zig"
527 "hash/cityhash.zig"
526 "hash_map.zig"528 "hash_map.zig"
527 "heap.zig"529 "heap.zig"
528 "heap/logging_allocator.zig"530 "heap/logging_allocator.zig"
std/hash.zig+11
...@@ -14,9 +14,20 @@ const siphash = @import("hash/siphash.zig");...@@ -14,9 +14,20 @@ const siphash = @import("hash/siphash.zig");
14pub const SipHash64 = siphash.SipHash64;14pub const SipHash64 = siphash.SipHash64;
15pub const SipHash128 = siphash.SipHash128;15pub const SipHash128 = siphash.SipHash128;
1616
17pub const murmur = @import("hash/murmur.zig");
18pub const Murmur2_32 = murmur.Murmur2_32;
19pub const Murmur2_64 = murmur.Murmur2_64;
20pub const Murmur3_32 = murmur.Murmur3_32;
21
22pub const cityhash = @import("hash/cityhash.zig");
23pub const CityHash32 = cityhash.CityHash32;
24pub const CityHash64 = cityhash.CityHash64;
25
17test "hash" {26test "hash" {
18 _ = @import("hash/adler.zig");27 _ = @import("hash/adler.zig");
19 _ = @import("hash/crc.zig");28 _ = @import("hash/crc.zig");
20 _ = @import("hash/fnv.zig");29 _ = @import("hash/fnv.zig");
21 _ = @import("hash/siphash.zig");30 _ = @import("hash/siphash.zig");
31 _ = @import("hash/murmur.zig");
32 _ = @import("hash/cityhash.zig");
22}33}
std/hash/cityhash.zig created+387
...@@ -0,0 +1,387 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4pub const CityHash32 = struct {
5 const Self = @This();
6
7 // Magic numbers for 32-bit hashing. Copied from Murmur3.
8 const c1: u32 = 0xcc9e2d51;
9 const c2: u32 = 0x1b873593;
10
11 fn fetch32(ptr: [*]const u8) u32 {
12 var v: u32 = undefined;
13 @memcpy(@ptrCast([*]u8, &v), ptr, 4);
14 if (builtin.endian == builtin.Endian.Big)
15 return @byteSwap(u32, v);
16 return v;
17 }
18
19 // A 32-bit to 32-bit integer hash copied from Murmur3.
20 fn fmix(h: u32) u32 {
21 var h1: u32 = h;
22 h1 ^= h1 >> 16;
23 h1 *%= 0x85ebca6b;
24 h1 ^= h1 >> 13;
25 h1 *%= 0xc2b2ae35;
26 h1 ^= h1 >> 16;
27 return h1;
28 }
29
30 // Rotate right helper
31 fn rotr32(x: u32, comptime r: u32) u32 {
32 return (x >> r) | (x << (32 - r));
33 }
34
35 // Helper from Murmur3 for combining two 32-bit values.
36 fn mur(a: u32, h: u32) u32 {
37 var a1: u32 = a;
38 var h1: u32 = h;
39 a1 *%= c1;
40 a1 = rotr32(a1, 17);
41 a1 *%= c2;
42 h1 ^= a1;
43 h1 = rotr32(h1, 19);
44 return h1 *% 5 +% 0xe6546b64;
45 }
46
47 fn hash32Len0To4(str: []const u8) u32 {
48 const len: u32 = @truncate(u32, str.len);
49 var b: u32 = 0;
50 var c: u32 = 9;
51 for (str) |v| {
52 b = b *% c1 +% @bitCast(u32, @intCast(i32, @bitCast(i8, v)));
53 c ^= b;
54 }
55 return fmix(mur(b, mur(len, c)));
56 }
57
58 fn hash32Len5To12(str: []const u8) u32 {
59 var a: u32 = @truncate(u32, str.len);
60 var b: u32 = a *% 5;
61 var c: u32 = 9;
62 const d: u32 = b;
63
64 a +%= fetch32(str.ptr);
65 b +%= fetch32(str.ptr + str.len - 4);
66 c +%= fetch32(str.ptr + ((str.len >> 1) & 4));
67
68 return fmix(mur(c, mur(b, mur(a, d))));
69 }
70
71 fn hash32Len13To24(str: []const u8) u32 {
72 const len: u32 = @truncate(u32, str.len);
73 const a: u32 = fetch32(str.ptr + (str.len >> 1) - 4);
74 const b: u32 = fetch32(str.ptr + 4);
75 const c: u32 = fetch32(str.ptr + str.len - 8);
76 const d: u32 = fetch32(str.ptr + (str.len >> 1));
77 const e: u32 = fetch32(str.ptr);
78 const f: u32 = fetch32(str.ptr + str.len - 4);
79
80 return fmix(mur(f, mur(e, mur(d, mur(c, mur(b, mur(a, len)))))));
81 }
82
83 pub fn hash(str: []const u8) u32 {
84 if (str.len <= 24) {
85 if (str.len <= 4) {
86 return hash32Len0To4(str);
87 } else {
88 if (str.len <= 12)
89 return hash32Len5To12(str);
90 return hash32Len13To24(str);
91 }
92 }
93
94 const len: u32 = @truncate(u32, str.len);
95 var h: u32 = len;
96 var g: u32 = c1 *% len;
97 var f: u32 = g;
98
99 const a0: u32 = rotr32(fetch32(str.ptr + str.len - 4) *% c1, 17) *% c2;
100 const a1: u32 = rotr32(fetch32(str.ptr + str.len - 8) *% c1, 17) *% c2;
101 const a2: u32 = rotr32(fetch32(str.ptr + str.len - 16) *% c1, 17) *% c2;
102 const a3: u32 = rotr32(fetch32(str.ptr + str.len - 12) *% c1, 17) *% c2;
103 const a4: u32 = rotr32(fetch32(str.ptr + str.len - 20) *% c1, 17) *% c2;
104
105 h ^= a0;
106 h = rotr32(h, 19);
107 h = h *% 5 +% 0xe6546b64;
108 h ^= a2;
109 h = rotr32(h, 19);
110 h = h *% 5 +% 0xe6546b64;
111 g ^= a1;
112 g = rotr32(g, 19);
113 g = g *% 5 +% 0xe6546b64;
114 g ^= a3;
115 g = rotr32(g, 19);
116 g = g *% 5 +% 0xe6546b64;
117 f +%= a4;
118 f = rotr32(f, 19);
119 f = f *% 5 +% 0xe6546b64;
120 var iters = (str.len - 1) / 20;
121 var ptr = str.ptr;
122 while (iters != 0) : (iters -= 1) {
123 const b0: u32 = rotr32(fetch32(ptr) *% c1, 17) *% c2;
124 const b1: u32 = fetch32(ptr + 4);
125 const b2: u32 = rotr32(fetch32(ptr + 8) *% c1, 17) *% c2;
126 const b3: u32 = rotr32(fetch32(ptr + 12) *% c1, 17) *% c2;
127 const b4: u32 = fetch32(ptr + 16);
128
129 h ^= b0;
130 h = rotr32(h, 18);
131 h = h *% 5 +% 0xe6546b64;
132 f +%= b1;
133 f = rotr32(f, 19);
134 f = f *% c1;
135 g +%= b2;
136 g = rotr32(g, 18);
137 g = g *% 5 +% 0xe6546b64;
138 h ^= b3 +% b1;
139 h = rotr32(h, 19);
140 h = h *% 5 +% 0xe6546b64;
141 g ^= b4;
142 g = @byteSwap(u32, g) *% 5;
143 h +%= b4 *% 5;
144 h = @byteSwap(u32, h);
145 f +%= b0;
146 const t: u32 = h;
147 h = f;
148 f = g;
149 g = t;
150 ptr += 20;
151 }
152 g = rotr32(g, 11) *% c1;
153 g = rotr32(g, 17) *% c1;
154 f = rotr32(f, 11) *% c1;
155 f = rotr32(f, 17) *% c1;
156 h = rotr32(h +% g, 19);
157 h = h *% 5 +% 0xe6546b64;
158 h = rotr32(h, 17) *% c1;
159 h = rotr32(h +% f, 19);
160 h = h *% 5 +% 0xe6546b64;
161 h = rotr32(h, 17) *% c1;
162 return h;
163 }
164};
165
166pub const CityHash64 = struct {
167 const Self = @This();
168
169 // Some primes between 2^63 and 2^64 for various uses.
170 const k0: u64 = 0xc3a5c85c97cb3127;
171 const k1: u64 = 0xb492b66fbe98f273;
172 const k2: u64 = 0x9ae16a3b2f90404f;
173
174 fn fetch32(ptr: [*]const u8) u32 {
175 var v: u32 = undefined;
176 @memcpy(@ptrCast([*]u8, &v), ptr, 4);
177 if (builtin.endian == builtin.Endian.Big)
178 return @byteSwap(u32, v);
179 return v;
180 }
181
182 fn fetch64(ptr: [*]const u8) u64 {
183 var v: u64 = undefined;
184 @memcpy(@ptrCast([*]u8, &v), ptr, 8);
185 if (builtin.endian == builtin.Endian.Big)
186 return @byteSwap(u64, v);
187 return v;
188 }
189
190 // Rotate right helper
191 fn rotr64(x: u64, comptime r: u64) u64 {
192 return (x >> r) | (x << (64 - r));
193 }
194
195 fn shiftmix(v: u64) u64 {
196 return v ^ (v >> 47);
197 }
198
199 fn hashLen16(u: u64, v: u64) u64 {
200 return @inlineCall(hash128To64, u, v);
201 }
202
203 fn hashLen16Mul(low: u64, high: u64, mul: u64) u64 {
204 var a: u64 = (low ^ high) *% mul;
205 a ^= (a >> 47);
206 var b: u64 = (high ^ a) *% mul;
207 b ^= (b >> 47);
208 b *%= mul;
209 return b;
210 }
211
212 fn hash128To64(low: u64, high: u64) u64 {
213 return @inlineCall(hashLen16Mul, low, high, 0x9ddfea08eb382d69);
214 }
215
216 fn hashLen0To16(str: []const u8) u64 {
217 const len: u64 = @truncate(u64, str.len);
218 if (len >= 8) {
219 const mul: u64 = k2 +% len *% 2;
220 const a: u64 = fetch64(str.ptr) +% k2;
221 const b: u64 = fetch64(str.ptr + str.len - 8);
222 const c: u64 = rotr64(b, 37) *% mul +% a;
223 const d: u64 = (rotr64(a, 25) +% b) *% mul;
224 return hashLen16Mul(c, d, mul);
225 }
226 if (len >= 4) {
227 const mul: u64 = k2 +% len *% 2;
228 const a: u64 = fetch32(str.ptr);
229 return hashLen16Mul(len +% (a << 3), fetch32(str.ptr + str.len - 4), mul);
230 }
231 if (len > 0) {
232 const a: u8 = str[0];
233 const b: u8 = str[str.len >> 1];
234 const c: u8 = str[str.len - 1];
235 const y: u32 = @intCast(u32, a) +% (@intCast(u32, b) << 8);
236 const z: u32 = @truncate(u32, str.len) +% (@intCast(u32, c) << 2);
237 return shiftmix(@intCast(u64, y) *% k2 ^ @intCast(u64, z) *% k0) *% k2;
238 }
239 return k2;
240 }
241
242 fn hashLen17To32(str: []const u8) u64 {
243 const len: u64 = @truncate(u64, str.len);
244 const mul: u64 = k2 +% len *% 2;
245 const a: u64 = fetch64(str.ptr) *% k1;
246 const b: u64 = fetch64(str.ptr + 8);
247 const c: u64 = fetch64(str.ptr + str.len - 8) *% mul;
248 const d: u64 = fetch64(str.ptr + str.len - 16) *% k2;
249
250 return hashLen16Mul(rotr64(a +% b, 43) +% rotr64(c, 30) +% d, a +% rotr64(b +% k2, 18) +% c, mul);
251 }
252
253 fn hashLen33To64(str: []const u8) u64 {
254 const len: u64 = @truncate(u64, str.len);
255 const mul: u64 = k2 +% len *% 2;
256 const a: u64 = fetch64(str.ptr) *% k2;
257 const b: u64 = fetch64(str.ptr + 8);
258 const c: u64 = fetch64(str.ptr + str.len - 24);
259 const d: u64 = fetch64(str.ptr + str.len - 32);
260 const e: u64 = fetch64(str.ptr + 16) *% k2;
261 const f: u64 = fetch64(str.ptr + 24) *% 9;
262 const g: u64 = fetch64(str.ptr + str.len - 8);
263 const h: u64 = fetch64(str.ptr + str.len - 16) *% mul;
264
265 const u: u64 = rotr64(a +% g, 43) +% (rotr64(b, 30) +% c) *% 9;
266 const v: u64 = ((a +% g) ^ d) +% f +% 1;
267 const w: u64 = @byteSwap(u64, (u +% v) *% mul) +% h;
268 const x: u64 = rotr64(e +% f, 42) +% c;
269 const y: u64 = (@byteSwap(u64, (v +% w) *% mul) +% g) *% mul;
270 const z: u64 = e +% f +% c;
271 const a1: u64 = @byteSwap(u64, (x +% z) *% mul +% y) +% b;
272 const b1: u64 = shiftmix((z +% a1) *% mul +% d +% h) *% mul;
273 return b1 +% x;
274 }
275
276 const WeakPair = struct {
277 first: u64,
278 second: u64,
279 };
280
281 fn weakHashLen32WithSeedsHelper(w: u64, x: u64, y: u64, z: u64, a: u64, b: u64) WeakPair {
282 var a1: u64 = a;
283 var b1: u64 = b;
284 a1 +%= w;
285 b1 = rotr64(b1 +% a1 +% z, 21);
286 var c: u64 = a1;
287 a1 +%= x;
288 a1 +%= y;
289 b1 +%= rotr64(a1, 44);
290 return WeakPair{ .first = a1 +% z, .second = b1 +% c };
291 }
292
293 fn weakHashLen32WithSeeds(ptr: [*]const u8, a: u64, b: u64) WeakPair {
294 return @inlineCall(weakHashLen32WithSeedsHelper, fetch64(ptr), fetch64(ptr + 8), fetch64(ptr + 16), fetch64(ptr + 24), a, b);
295 }
296
297 pub fn hash(str: []const u8) u64 {
298 if (str.len <= 32) {
299 if (str.len <= 16) {
300 return hashLen0To16(str);
301 } else {
302 return hashLen17To32(str);
303 }
304 } else if (str.len <= 64) {
305 return hashLen33To64(str);
306 }
307
308 var len: u64 = @truncate(u64, str.len);
309
310 var x: u64 = fetch64(str.ptr + str.len - 40);
311 var y: u64 = fetch64(str.ptr + str.len - 16) +% fetch64(str.ptr + str.len - 56);
312 var z: u64 = hashLen16(fetch64(str.ptr + str.len - 48) +% len, fetch64(str.ptr + str.len - 24));
313 var v: WeakPair = weakHashLen32WithSeeds(str.ptr + str.len - 64, len, z);
314 var w: WeakPair = weakHashLen32WithSeeds(str.ptr + str.len - 32, y +% k1, x);
315
316 x = x *% k1 +% fetch64(str.ptr);
317 len = (len - 1) & ~@intCast(u64, 63);
318
319 var ptr: [*]const u8 = str.ptr;
320 while (true) {
321 x = rotr64(x +% y +% v.first +% fetch64(ptr + 8), 37) *% k1;
322 y = rotr64(y +% v.second +% fetch64(ptr + 48), 42) *% k1;
323 x ^= w.second;
324 y +%= v.first +% fetch64(ptr + 40);
325 z = rotr64(z +% w.first, 33) *% k1;
326 v = weakHashLen32WithSeeds(ptr, v.second *% k1, x +% w.first);
327 w = weakHashLen32WithSeeds(ptr + 32, z +% w.second, y +% fetch64(ptr + 16));
328 const t: u64 = z;
329 z = x;
330 x = t;
331
332 ptr += 64;
333 len -= 64;
334 if (len == 0)
335 break;
336 }
337
338 return hashLen16(hashLen16(v.first, w.first) +% shiftmix(y) *% k1 +% z, hashLen16(v.second, w.second) +% x);
339 }
340
341 pub fn hashWithSeed(str: []const u8, seed: u64) u64 {
342 return @inlineCall(Self.hashWithSeeds, str, k2, seed);
343 }
344
345 pub fn hashWithSeeds(str: []const u8, seed0: u64, seed1: u64) u64 {
346 return hashLen16(hash(str) -% seed0, seed1);
347 }
348};
349
350fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 {
351 const hashbytes = hashbits / 8;
352 var key: [256]u8 = undefined;
353 var hashes: [hashbytes * 256]u8 = undefined;
354 var final: [hashbytes]u8 = undefined;
355
356 @memset(@ptrCast([*]u8, &key[0]), 0, @sizeOf(@typeOf(key)));
357 @memset(@ptrCast([*]u8, &hashes[0]), 0, @sizeOf(@typeOf(hashes)));
358 @memset(@ptrCast([*]u8, &final[0]), 0, @sizeOf(@typeOf(final)));
359
360 var i: u32 = 0;
361 while (i < 256) : (i += 1) {
362 key[i] = @intCast(u8, i);
363
364 var h = hash_fn(key[0..i], 256 - i);
365 if (builtin.endian == builtin.Endian.Big)
366 h = @byteSwap(@typeOf(h), h);
367 @memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes);
368 }
369
370 return @truncate(u32, hash_fn(hashes, 0));
371}
372
373fn CityHash32hashIgnoreSeed(str: []const u8, seed: u32) u32 {
374 return CityHash32.hash(str);
375}
376
377test "cityhash32" {
378 // Note: SMHasher doesn't provide a 32bit version of the algorithm.
379 // Note: The implementation was verified against the Google Abseil version.
380 std.testing.expectEqual(SMHasherTest(CityHash32hashIgnoreSeed, 32), 0x68254F81);
381}
382
383test "cityhash64" {
384 // Note: This is not compliant with the SMHasher implementation of CityHash64!
385 // Note: The implementation was verified against the Google Abseil version.
386 std.testing.expectEqual(SMHasherTest(CityHash64.hashWithSeed, 64), 0x5FABC5C5);
387}
std/hash/murmur.zig created+345
...@@ -0,0 +1,345 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const testing = std.testing;
4
5const default_seed: u32 = 0xc70f6907;
6
7pub const Murmur2_32 = struct {
8 const Self = @This();
9
10 pub fn hash(str: []const u8) u32 {
11 return @inlineCall(Self.hashWithSeed, str, default_seed);
12 }
13
14 pub fn hashWithSeed(str: []const u8, seed: u32) u32 {
15 const m: u32 = 0x5bd1e995;
16 const len = @truncate(u32, str.len);
17 var h1: u32 = seed ^ len;
18 for (@ptrCast([*]allowzero align(1) const u32, str.ptr)[0..(len >> 2)]) |v| {
19 var k1: u32 = v;
20 if (builtin.endian == builtin.Endian.Big)
21 k1 = @byteSwap(u32, k1);
22 k1 *%= m;
23 k1 ^= k1 >> 24;
24 k1 *%= m;
25 h1 *%= m;
26 h1 ^= k1;
27 }
28 const offset = len & 0xfffffffc;
29 const rest = len & 3;
30 if (rest >= 3) {
31 h1 ^= @intCast(u32, str[offset + 2]) << 16;
32 }
33 if (rest >= 2) {
34 h1 ^= @intCast(u32, str[offset + 1]) << 8;
35 }
36 if (rest >= 1) {
37 h1 ^= @intCast(u32, str[offset + 0]);
38 h1 *%= m;
39 }
40 h1 ^= h1 >> 13;
41 h1 *%= m;
42 h1 ^= h1 >> 15;
43 return h1;
44 }
45
46 pub fn hashUint32(v: u32) u32 {
47 return @inlineCall(Self.hashUint32WithSeed, v, default_seed);
48 }
49
50 pub fn hashUint32WithSeed(v: u32, seed: u32) u32 {
51 const m: u32 = 0x5bd1e995;
52 const len: u32 = 4;
53 var h1: u32 = seed ^ len;
54 var k1: u32 = undefined;
55 k1 = v *% m;
56 k1 ^= k1 >> 24;
57 k1 *%= m;
58 h1 *%= m;
59 h1 ^= k1;
60 h1 ^= h1 >> 13;
61 h1 *%= m;
62 h1 ^= h1 >> 15;
63 return h1;
64 }
65
66 pub fn hashUint64(v: u64) u32 {
67 return @inlineCall(Self.hashUint64WithSeed, v, default_seed);
68 }
69
70 pub fn hashUint64WithSeed(v: u64, seed: u32) u32 {
71 const m: u32 = 0x5bd1e995;
72 const len: u32 = 8;
73 var h1: u32 = seed ^ len;
74 var k1: u32 = undefined;
75 k1 = @truncate(u32, v) *% m;
76 k1 ^= k1 >> 24;
77 k1 *%= m;
78 h1 *%= m;
79 h1 ^= k1;
80 k1 = @truncate(u32, v >> 32) *% m;
81 k1 ^= k1 >> 24;
82 k1 *%= m;
83 h1 *%= m;
84 h1 ^= k1;
85 h1 ^= h1 >> 13;
86 h1 *%= m;
87 h1 ^= h1 >> 15;
88 return h1;
89 }
90};
91
92pub const Murmur2_64 = struct {
93 const Self = @This();
94
95 pub fn hash(str: []const u8) u64 {
96 return @inlineCall(Self.hashWithSeed, str, default_seed);
97 }
98
99 pub fn hashWithSeed(str: []const u8, seed: u64) u64 {
100 const m: u64 = 0xc6a4a7935bd1e995;
101 const len = @truncate(u64, str.len);
102 var h1: u64 = seed ^ (len *% m);
103 for (@ptrCast([*]allowzero align(1) const u64, str.ptr)[0..(len >> 3)]) |v| {
104 var k1: u64 = v;
105 if (builtin.endian == builtin.Endian.Big)
106 k1 = @byteSwap(u64, k1);
107 k1 *%= m;
108 k1 ^= k1 >> 47;
109 k1 *%= m;
110 h1 ^= k1;
111 h1 *%= m;
112 }
113 const rest = len & 7;
114 const offset = len - rest;
115 if (rest > 0) {
116 var k1: u64 = 0;
117 @memcpy(@ptrCast([*]u8, &k1), @ptrCast([*]const u8, &str[offset]), rest);
118 if (builtin.endian == builtin.Endian.Big)
119 k1 = @byteSwap(u64, k1);
120 h1 ^= k1;
121 h1 *%= m;
122 }
123 h1 ^= h1 >> 47;
124 h1 *%= m;
125 h1 ^= h1 >> 47;
126 return h1;
127 }
128
129 pub fn hashUint32(v: u32) u64 {
130 return @inlineCall(Self.hashUint32WithSeed, v, default_seed);
131 }
132
133 pub fn hashUint32WithSeed(v: u32, seed: u32) u64 {
134 const m: u64 = 0xc6a4a7935bd1e995;
135 const len: u64 = 4;
136 var h1: u64 = seed ^ (len *% m);
137 var k1: u64 = v;
138 h1 ^= k1;
139 h1 *%= m;
140 h1 ^= h1 >> 47;
141 h1 *%= m;
142 h1 ^= h1 >> 47;
143 return h1;
144 }
145
146 pub fn hashUint64(v: u64) u64 {
147 return @inlineCall(Self.hashUint64WithSeed, v, default_seed);
148 }
149
150 pub fn hashUint64WithSeed(v: u64, seed: u32) u64 {
151 const m: u64 = 0xc6a4a7935bd1e995;
152 const len: u64 = 8;
153 var h1: u64 = seed ^ (len *% m);
154 var k1: u64 = undefined;
155 k1 = v *% m;
156 k1 ^= k1 >> 47;
157 k1 *%= m;
158 h1 ^= k1;
159 h1 *%= m;
160 h1 ^= h1 >> 47;
161 h1 *%= m;
162 h1 ^= h1 >> 47;
163 return h1;
164 }
165};
166
167pub const Murmur3_32 = struct {
168 const Self = @This();
169
170 fn rotl32(x: u32, comptime r: u32) u32 {
171 return (x << r) | (x >> (32 - r));
172 }
173
174 pub fn hash(str: []const u8) u32 {
175 return @inlineCall(Self.hashWithSeed, str, default_seed);
176 }
177
178 pub fn hashWithSeed(str: []const u8, seed: u32) u32 {
179 const c1: u32 = 0xcc9e2d51;
180 const c2: u32 = 0x1b873593;
181 const len = @truncate(u32, str.len);
182 var h1: u32 = seed;
183 for (@ptrCast([*]allowzero align(1) const u32, str.ptr)[0..(len >> 2)]) |v| {
184 var k1: u32 = v;
185 if (builtin.endian == builtin.Endian.Big)
186 k1 = @byteSwap(u32, k1);
187 k1 *%= c1;
188 k1 = rotl32(k1, 15);
189 k1 *%= c2;
190 h1 ^= k1;
191 h1 = rotl32(h1, 13);
192 h1 *%= 5;
193 h1 +%= 0xe6546b64;
194 }
195 {
196 var k1: u32 = 0;
197 const offset = len & 0xfffffffc;
198 const rest = len & 3;
199 if (rest == 3) {
200 k1 ^= @intCast(u32, str[offset + 2]) << 16;
201 }
202 if (rest >= 2) {
203 k1 ^= @intCast(u32, str[offset + 1]) << 8;
204 }
205 if (rest >= 1) {
206 k1 ^= @intCast(u32, str[offset + 0]);
207 k1 *%= c1;
208 k1 = rotl32(k1, 15);
209 k1 *%= c2;
210 h1 ^= k1;
211 }
212 }
213 h1 ^= len;
214 h1 ^= h1 >> 16;
215 h1 *%= 0x85ebca6b;
216 h1 ^= h1 >> 13;
217 h1 *%= 0xc2b2ae35;
218 h1 ^= h1 >> 16;
219 return h1;
220 }
221
222 pub fn hashUint32(v: u32) u32 {
223 return @inlineCall(Self.hashUint32WithSeed, v, default_seed);
224 }
225
226 pub fn hashUint32WithSeed(v: u32, seed: u32) u32 {
227 const c1: u32 = 0xcc9e2d51;
228 const c2: u32 = 0x1b873593;
229 const len: u32 = 4;
230 var h1: u32 = seed;
231 var k1: u32 = undefined;
232 k1 = v *% c1;
233 k1 = rotl32(k1, 15);
234 k1 *%= c2;
235 h1 ^= k1;
236 h1 = rotl32(h1, 13);
237 h1 *%= 5;
238 h1 +%= 0xe6546b64;
239 h1 ^= len;
240 h1 ^= h1 >> 16;
241 h1 *%= 0x85ebca6b;
242 h1 ^= h1 >> 13;
243 h1 *%= 0xc2b2ae35;
244 h1 ^= h1 >> 16;
245 return h1;
246 }
247
248 pub fn hashUint64(v: u64) u32 {
249 return @inlineCall(Self.hashUint64WithSeed, v, default_seed);
250 }
251
252 pub fn hashUint64WithSeed(v: u64, seed: u32) u32 {
253 const c1: u32 = 0xcc9e2d51;
254 const c2: u32 = 0x1b873593;
255 const len: u32 = 8;
256 var h1: u32 = seed;
257 var k1: u32 = undefined;
258 k1 = @truncate(u32, v) *% c1;
259 k1 = rotl32(k1, 15);
260 k1 *%= c2;
261 h1 ^= k1;
262 h1 = rotl32(h1, 13);
263 h1 *%= 5;
264 h1 +%= 0xe6546b64;
265 k1 = @truncate(u32, v >> 32) *% c1;
266 k1 = rotl32(k1, 15);
267 k1 *%= c2;
268 h1 ^= k1;
269 h1 = rotl32(h1, 13);
270 h1 *%= 5;
271 h1 +%= 0xe6546b64;
272 h1 ^= len;
273 h1 ^= h1 >> 16;
274 h1 *%= 0x85ebca6b;
275 h1 ^= h1 >> 13;
276 h1 *%= 0xc2b2ae35;
277 h1 ^= h1 >> 16;
278 return h1;
279 }
280};
281
282fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 {
283 const hashbytes = hashbits / 8;
284 var key: [256]u8 = undefined;
285 var hashes: [hashbytes * 256]u8 = undefined;
286 var final: [hashbytes]u8 = undefined;
287
288 @memset(@ptrCast([*]u8, &key[0]), 0, @sizeOf(@typeOf(key)));
289 @memset(@ptrCast([*]u8, &hashes[0]), 0, @sizeOf(@typeOf(hashes)));
290 @memset(@ptrCast([*]u8, &final[0]), 0, @sizeOf(@typeOf(final)));
291
292 var i: u32 = 0;
293 while (i < 256) : (i += 1) {
294 key[i] = @truncate(u8, i);
295
296 var h = hash_fn(key[0..i], 256 - i);
297 if (builtin.endian == builtin.Endian.Big)
298 h = @byteSwap(@typeOf(h), h);
299 @memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes);
300 }
301
302 return @truncate(u32, hash_fn(hashes, 0));
303}
304
305test "murmur2_32" {
306 testing.expectEqual(SMHasherTest(Murmur2_32.hashWithSeed, 32), 0x27864C1E);
307 var v0: u32 = 0x12345678;
308 var v1: u64 = 0x1234567812345678;
309 var v0le: u32 = v0;
310 var v1le: u64 = v1;
311 if (builtin.endian == builtin.Endian.Big) {
312 v0le = @byteSwap(u32, v0le);
313 v1le = @byteSwap(u64, v1le);
314 }
315 testing.expectEqual(Murmur2_32.hash(@ptrCast([*]u8, &v0le)[0..4]), Murmur2_32.hashUint32(v0));
316 testing.expectEqual(Murmur2_32.hash(@ptrCast([*]u8, &v1le)[0..8]), Murmur2_32.hashUint64(v1));
317}
318
319test "murmur2_64" {
320 std.testing.expectEqual(SMHasherTest(Murmur2_64.hashWithSeed, 64), 0x1F0D3804);
321 var v0: u32 = 0x12345678;
322 var v1: u64 = 0x1234567812345678;
323 var v0le: u32 = v0;
324 var v1le: u64 = v1;
325 if (builtin.endian == builtin.Endian.Big) {
326 v0le = @byteSwap(u32, v0le);
327 v1le = @byteSwap(u64, v1le);
328 }
329 testing.expectEqual(Murmur2_64.hash(@ptrCast([*]u8, &v0le)[0..4]), Murmur2_64.hashUint32(v0));
330 testing.expectEqual(Murmur2_64.hash(@ptrCast([*]u8, &v1le)[0..8]), Murmur2_64.hashUint64(v1));
331}
332
333test "murmur3_32" {
334 std.testing.expectEqual(SMHasherTest(Murmur3_32.hashWithSeed, 32), 0xB0F57EE3);
335 var v0: u32 = 0x12345678;
336 var v1: u64 = 0x1234567812345678;
337 var v0le: u32 = v0;
338 var v1le: u64 = v1;
339 if (builtin.endian == builtin.Endian.Big) {
340 v0le = @byteSwap(u32, v0le);
341 v1le = @byteSwap(u64, v1le);
342 }
343 testing.expectEqual(Murmur3_32.hash(@ptrCast([*]u8, &v0le)[0..4]), Murmur3_32.hashUint32(v0));
344 testing.expectEqual(Murmur3_32.hash(@ptrCast([*]u8, &v1le)[0..8]), Murmur3_32.hashUint64(v1));
345}