Commit 73c98ca0e6

Andrew Kelley <andrew@ziglang.org>
2025-07-27 23:10:55
simplify std.hash.Adler32
1 parent a4f05a4
Changed files (4)
lib/std/hash/adler.zig
@@ -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);
-}
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..]));
+}
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;
 
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;