master
 1const std = @import("std");
 2
 3fn hashMaybeSeed(comptime hash_fn: anytype, seed: anytype, buf: []const u8) @typeInfo(@TypeOf(hash_fn)).@"fn".return_type.? {
 4    const HashFn = @typeInfo(@TypeOf(hash_fn)).@"fn";
 5    if (HashFn.params.len > 1) {
 6        if (@typeInfo(HashFn.params[0].type.?) == .int) {
 7            return hash_fn(@intCast(seed), buf);
 8        } else {
 9            return hash_fn(buf, @intCast(seed));
10        }
11    } else {
12        return hash_fn(buf);
13    }
14}
15
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
25// Returns a verification code, the same as used by SMHasher.
26//
27// Hash keys of the form {0}, {0,1}, {0,1,2}... up to N=255, using 256-N as seed.
28// First four-bytes of the hash, interpreted as little-endian is the verification code.
29pub fn smhasher(comptime hash_fn: anytype) u32 {
30    const HashFnTy = @typeInfo(@TypeOf(hash_fn)).@"fn";
31    const HashResult = HashFnTy.return_type.?;
32    const hash_size = @sizeOf(HashResult);
33
34    var buf: [256]u8 = undefined;
35    var buf_all: [256 * hash_size]u8 = undefined;
36
37    for (0..256) |i| {
38        buf[i] = @intCast(i);
39        const h = hashMaybeSeed(hash_fn, 256 - i, buf[0..i]);
40        std.mem.writeInt(HashResult, buf_all[i * hash_size ..][0..hash_size], h, .little);
41    }
42
43    return @truncate(hashMaybeSeed(hash_fn, 0, buf_all[0..]));
44}
45
46pub fn iterativeApi(comptime Hash: anytype) !void {
47    // Sum(1..32) = 528
48    var buf: [528]u8 = @splat(0);
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}