authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-11 13:58:31-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-11 13:58:31-07:00
log5cc2e500e68a60cb99b48755af39856ff60198b6
treeb00e28d6f15de11b468bea413562b8d45c24f46e
parent73b17474d7ff620fa68434e233b472269bdca2b4
parent0c8e2c987d59c81655c139b7d2d822cc95871140

Merge branch 'SpexGuy-fix-comptime-cityhash'

I commented out the comptime test though since it was causing OOM on the CI server. closes #7331

1 files changed, 105 insertions(+), 93 deletions(-)

lib/std/hash/cityhash.zig+105-93
...@@ -6,6 +6,19 @@...@@ -6,6 +6,19 @@
6const std = @import("std");6const std = @import("std");
7const builtin = @import("builtin");7const builtin = @import("builtin");
88
9inline fn offsetPtr(ptr: [*]const u8, offset: usize) [*]const u8 {
10 // ptr + offset doesn't work at comptime so we need this instead.
11 return @ptrCast([*]const u8, &ptr[offset]);
12}
13
14fn fetch32(ptr: [*]const u8, offset: usize) u32 {
15 return std.mem.readIntLittle(u32, offsetPtr(ptr, offset)[0..4]);
16}
17
18fn fetch64(ptr: [*]const u8, offset: usize) u64 {
19 return std.mem.readIntLittle(u64, offsetPtr(ptr, offset)[0..8]);
20}
21
9pub const CityHash32 = struct {22pub const CityHash32 = struct {
10 const Self = @This();23 const Self = @This();
1124
...@@ -13,14 +26,6 @@ pub const CityHash32 = struct {...@@ -13,14 +26,6 @@ pub const CityHash32 = struct {
13 const c1: u32 = 0xcc9e2d51;26 const c1: u32 = 0xcc9e2d51;
14 const c2: u32 = 0x1b873593;27 const c2: u32 = 0x1b873593;
1528
16 fn fetch32(ptr: [*]const u8) u32 {
17 var v: u32 = undefined;
18 @memcpy(@ptrCast([*]u8, &v), ptr, 4);
19 if (builtin.endian == .Big)
20 return @byteSwap(u32, v);
21 return v;
22 }
23
24 // A 32-bit to 32-bit integer hash copied from Murmur3.29 // A 32-bit to 32-bit integer hash copied from Murmur3.
25 fn fmix(h: u32) u32 {30 fn fmix(h: u32) u32 {
26 var h1: u32 = h;31 var h1: u32 = h;
...@@ -66,21 +71,21 @@ pub const CityHash32 = struct {...@@ -66,21 +71,21 @@ pub const CityHash32 = struct {
66 var c: u32 = 9;71 var c: u32 = 9;
67 const d: u32 = b;72 const d: u32 = b;
6873
69 a +%= fetch32(str.ptr);74 a +%= fetch32(str.ptr, 0);
70 b +%= fetch32(str.ptr + str.len - 4);75 b +%= fetch32(str.ptr, str.len - 4);
71 c +%= fetch32(str.ptr + ((str.len >> 1) & 4));76 c +%= fetch32(str.ptr, (str.len >> 1) & 4);
7277
73 return fmix(mur(c, mur(b, mur(a, d))));78 return fmix(mur(c, mur(b, mur(a, d))));
74 }79 }
7580
76 fn hash32Len13To24(str: []const u8) u32 {81 fn hash32Len13To24(str: []const u8) u32 {
77 const len: u32 = @truncate(u32, str.len);82 const len: u32 = @truncate(u32, str.len);
78 const a: u32 = fetch32(str.ptr + (str.len >> 1) - 4);83 const a: u32 = fetch32(str.ptr, (str.len >> 1) - 4);
79 const b: u32 = fetch32(str.ptr + 4);84 const b: u32 = fetch32(str.ptr, 4);
80 const c: u32 = fetch32(str.ptr + str.len - 8);85 const c: u32 = fetch32(str.ptr, str.len - 8);
81 const d: u32 = fetch32(str.ptr + (str.len >> 1));86 const d: u32 = fetch32(str.ptr, str.len >> 1);
82 const e: u32 = fetch32(str.ptr);87 const e: u32 = fetch32(str.ptr, 0);
83 const f: u32 = fetch32(str.ptr + str.len - 4);88 const f: u32 = fetch32(str.ptr, str.len - 4);
8489
85 return fmix(mur(f, mur(e, mur(d, mur(c, mur(b, mur(a, len)))))));90 return fmix(mur(f, mur(e, mur(d, mur(c, mur(b, mur(a, len)))))));
86 }91 }
...@@ -101,11 +106,11 @@ pub const CityHash32 = struct {...@@ -101,11 +106,11 @@ pub const CityHash32 = struct {
101 var g: u32 = c1 *% len;106 var g: u32 = c1 *% len;
102 var f: u32 = g;107 var f: u32 = g;
103108
104 const a0: u32 = rotr32(fetch32(str.ptr + str.len - 4) *% c1, 17) *% c2;109 const a0: u32 = rotr32(fetch32(str.ptr, str.len - 4) *% c1, 17) *% c2;
105 const a1: u32 = rotr32(fetch32(str.ptr + str.len - 8) *% c1, 17) *% c2;110 const a1: u32 = rotr32(fetch32(str.ptr, str.len - 8) *% c1, 17) *% c2;
106 const a2: u32 = rotr32(fetch32(str.ptr + str.len - 16) *% c1, 17) *% c2;111 const a2: u32 = rotr32(fetch32(str.ptr, str.len - 16) *% c1, 17) *% c2;
107 const a3: u32 = rotr32(fetch32(str.ptr + str.len - 12) *% c1, 17) *% c2;112 const a3: u32 = rotr32(fetch32(str.ptr, str.len - 12) *% c1, 17) *% c2;
108 const a4: u32 = rotr32(fetch32(str.ptr + str.len - 20) *% c1, 17) *% c2;113 const a4: u32 = rotr32(fetch32(str.ptr, str.len - 20) *% c1, 17) *% c2;
109114
110 h ^= a0;115 h ^= a0;
111 h = rotr32(h, 19);116 h = rotr32(h, 19);
...@@ -125,11 +130,11 @@ pub const CityHash32 = struct {...@@ -125,11 +130,11 @@ pub const CityHash32 = struct {
125 var iters = (str.len - 1) / 20;130 var iters = (str.len - 1) / 20;
126 var ptr = str.ptr;131 var ptr = str.ptr;
127 while (iters != 0) : (iters -= 1) {132 while (iters != 0) : (iters -= 1) {
128 const b0: u32 = rotr32(fetch32(ptr) *% c1, 17) *% c2;133 const b0: u32 = rotr32(fetch32(ptr, 0) *% c1, 17) *% c2;
129 const b1: u32 = fetch32(ptr + 4);134 const b1: u32 = fetch32(ptr, 4);
130 const b2: u32 = rotr32(fetch32(ptr + 8) *% c1, 17) *% c2;135 const b2: u32 = rotr32(fetch32(ptr, 8) *% c1, 17) *% c2;
131 const b3: u32 = rotr32(fetch32(ptr + 12) *% c1, 17) *% c2;136 const b3: u32 = rotr32(fetch32(ptr, 12) *% c1, 17) *% c2;
132 const b4: u32 = fetch32(ptr + 16);137 const b4: u32 = fetch32(ptr, 16);
133138
134 h ^= b0;139 h ^= b0;
135 h = rotr32(h, 18);140 h = rotr32(h, 18);
...@@ -152,7 +157,7 @@ pub const CityHash32 = struct {...@@ -152,7 +157,7 @@ pub const CityHash32 = struct {
152 h = f;157 h = f;
153 f = g;158 f = g;
154 g = t;159 g = t;
155 ptr += 20;160 ptr = offsetPtr(ptr, 20);
156 }161 }
157 g = rotr32(g, 11) *% c1;162 g = rotr32(g, 11) *% c1;
158 g = rotr32(g, 17) *% c1;163 g = rotr32(g, 17) *% c1;
...@@ -176,22 +181,6 @@ pub const CityHash64 = struct {...@@ -176,22 +181,6 @@ pub const CityHash64 = struct {
176 const k1: u64 = 0xb492b66fbe98f273;181 const k1: u64 = 0xb492b66fbe98f273;
177 const k2: u64 = 0x9ae16a3b2f90404f;182 const k2: u64 = 0x9ae16a3b2f90404f;
178183
179 fn fetch32(ptr: [*]const u8) u32 {
180 var v: u32 = undefined;
181 @memcpy(@ptrCast([*]u8, &v), ptr, 4);
182 if (builtin.endian == .Big)
183 return @byteSwap(u32, v);
184 return v;
185 }
186
187 fn fetch64(ptr: [*]const u8) u64 {
188 var v: u64 = undefined;
189 @memcpy(@ptrCast([*]u8, &v), ptr, 8);
190 if (builtin.endian == .Big)
191 return @byteSwap(u64, v);
192 return v;
193 }
194
195 // Rotate right helper184 // Rotate right helper
196 fn rotr64(x: u64, comptime r: u64) u64 {185 fn rotr64(x: u64, comptime r: u64) u64 {
197 return (x >> r) | (x << (64 - r));186 return (x >> r) | (x << (64 - r));
...@@ -222,16 +211,16 @@ pub const CityHash64 = struct {...@@ -222,16 +211,16 @@ pub const CityHash64 = struct {
222 const len: u64 = @as(u64, str.len);211 const len: u64 = @as(u64, str.len);
223 if (len >= 8) {212 if (len >= 8) {
224 const mul: u64 = k2 +% len *% 2;213 const mul: u64 = k2 +% len *% 2;
225 const a: u64 = fetch64(str.ptr) +% k2;214 const a: u64 = fetch64(str.ptr, 0) +% k2;
226 const b: u64 = fetch64(str.ptr + str.len - 8);215 const b: u64 = fetch64(str.ptr, str.len - 8);
227 const c: u64 = rotr64(b, 37) *% mul +% a;216 const c: u64 = rotr64(b, 37) *% mul +% a;
228 const d: u64 = (rotr64(a, 25) +% b) *% mul;217 const d: u64 = (rotr64(a, 25) +% b) *% mul;
229 return hashLen16Mul(c, d, mul);218 return hashLen16Mul(c, d, mul);
230 }219 }
231 if (len >= 4) {220 if (len >= 4) {
232 const mul: u64 = k2 +% len *% 2;221 const mul: u64 = k2 +% len *% 2;
233 const a: u64 = fetch32(str.ptr);222 const a: u64 = fetch32(str.ptr, 0);
234 return hashLen16Mul(len +% (a << 3), fetch32(str.ptr + str.len - 4), mul);223 return hashLen16Mul(len +% (a << 3), fetch32(str.ptr, str.len - 4), mul);
235 }224 }
236 if (len > 0) {225 if (len > 0) {
237 const a: u8 = str[0];226 const a: u8 = str[0];
...@@ -247,10 +236,10 @@ pub const CityHash64 = struct {...@@ -247,10 +236,10 @@ pub const CityHash64 = struct {
247 fn hashLen17To32(str: []const u8) u64 {236 fn hashLen17To32(str: []const u8) u64 {
248 const len: u64 = @as(u64, str.len);237 const len: u64 = @as(u64, str.len);
249 const mul: u64 = k2 +% len *% 2;238 const mul: u64 = k2 +% len *% 2;
250 const a: u64 = fetch64(str.ptr) *% k1;239 const a: u64 = fetch64(str.ptr, 0) *% k1;
251 const b: u64 = fetch64(str.ptr + 8);240 const b: u64 = fetch64(str.ptr, 8);
252 const c: u64 = fetch64(str.ptr + str.len - 8) *% mul;241 const c: u64 = fetch64(str.ptr, str.len - 8) *% mul;
253 const d: u64 = fetch64(str.ptr + str.len - 16) *% k2;242 const d: u64 = fetch64(str.ptr, str.len - 16) *% k2;
254243
255 return hashLen16Mul(rotr64(a +% b, 43) +% rotr64(c, 30) +% d, a +% rotr64(b +% k2, 18) +% c, mul);244 return hashLen16Mul(rotr64(a +% b, 43) +% rotr64(c, 30) +% d, a +% rotr64(b +% k2, 18) +% c, mul);
256 }245 }
...@@ -258,14 +247,14 @@ pub const CityHash64 = struct {...@@ -258,14 +247,14 @@ pub const CityHash64 = struct {
258 fn hashLen33To64(str: []const u8) u64 {247 fn hashLen33To64(str: []const u8) u64 {
259 const len: u64 = @as(u64, str.len);248 const len: u64 = @as(u64, str.len);
260 const mul: u64 = k2 +% len *% 2;249 const mul: u64 = k2 +% len *% 2;
261 const a: u64 = fetch64(str.ptr) *% k2;250 const a: u64 = fetch64(str.ptr, 0) *% k2;
262 const b: u64 = fetch64(str.ptr + 8);251 const b: u64 = fetch64(str.ptr, 8);
263 const c: u64 = fetch64(str.ptr + str.len - 24);252 const c: u64 = fetch64(str.ptr, str.len - 24);
264 const d: u64 = fetch64(str.ptr + str.len - 32);253 const d: u64 = fetch64(str.ptr, str.len - 32);
265 const e: u64 = fetch64(str.ptr + 16) *% k2;254 const e: u64 = fetch64(str.ptr, 16) *% k2;
266 const f: u64 = fetch64(str.ptr + 24) *% 9;255 const f: u64 = fetch64(str.ptr, 24) *% 9;
267 const g: u64 = fetch64(str.ptr + str.len - 8);256 const g: u64 = fetch64(str.ptr, str.len - 8);
268 const h: u64 = fetch64(str.ptr + str.len - 16) *% mul;257 const h: u64 = fetch64(str.ptr, str.len - 16) *% mul;
269258
270 const u: u64 = rotr64(a +% g, 43) +% (rotr64(b, 30) +% c) *% 9;259 const u: u64 = rotr64(a +% g, 43) +% (rotr64(b, 30) +% c) *% 9;
271 const v: u64 = ((a +% g) ^ d) +% f +% 1;260 const v: u64 = ((a +% g) ^ d) +% f +% 1;
...@@ -297,10 +286,10 @@ pub const CityHash64 = struct {...@@ -297,10 +286,10 @@ pub const CityHash64 = struct {
297286
298 fn weakHashLen32WithSeeds(ptr: [*]const u8, a: u64, b: u64) WeakPair {287 fn weakHashLen32WithSeeds(ptr: [*]const u8, a: u64, b: u64) WeakPair {
299 return @call(.{ .modifier = .always_inline }, weakHashLen32WithSeedsHelper, .{288 return @call(.{ .modifier = .always_inline }, weakHashLen32WithSeedsHelper, .{
300 fetch64(ptr),289 fetch64(ptr, 0),
301 fetch64(ptr + 8),290 fetch64(ptr, 8),
302 fetch64(ptr + 16),291 fetch64(ptr, 16),
303 fetch64(ptr + 24),292 fetch64(ptr, 24),
304 a,293 a,
305 b,294 b,
306 });295 });
...@@ -319,29 +308,29 @@ pub const CityHash64 = struct {...@@ -319,29 +308,29 @@ pub const CityHash64 = struct {
319308
320 var len: u64 = @as(u64, str.len);309 var len: u64 = @as(u64, str.len);
321310
322 var x: u64 = fetch64(str.ptr + str.len - 40);311 var x: u64 = fetch64(str.ptr, str.len - 40);
323 var y: u64 = fetch64(str.ptr + str.len - 16) +% fetch64(str.ptr + str.len - 56);312 var y: u64 = fetch64(str.ptr, str.len - 16) +% fetch64(str.ptr, str.len - 56);
324 var z: u64 = hashLen16(fetch64(str.ptr + str.len - 48) +% len, fetch64(str.ptr + str.len - 24));313 var z: u64 = hashLen16(fetch64(str.ptr, str.len - 48) +% len, fetch64(str.ptr, str.len - 24));
325 var v: WeakPair = weakHashLen32WithSeeds(str.ptr + str.len - 64, len, z);314 var v: WeakPair = weakHashLen32WithSeeds(offsetPtr(str.ptr, str.len - 64), len, z);
326 var w: WeakPair = weakHashLen32WithSeeds(str.ptr + str.len - 32, y +% k1, x);315 var w: WeakPair = weakHashLen32WithSeeds(offsetPtr(str.ptr, str.len - 32), y +% k1, x);
327316
328 x = x *% k1 +% fetch64(str.ptr);317 x = x *% k1 +% fetch64(str.ptr, 0);
329 len = (len - 1) & ~@intCast(u64, 63);318 len = (len - 1) & ~@intCast(u64, 63);
330319
331 var ptr: [*]const u8 = str.ptr;320 var ptr: [*]const u8 = str.ptr;
332 while (true) {321 while (true) {
333 x = rotr64(x +% y +% v.first +% fetch64(ptr + 8), 37) *% k1;322 x = rotr64(x +% y +% v.first +% fetch64(ptr, 8), 37) *% k1;
334 y = rotr64(y +% v.second +% fetch64(ptr + 48), 42) *% k1;323 y = rotr64(y +% v.second +% fetch64(ptr, 48), 42) *% k1;
335 x ^= w.second;324 x ^= w.second;
336 y +%= v.first +% fetch64(ptr + 40);325 y +%= v.first +% fetch64(ptr, 40);
337 z = rotr64(z +% w.first, 33) *% k1;326 z = rotr64(z +% w.first, 33) *% k1;
338 v = weakHashLen32WithSeeds(ptr, v.second *% k1, x +% w.first);327 v = weakHashLen32WithSeeds(ptr, v.second *% k1, x +% w.first);
339 w = weakHashLen32WithSeeds(ptr + 32, z +% w.second, y +% fetch64(ptr + 16));328 w = weakHashLen32WithSeeds(offsetPtr(ptr, 32), z +% w.second, y +% fetch64(ptr, 16));
340 const t: u64 = z;329 const t: u64 = z;
341 z = x;330 z = x;
342 x = t;331 x = t;
343332
344 ptr += 64;333 ptr = offsetPtr(ptr, 64);
345 len -= 64;334 len -= 64;
346 if (len == 0)335 if (len == 0)
347 break;336 break;
...@@ -359,27 +348,31 @@ pub const CityHash64 = struct {...@@ -359,27 +348,31 @@ pub const CityHash64 = struct {
359 }348 }
360};349};
361350
362fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 {351fn SMHasherTest(comptime hash_fn: anytype) u32 {
363 const hashbytes = hashbits / 8;352 const HashResult = @typeInfo(@TypeOf(hash_fn)).Fn.return_type.?;
353
364 var key: [256]u8 = undefined;354 var key: [256]u8 = undefined;
365 var hashes: [hashbytes * 256]u8 = undefined;355 var hashes_bytes: [256 * @sizeOf(HashResult)]u8 = undefined;
366 var final: [hashbytes]u8 = undefined;356 var final: HashResult = 0;
367357
368 @memset(@ptrCast([*]u8, &key[0]), 0, @sizeOf(@TypeOf(key)));358 std.mem.set(u8, &key, 0);
369 @memset(@ptrCast([*]u8, &hashes[0]), 0, @sizeOf(@TypeOf(hashes)));359 std.mem.set(u8, &hashes_bytes, 0);
370 @memset(@ptrCast([*]u8, &final[0]), 0, @sizeOf(@TypeOf(final)));
371360
372 var i: u32 = 0;361 var i: u32 = 0;
373 while (i < 256) : (i += 1) {362 while (i < 256) : (i += 1) {
374 key[i] = @intCast(u8, i);363 key[i] = @intCast(u8, i);
375364
376 var h = hash_fn(key[0..i], 256 - i);365 var h: HashResult = hash_fn(key[0..i], 256 - i);
377 if (builtin.endian == .Big)366
378 h = @byteSwap(@TypeOf(h), h);367 // comptime can't really do reinterpret casting yet,
379 @memcpy(@ptrCast([*]u8, &hashes[i * hashbytes]), @ptrCast([*]u8, &h), hashbytes);368 // so we need to write the bytes manually.
369 for (hashes_bytes[i * @sizeOf(HashResult) ..][0..@sizeOf(HashResult)]) |*byte| {
370 byte.* = @truncate(u8, h);
371 h = h >> 8;
372 }
380 }373 }
381374
382 return @truncate(u32, hash_fn(&hashes, 0));375 return @truncate(u32, hash_fn(&hashes_bytes, 0));
383}376}
384377
385fn CityHash32hashIgnoreSeed(str: []const u8, seed: u32) u32 {378fn CityHash32hashIgnoreSeed(str: []const u8, seed: u32) u32 {
...@@ -387,13 +380,32 @@ fn CityHash32hashIgnoreSeed(str: []const u8, seed: u32) u32 {...@@ -387,13 +380,32 @@ fn CityHash32hashIgnoreSeed(str: []const u8, seed: u32) u32 {
387}380}
388381
389test "cityhash32" {382test "cityhash32" {
390 // Note: SMHasher doesn't provide a 32bit version of the algorithm.383 const Test = struct {
391 // Note: The implementation was verified against the Google Abseil version.384 fn doTest() void {
392 std.testing.expectEqual(SMHasherTest(CityHash32hashIgnoreSeed, 32), 0x68254F81);385 // Note: SMHasher doesn't provide a 32bit version of the algorithm.
386 // Note: The implementation was verified against the Google Abseil version.
387 std.testing.expectEqual(SMHasherTest(CityHash32hashIgnoreSeed), 0x68254F81);
388 std.testing.expectEqual(SMHasherTest(CityHash32hashIgnoreSeed), 0x68254F81);
389 }
390 };
391 Test.doTest();
392 // TODO This is uncommented to prevent OOM on the CI server. Re-enable this test
393 // case once we ship stage2.
394 //@setEvalBranchQuota(50000);
395 //comptime Test.doTest();
393}396}
394397
395test "cityhash64" {398test "cityhash64" {
396 // Note: This is not compliant with the SMHasher implementation of CityHash64!399 const Test = struct {
397 // Note: The implementation was verified against the Google Abseil version.400 fn doTest() void {
398 std.testing.expectEqual(SMHasherTest(CityHash64.hashWithSeed, 64), 0x5FABC5C5);401 // Note: This is not compliant with the SMHasher implementation of CityHash64!
402 // Note: The implementation was verified against the Google Abseil version.
403 std.testing.expectEqual(SMHasherTest(CityHash64.hashWithSeed), 0x5FABC5C5);
404 }
405 };
406 Test.doTest();
407 // TODO This is uncommented to prevent OOM on the CI server. Re-enable this test
408 // case once we ship stage2.
409 //@setEvalBranchQuota(50000);
410 //comptime Test.doTest();
399}411}