From 73c98ca0e6aa52b942b92135ecf0305362030733 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 27 Jul 2025 14:10:55 -0700 Subject: [PATCH] simplify std.hash.Adler32 --- lib/std/hash.zig | 5 +- lib/std/hash/Adler32.zig | 117 ++++++++++++++++++++++++++++++++++ lib/std/hash/adler.zig | 134 --------------------------------------- lib/std/hash/verify.zig | 2 +- 4 files changed, 120 insertions(+), 138 deletions(-) create mode 100644 lib/std/hash/Adler32.zig delete mode 100644 lib/std/hash/adler.zig diff --git a/lib/std/hash.zig b/lib/std/hash.zig index 5f1697a236..781971bd13 100644 --- a/lib/std/hash.zig +++ b/lib/std/hash.zig @@ -1,5 +1,4 @@ -const adler = @import("hash/adler.zig"); -pub const Adler32 = adler.Adler32; +pub const Adler32 = @import("hash/Adler32.zig"); const auto_hash = @import("hash/auto_hash.zig"); pub const autoHash = auto_hash.autoHash; @@ -116,7 +115,7 @@ test int { } test { - _ = adler; + _ = Adler32; _ = auto_hash; _ = crc; _ = fnv; diff --git a/lib/std/hash/Adler32.zig b/lib/std/hash/Adler32.zig new file mode 100644 index 0000000000..6932dc59da --- /dev/null +++ b/lib/std/hash/Adler32.zig @@ -0,0 +1,117 @@ +//! https://tools.ietf.org/html/rfc1950#section-9 +//! https://github.com/madler/zlib/blob/master/adler32.c + +const Adler32 = @This(); +const std = @import("std"); +const testing = std.testing; + +adler: u32 = 1, + +pub fn permute(state: u32, input: []const u8) u32 { + const base = 65521; + const nmax = 5552; + + var s1 = state & 0xffff; + var s2 = (state >> 16) & 0xffff; + + if (input.len == 1) { + s1 +%= input[0]; + if (s1 >= base) { + s1 -= base; + } + s2 +%= s1; + if (s2 >= base) { + s2 -= base; + } + } else if (input.len < 16) { + for (input) |b| { + s1 +%= b; + s2 +%= s1; + } + if (s1 >= base) { + s1 -= base; + } + + s2 %= base; + } else { + const n = nmax / 16; // note: 16 | nmax + + var i: usize = 0; + + while (i + nmax <= input.len) { + var rounds: usize = 0; + while (rounds < n) : (rounds += 1) { + comptime var j: usize = 0; + inline while (j < 16) : (j += 1) { + s1 +%= input[i + j]; + s2 +%= s1; + } + i += 16; + } + + s1 %= base; + s2 %= base; + } + + if (i < input.len) { + while (i + 16 <= input.len) : (i += 16) { + comptime var j: usize = 0; + inline while (j < 16) : (j += 1) { + s1 +%= input[i + j]; + s2 +%= s1; + } + } + while (i < input.len) : (i += 1) { + s1 +%= input[i]; + s2 +%= s1; + } + + s1 %= base; + s2 %= base; + } + } + + return s1 | (s2 << 16); +} + +pub fn update(a: *Adler32, input: []const u8) void { + a.adler = permute(a.adler, input); +} + +pub fn hash(input: []const u8) u32 { + return permute(1, input); +} + +test "sanity" { + try testing.expectEqual(@as(u32, 0x620062), hash("a")); + try testing.expectEqual(@as(u32, 0xbc002ed), hash("example")); +} + +test "long" { + const long1 = [_]u8{1} ** 1024; + try testing.expectEqual(@as(u32, 0x06780401), hash(long1[0..])); + + const long2 = [_]u8{1} ** 1025; + try testing.expectEqual(@as(u32, 0x0a7a0402), hash(long2[0..])); +} + +test "very long" { + const long = [_]u8{1} ** 5553; + try testing.expectEqual(@as(u32, 0x707f15b2), hash(long[0..])); +} + +test "very long with variation" { + const long = comptime blk: { + @setEvalBranchQuota(7000); + var result: [6000]u8 = undefined; + + var i: usize = 0; + while (i < result.len) : (i += 1) { + result[i] = @as(u8, @truncate(i)); + } + + break :blk result; + }; + + try testing.expectEqual(@as(u32, 0x5af38d6e), hash(long[0..])); +} diff --git a/lib/std/hash/adler.zig b/lib/std/hash/adler.zig deleted file mode 100644 index 52f7b2691a..0000000000 --- a/lib/std/hash/adler.zig +++ /dev/null @@ -1,134 +0,0 @@ -// Adler32 checksum. -// -// https://tools.ietf.org/html/rfc1950#section-9 -// https://github.com/madler/zlib/blob/master/adler32.c - -const std = @import("std"); -const testing = std.testing; - -pub const Adler32 = struct { - const base = 65521; - const nmax = 5552; - - adler: u32, - - pub fn init() Adler32 { - return Adler32{ .adler = 1 }; - } - - // This fast variant is taken from zlib. It reduces the required modulos and unrolls longer - // buffer inputs and should be much quicker. - pub fn update(self: *Adler32, input: []const u8) void { - var s1 = self.adler & 0xffff; - var s2 = (self.adler >> 16) & 0xffff; - - if (input.len == 1) { - s1 +%= input[0]; - if (s1 >= base) { - s1 -= base; - } - s2 +%= s1; - if (s2 >= base) { - s2 -= base; - } - } else if (input.len < 16) { - for (input) |b| { - s1 +%= b; - s2 +%= s1; - } - if (s1 >= base) { - s1 -= base; - } - - s2 %= base; - } else { - const n = nmax / 16; // note: 16 | nmax - - var i: usize = 0; - - while (i + nmax <= input.len) { - var rounds: usize = 0; - while (rounds < n) : (rounds += 1) { - comptime var j: usize = 0; - inline while (j < 16) : (j += 1) { - s1 +%= input[i + j]; - s2 +%= s1; - } - i += 16; - } - - s1 %= base; - s2 %= base; - } - - if (i < input.len) { - while (i + 16 <= input.len) : (i += 16) { - comptime var j: usize = 0; - inline while (j < 16) : (j += 1) { - s1 +%= input[i + j]; - s2 +%= s1; - } - } - while (i < input.len) : (i += 1) { - s1 +%= input[i]; - s2 +%= s1; - } - - s1 %= base; - s2 %= base; - } - } - - self.adler = s1 | (s2 << 16); - } - - pub fn final(self: *Adler32) u32 { - return self.adler; - } - - pub fn hash(input: []const u8) u32 { - var c = Adler32.init(); - c.update(input); - return c.final(); - } -}; - -test "adler32 sanity" { - try testing.expectEqual(@as(u32, 0x620062), Adler32.hash("a")); - try testing.expectEqual(@as(u32, 0xbc002ed), Adler32.hash("example")); -} - -test "adler32 long" { - const long1 = [_]u8{1} ** 1024; - try testing.expectEqual(@as(u32, 0x06780401), Adler32.hash(long1[0..])); - - const long2 = [_]u8{1} ** 1025; - try testing.expectEqual(@as(u32, 0x0a7a0402), Adler32.hash(long2[0..])); -} - -test "adler32 very long" { - const long = [_]u8{1} ** 5553; - try testing.expectEqual(@as(u32, 0x707f15b2), Adler32.hash(long[0..])); -} - -test "adler32 very long with variation" { - const long = comptime blk: { - @setEvalBranchQuota(7000); - var result: [6000]u8 = undefined; - - var i: usize = 0; - while (i < result.len) : (i += 1) { - result[i] = @as(u8, @truncate(i)); - } - - break :blk result; - }; - - try testing.expectEqual(@as(u32, 0x5af38d6e), std.hash.Adler32.hash(long[0..])); -} - -const verify = @import("verify.zig"); - -test "adler32 iterative" { - try verify.iterativeApi(Adler32); -} diff --git a/lib/std/hash/verify.zig b/lib/std/hash/verify.zig index 61f501a881..a96a36c050 100644 --- a/lib/std/hash/verify.zig +++ b/lib/std/hash/verify.zig @@ -45,7 +45,7 @@ pub fn smhasher(comptime hash_fn: anytype) u32 { pub fn iterativeApi(comptime Hash: anytype) !void { // Sum(1..32) = 528 - var buf: [528]u8 = [_]u8{0} ** 528; + var buf: [528]u8 = @splat(0); var len: usize = 0; const seed = 0;