authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2023-09-01 19:37:40+12:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2023-09-02 15:37:49+12:00
log1c148f161905469b0ad5cf4de801cf287c491174
tree702966068829d7775a33e79d734f24bca5eab97d
parent26d61812a8dbad204517d00d3a1f0e52275eceeb

std/hash: add generic tests for idempotency/iterative api


7 files changed, 61 insertions(+), 48 deletions(-)

lib/std/hash/adler.zig+7-1
...@@ -3,7 +3,7 @@...@@ -3,7 +3,7 @@
3// https://tools.ietf.org/html/rfc1950#section-93// https://tools.ietf.org/html/rfc1950#section-9
4// https://github.com/madler/zlib/blob/master/adler32.c4// https://github.com/madler/zlib/blob/master/adler32.c
55
6const std = @import("../std.zig");6const std = @import("std");
7const testing = std.testing;7const testing = std.testing;
88
9pub const Adler32 = struct {9pub const Adler32 = struct {
...@@ -126,3 +126,9 @@ test "adler32 very long with variation" {...@@ -126,3 +126,9 @@ test "adler32 very long with variation" {
126126
127 try testing.expectEqual(@as(u32, 0x5af38d6e), std.hash.Adler32.hash(long[0..]));127 try testing.expectEqual(@as(u32, 0x5af38d6e), std.hash.Adler32.hash(long[0..]));
128}128}
129
130const verify = @import("verify.zig");
131
132test "adler32 iterative" {
133 try verify.iterativeApi(Adler32);
134}
lib/std/hash/crc.zig+11-1
...@@ -5,7 +5,7 @@...@@ -5,7 +5,7 @@
5// - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is5// - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is
6// still moderately fast just slow relative to the slicing approach.6// still moderately fast just slow relative to the slicing approach.
77
8const std = @import("../std.zig");8const std = @import("std");
9const builtin = @import("builtin");9const builtin = @import("builtin");
10const debug = std.debug;10const debug = std.debug;
11const testing = std.testing;11const testing = std.testing;
...@@ -194,6 +194,8 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type {...@@ -194,6 +194,8 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type {
194 };194 };
195}195}
196196
197const verify = @import("verify.zig");
198
197test "crc32 ieee" {199test "crc32 ieee" {
198 const Crc32Ieee = Crc32WithPoly(.IEEE);200 const Crc32Ieee = Crc32WithPoly(.IEEE);
199201
...@@ -210,6 +212,10 @@ test "crc32 castagnoli" {...@@ -210,6 +212,10 @@ test "crc32 castagnoli" {
210 try testing.expect(Crc32Castagnoli.hash("abc") == 0x364b3fb7);212 try testing.expect(Crc32Castagnoli.hash("abc") == 0x364b3fb7);
211}213}
212214
215test "crc32 iterative" {
216 try verify.iterativeApi(Crc32WithPoly(.IEEE));
217}
218
213// half-byte lookup table implementation.219// half-byte lookup table implementation.
214pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type {220pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type {
215 return struct {221 return struct {
...@@ -258,6 +264,10 @@ pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type {...@@ -258,6 +264,10 @@ pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type {
258 };264 };
259}265}
260266
267test "small crc32 iterative" {
268 try verify.iterativeApi(Crc32SmallWithPoly(.IEEE));
269}
270
261test "small crc32 ieee" {271test "small crc32 ieee" {
262 const Crc32Ieee = Crc32SmallWithPoly(.IEEE);272 const Crc32Ieee = Crc32SmallWithPoly(.IEEE);
263273
lib/std/hash/crc/catalog_test.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1//! This file is auto-generated by tools/update_crc_catalog.zig.1//! This file is auto-generated by tools/update_crc_catalog.zig.
22
3const std = @import("../../std.zig");3const std = @import("std");
4const testing = std.testing;4const testing = std.testing;
5const catalog = @import("catalog.zig");5const catalog = @import("catalog.zig");
66
lib/std/hash/fnv.zig+5
...@@ -40,19 +40,24 @@ fn Fnv1a(comptime T: type, comptime prime: T, comptime offset: T) type {...@@ -40,19 +40,24 @@ fn Fnv1a(comptime T: type, comptime prime: T, comptime offset: T) type {
40 };40 };
41}41}
4242
43const verify = @import("verify.zig");
44
43test "fnv1a-32" {45test "fnv1a-32" {
44 try testing.expect(Fnv1a_32.hash("") == 0x811c9dc5);46 try testing.expect(Fnv1a_32.hash("") == 0x811c9dc5);
45 try testing.expect(Fnv1a_32.hash("a") == 0xe40c292c);47 try testing.expect(Fnv1a_32.hash("a") == 0xe40c292c);
46 try testing.expect(Fnv1a_32.hash("foobar") == 0xbf9cf968);48 try testing.expect(Fnv1a_32.hash("foobar") == 0xbf9cf968);
49 try verify.iterativeApi(Fnv1a_32);
47}50}
4851
49test "fnv1a-64" {52test "fnv1a-64" {
50 try testing.expect(Fnv1a_64.hash("") == 0xcbf29ce484222325);53 try testing.expect(Fnv1a_64.hash("") == 0xcbf29ce484222325);
51 try testing.expect(Fnv1a_64.hash("a") == 0xaf63dc4c8601ec8c);54 try testing.expect(Fnv1a_64.hash("a") == 0xaf63dc4c8601ec8c);
52 try testing.expect(Fnv1a_64.hash("foobar") == 0x85944171f73967e8);55 try testing.expect(Fnv1a_64.hash("foobar") == 0x85944171f73967e8);
56 try verify.iterativeApi(Fnv1a_64);
53}57}
5458
55test "fnv1a-128" {59test "fnv1a-128" {
56 try testing.expect(Fnv1a_128.hash("") == 0x6c62272e07bb014262b821756295c58d);60 try testing.expect(Fnv1a_128.hash("") == 0x6c62272e07bb014262b821756295c58d);
57 try testing.expect(Fnv1a_128.hash("a") == 0xd228cb696f1a8caf78912b704e4a8964);61 try testing.expect(Fnv1a_128.hash("a") == 0xd228cb696f1a8caf78912b704e4a8964);
62 try verify.iterativeApi(Fnv1a_128);
58}63}
lib/std/hash/verify.zig+27
...@@ -13,6 +13,15 @@ fn hashMaybeSeed(comptime hash_fn: anytype, seed: anytype, buf: []const u8) @typ...@@ -13,6 +13,15 @@ fn hashMaybeSeed(comptime hash_fn: anytype, seed: anytype, buf: []const u8) @typ
13 }13 }
14}14}
1515
16fn initMaybeSeed(comptime Hash: anytype, seed: anytype) Hash {
17 const HashFn = @typeInfo(@TypeOf(Hash.init)).Fn;
18 if (HashFn.params.len == 1) {
19 return Hash.init(@intCast(seed));
20 } else {
21 return Hash.init();
22 }
23}
24
16// Returns a verification code, the same as user by SMHasher.25// Returns a verification code, the same as user by SMHasher.
17//26//
18// Hash keys of the form {0}, {0,1}, {0,1,2}... up to N=255, using 256-N as seed.27// Hash keys of the form {0}, {0,1}, {0,1,2}... up to N=255, using 256-N as seed.
...@@ -33,3 +42,21 @@ pub fn smhasher(comptime hash_fn: anytype) u32 {...@@ -33,3 +42,21 @@ pub fn smhasher(comptime hash_fn: anytype) u32 {
3342
34 return @truncate(hashMaybeSeed(hash_fn, 0, buf_all[0..]));43 return @truncate(hashMaybeSeed(hash_fn, 0, buf_all[0..]));
35}44}
45
46pub fn iterativeApi(comptime Hash: anytype) !void {
47 // Sum(1..32) = 528
48 var buf: [528]u8 = [_]u8{0} ** 528;
49 var len: usize = 0;
50 const seed = 0;
51
52 var hasher = initMaybeSeed(Hash, seed);
53 for (1..32) |i| {
54 const r = hashMaybeSeed(Hash.hash, seed, buf[0 .. len + i]);
55 hasher.update(buf[len..][0..i]);
56 const f1 = hasher.final();
57 const f2 = hasher.final();
58 if (f1 != f2) return error.IterativeHashWasNotIdempotent;
59 if (f1 != r) return error.IterativeHashDidNotMatchDirect;
60 len += i;
61 }
62}
lib/std/hash/wyhash.zig+2-45
...@@ -234,51 +234,8 @@ test "smhasher" {...@@ -234,51 +234,8 @@ test "smhasher" {
234 try expectEqual(verify.smhasher(Wyhash.hash), 0xBD5E840C);234 try expectEqual(verify.smhasher(Wyhash.hash), 0xBD5E840C);
235}235}
236236
237test "test vectors streaming" {237test "iterative api" {
238 const step = 5;238 try verify.iterativeApi(Wyhash);
239
240 for (vectors) |e| {
241 var wh = Wyhash.init(e.seed);
242 var i: usize = 0;
243 while (i < e.input.len) : (i += step) {
244 const len = if (i + step > e.input.len) e.input.len - i else step;
245 wh.update(e.input[i..][0..len]);
246 }
247 try expectEqual(e.expected, wh.final());
248 }
249}
250
251test "test ensure idempotent final call" {
252 const e: TestVector = .{ .seed = 6, .expected = 0xc39cab13b115aad3, .input = "12345678901234567890123456789012345678901234567890123456789012345678901234567890" };
253 var wh = Wyhash.init(e.seed);
254 wh.update(e.input);
255
256 for (0..10) |_| {
257 try expectEqual(e.expected, wh.final());
258 }
259}
260
261test "iterative non-divisible update" {
262 var buf: [8192]u8 = undefined;
263 for (&buf, 0..) |*e, i| {
264 e.* = @as(u8, @truncate(i));
265 }
266
267 const seed = 0x128dad08f;
268
269 var end: usize = 32;
270 while (end < buf.len) : (end += 32) {
271 const non_iterative_hash = Wyhash.hash(seed, buf[0..end]);
272
273 var wy = Wyhash.init(seed);
274 var i: usize = 0;
275 while (i < end) : (i += 33) {
276 wy.update(buf[i..@min(i + 33, end)]);
277 }
278 const iterative_hash = wy.final();
279
280 try std.testing.expectEqual(iterative_hash, non_iterative_hash);
281 }
282}239}
283240
284test "iterative maintains last sixteen" {241test "iterative maintains last sixteen" {
lib/std/hash/xxhash.zig+8
...@@ -461,6 +461,10 @@ test "xxhash64" {...@@ -461,6 +461,10 @@ test "xxhash64" {
461 try expectEqual(verify.smhasher(H.hash), 0x024B7CF4);461 try expectEqual(verify.smhasher(H.hash), 0x024B7CF4);
462}462}
463463
464test "xxhash64 iterative api" {
465 try verify.iterativeApi(XxHash64);
466}
467
464test "xxhash32" {468test "xxhash32" {
465 const H = XxHash32;469 const H = XxHash32;
466470
...@@ -474,3 +478,7 @@ test "xxhash32" {...@@ -474,3 +478,7 @@ test "xxhash32" {
474478
475 try expectEqual(verify.smhasher(H.hash), 0xBA88B743);479 try expectEqual(verify.smhasher(H.hash), 0xBA88B743);
476}480}
481
482test "xxhash32 iterative api" {
483 try verify.iterativeApi(XxHash32);
484}