authorgravatar for andersfr@gmail.comandersfr <andersfr@gmail.com> 2019-07-06 15:12:07+02:00
committergravatar for andersfr@gmail.comandersfr <andersfr@gmail.com> 2019-07-13 13:59:15+02:00
logdfbe65e8cd62625c5ef783aee01056bac678ecf1
treec6994cf7731cbd8e7a8f244c59bb2b2dc807d630
parentbdfb31420a1046178406f40e867811de8d65d94a

Ported CityHash and Murmur hashing algorithms to native zig


3 files changed, 720 insertions(+), 0 deletions(-)

std/hash.zig+11
......@@ -14,9 +14,20 @@ const siphash = @import("hash/siphash.zig");
1414pub const SipHash64 = siphash.SipHash64;
1515pub 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
1726test "hash" {
1827 _ = @import("hash/adler.zig");
1928 _ = @import("hash/crc.zig");
2029 _ = @import("hash/fnv.zig");
2130 _ = @import("hash/siphash.zig");
31 _ = @import("hash/murmur.zig");
32 _ = @import("hash/cityhash.zig");
2233}
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 = @intCast(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 = @intCast(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 = @intCast(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 = @intCast(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 = @intCast(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 = @intCast(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 = @intCast(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 = @intCast(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 = @intCast(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 @intCast(u32, hash_fn(hashes, 0) & 0xffffffff);
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+322
......@@ -0,0 +1,322 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4const default_seed: u32 = 0xc70f6907;
5
6pub const Murmur2_32 = struct {
7 const Self = @This();
8
9 pub fn hash(str: []const u8) u32 {
10 return @inlineCall(Self.hashWithSeed, str, default_seed);
11 }
12
13 pub fn hashWithSeed(str: []const u8, seed: u32) u32 {
14 const m: u32 = 0x5bd1e995;
15 const len = @intCast(u32, str.len);
16 var h1: u32 = seed ^ len;
17 for (@ptrCast([*]allowzero align(1) const u32, str.ptr)[0..(len >> 2)]) |v| {
18 var k1: u32 = v;
19 if (builtin.endian == builtin.Endian.Big)
20 k1 = @byteSwap(u32, k1);
21 k1 *%= m;
22 k1 ^= k1 >> 24;
23 k1 *%= m;
24 h1 *%= m;
25 h1 ^= k1;
26 }
27 const offset = len & 0xfffffffc;
28 const rest = len & 3;
29 if (rest >= 3) {
30 h1 ^= @intCast(u32, str[offset + 2]) << 16;
31 }
32 if (rest >= 2) {
33 h1 ^= @intCast(u32, str[offset + 1]) << 8;
34 }
35 if (rest >= 1) {
36 h1 ^= @intCast(u32, str[offset + 0]);
37 h1 *%= m;
38 }
39 h1 ^= h1 >> 13;
40 h1 *%= m;
41 h1 ^= h1 >> 15;
42 return h1;
43 }
44
45 pub fn hashUint32(v: u32) u32 {
46 return @inlineCall(Self.hashUint32WithSeed, v, default_seed);
47 }
48
49 pub fn hashUint32WithSeed(v: u32, seed: u32) u32 {
50 const m: u32 = 0x5bd1e995;
51 const len: u32 = 4;
52 var h1: u32 = seed ^ len;
53 var k1: u32 = undefined;
54 k1 = v *% m;
55 k1 ^= k1 >> 24;
56 k1 *%= m;
57 h1 *%= m;
58 h1 ^= k1;
59 h1 ^= h1 >> 13;
60 h1 *%= m;
61 h1 ^= h1 >> 15;
62 return h1;
63 }
64
65 pub fn hashUint64(v: u64) u32 {
66 return @inlineCall(Self.hashUint64WithSeed, v, default_seed);
67 }
68
69 pub fn hashUint64WithSeed(v: u64, seed: u32) u32 {
70 const m: u32 = 0x5bd1e995;
71 const len: u32 = 4;
72 var h1: u32 = seed ^ len;
73 var k1: u32 = undefined;
74 k1 = @intCast(u32, v) *% m;
75 k1 ^= k1 >> 24;
76 k1 *%= m;
77 h1 *%= m;
78 h1 ^= k1;
79 k1 = @intCast(u32, v >> 32) *% m;
80 k1 ^= k1 >> 24;
81 k1 *%= m;
82 h1 *%= m;
83 h1 ^= k1;
84 h1 ^= h1 >> 13;
85 h1 *%= m;
86 h1 ^= h1 >> 15;
87 return h1;
88 }
89};
90
91pub const Murmur2_64 = struct {
92 const Self = @This();
93
94 pub fn hash(str: []const u8) u64 {
95 return @inlineCall(Self.hashWithSeed, str, default_seed);
96 }
97
98 pub fn hashWithSeed(str: []const u8, seed: u64) u64 {
99 const m: u64 = 0xc6a4a7935bd1e995;
100 const len = @intCast(u64, str.len);
101 var h1: u64 = seed ^ (len *% m);
102 for (@ptrCast([*]allowzero align(1) const u64, str.ptr)[0..(len >> 3)]) |v| {
103 var k1: u64 = v;
104 if (builtin.endian == builtin.Endian.Big)
105 k1 = @byteSwap(u64, k1);
106 k1 *%= m;
107 k1 ^= k1 >> 47;
108 k1 *%= m;
109 h1 ^= k1;
110 h1 *%= m;
111 }
112 const rest = len & 7;
113 const offset = len - rest;
114 if (rest > 0) {
115 var k1: u64 = 0;
116 @memcpy(@ptrCast([*]u8, &k1), @ptrCast([*]const u8, &str[offset]), rest);
117 if (builtin.endian == builtin.Endian.Big)
118 k1 = @byteSwap(u64, k1);
119 h1 ^= k1;
120 h1 *%= m;
121 }
122 h1 ^= h1 >> 47;
123 h1 *%= m;
124 h1 ^= h1 >> 47;
125 return h1;
126 }
127
128 pub fn hashUint32(v: u32) u64 {
129 return @inlineCall(Self.hashUint32WithSeed, v, default_seed);
130 }
131
132 pub fn hashUint32WithSeed(v: u32, seed: u32) u64 {
133 const m: u64 = 0xc6a4a7935bd1e995;
134 const len: u64 = 4;
135 var h1: u64 = seed ^ (len *% m);
136 var k1: u64 = undefined;
137 k1 = v *% m;
138 k1 ^= k1 >> 47;
139 k1 *%= m;
140 h1 ^= k1;
141 h1 *%= m;
142 h1 ^= h1 >> 47;
143 h1 *%= m;
144 h1 ^= h1 >> 47;
145 return h1;
146 }
147
148 pub fn hashUint64(v: u64) u64 {
149 return @inlineCall(Self.hashUint64WithSeed, v, default_seed);
150 }
151
152 pub fn hashUint64WithSeed(v: u64, seed: u32) u64 {
153 const m: u64 = 0xc6a4a7935bd1e995;
154 const len: u64 = 8;
155 var h1: u64 = seed ^ (len *% m);
156 var k1: u64 = undefined;
157 k1 = @intCast(u32, v) *% m;
158 k1 ^= k1 >> 47;
159 k1 *%= m;
160 h1 ^= k1;
161 h1 *%= m;
162 k1 = @intCast(u32, v >> 32) *% m;
163 k1 ^= k1 >> 47;
164 k1 *%= m;
165 h1 ^= k1;
166 h1 *%= m;
167 h1 ^= h1 >> 47;
168 h1 *%= m;
169 h1 ^= h1 >> 47;
170 return h1;
171 }
172};
173
174pub const Murmur3_32 = struct {
175 const Self = @This();
176
177 fn rotl32(x: u32, comptime r: u32) u32 {
178 return (x << r) | (x >> (32 - r));
179 }
180
181 pub fn hash(str: []const u8) u32 {
182 return @inlineCall(Self.hashWithSeed, str, default_seed);
183 }
184
185 pub fn hashWithSeed(str: []const u8, seed: u32) u32 {
186 const c1: u32 = 0xcc9e2d51;
187 const c2: u32 = 0x1b873593;
188 const len = @intCast(u32, str.len);
189 var h1: u32 = seed;
190 for (@ptrCast([*]allowzero align(1) const u32, str.ptr)[0..(len >> 2)]) |v| {
191 var k1: u32 = v;
192 if (builtin.endian == builtin.Endian.Big)
193 k1 = @byteSwap(u32, k1);
194 k1 *%= c1;
195 k1 = rotl32(k1, 15);
196 k1 *%= c2;
197 h1 ^= k1;
198 h1 = rotl32(h1, 13);
199 h1 *%= 5;
200 h1 +%= 0xe6546b64;
201 }
202 {
203 var k1: u32 = 0;
204 const offset = len & 0xfffffffc;
205 const rest = len & 3;
206 if (rest == 3) {
207 k1 ^= @intCast(u32, str[offset + 2]) << 16;
208 }
209 if (rest >= 2) {
210 k1 ^= @intCast(u32, str[offset + 1]) << 8;
211 }
212 if (rest >= 1) {
213 k1 ^= @intCast(u32, str[offset + 0]);
214 k1 *%= c1;
215 k1 = rotl32(k1, 15);
216 k1 *%= c2;
217 h1 ^= k1;
218 }
219 }
220 h1 ^= len;
221 h1 ^= h1 >> 16;
222 h1 *%= 0x85ebca6b;
223 h1 ^= h1 >> 13;
224 h1 *%= 0xc2b2ae35;
225 h1 ^= h1 >> 16;
226 return h1;
227 }
228
229 pub fn hashUint32(v: u32) u32 {
230 return @inlineCall(Self.hashUint32WithSeed, v, default_seed);
231 }
232
233 pub fn hashUint32WithSeed(v: u32, seed: u32) u32 {
234 const c1: u32 = 0xcc9e2d51;
235 const c2: u32 = 0x1b873593;
236 const len: u32 = 4;
237 var h1: u32 = seed;
238 var k1: u32 = undefined;
239 k1 = v *% c1;
240 k1 = rotl32(k1, 15);
241 k1 *%= c2;
242 h1 ^= k1;
243 h1 = rotl32(h1, 13);
244 h1 *%= 5;
245 h1 +%= 0xe6546b64;
246 h1 ^= len;
247 h1 ^= h1 >> 16;
248 h1 *%= 0x85ebca6b;
249 h1 ^= h1 >> 13;
250 h1 *%= 0xc2b2ae35;
251 h1 ^= h1 >> 16;
252 return h1;
253 }
254
255 pub fn hashUint64(v: u64) u32 {
256 return @inlineCall(Self.hashUint64WithSeed, v, default_seed);
257 }
258
259 pub fn hashUint64WithSeed(v: u64, seed: u32) u32 {
260 const c1: u32 = 0xcc9e2d51;
261 const c2: u32 = 0x1b873593;
262 const len: u32 = 8;
263 var h1: u32 = seed;
264 var k1: u32 = undefined;
265 k1 = @intCast(u32, v) *% c1;
266 k1 = rotl32(k1, 15);
267 k1 *%= c2;
268 h1 ^= k1;
269 h1 = rotl32(h1, 13);
270 h1 *%= 5;
271 h1 +%= 0xe6546b64;
272 k1 = @intCast(u32, v >> 32) *% c1;
273 k1 = rotl32(k1, 15);
274 k1 *%= c2;
275 h1 ^= k1;
276 h1 = rotl32(h1, 13);
277 h1 *%= 5;
278 h1 +%= 0xe6546b64;
279 h1 ^= len;
280 h1 ^= h1 >> 16;
281 h1 *%= 0x85ebca6b;
282 h1 ^= h1 >> 13;
283 h1 *%= 0xc2b2ae35;
284 h1 ^= h1 >> 16;
285 return h1;
286 }
287};
288
289fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 {
290 const hashbytes = hashbits / 8;
291 var key: [256]u8 = undefined;
292 var hashes: [hashbytes * 256]u8 = undefined;
293 var final: [hashbytes]u8 = undefined;
294
295 @memset(@ptrCast([*]u8, &key[0]), 0, @sizeOf(@typeOf(key)));
296 @memset(@ptrCast([*]u8, &hashes[0]), 0, @sizeOf(@typeOf(hashes)));
297 @memset(@ptrCast([*]u8, &final[0]), 0, @sizeOf(@typeOf(final)));
298
299 var i: u32 = 0;
300 while (i < 256) : (i += 1) {
301 key[i] = @intCast(u8, i);
302
303 var h = hash_fn(key[0..i], 256 - i);
304 if (builtin.endian == builtin.Endian.Big)
305 h = @byteSwap(@typeOf(h), h);
306 @memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes);
307 }
308
309 return @intCast(u32, hash_fn(hashes, 0) & 0xffffffff);
310}
311
312test "murmur2_32" {
313 std.testing.expectEqual(SMHasherTest(Murmur2_32.hashWithSeed, 32), 0x27864C1E);
314}
315
316test "murmur2_64" {
317 std.testing.expectEqual(SMHasherTest(Murmur2_64.hashWithSeed, 64), 0x1F0D3804);
318}
319
320test "murmur3_32" {
321 std.testing.expectEqual(SMHasherTest(Murmur3_32.hashWithSeed, 32), 0xB0F57EE3);
322}