authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-29 00:45:17-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-04-29 00:45:17-07:00
logfc45e5b39c0e09dcf69cc65a92375bef0c9593cb
tree3b95615e2ef511c59e7dfc617edb77f176b72e9c
parent497f37ce92ba13aa609acce23135fd6b06ed410f
parent5f0ecafa0dddd9b630220d1c6cf4be2e42987faf
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #19784 from tiehuis/simplify-hash-crc32

std.hash.crc: simplify api

7 files changed, 65 insertions(+), 174 deletions(-)

lib/std/debug.zig+1-1
...@@ -1150,7 +1150,7 @@ pub fn readElfDebugInfo(...@@ -1150,7 +1150,7 @@ pub fn readElfDebugInfo(
1150 };1150 };
11511151
1152 const mapped_mem = try mapWholeFile(elf_file);1152 const mapped_mem = try mapWholeFile(elf_file);
1153 if (expected_crc) |crc| if (crc != std.hash.crc.Crc32SmallWithPoly(.IEEE).hash(mapped_mem)) return error.InvalidDebugInfo;1153 if (expected_crc) |crc| if (crc != std.hash.crc.Crc32.hash(mapped_mem)) return error.InvalidDebugInfo;
11541154
1155 const hdr: *const elf.Ehdr = @ptrCast(&mapped_mem[0]);1155 const hdr: *const elf.Ehdr = @ptrCast(&mapped_mem[0]);
1156 if (!mem.eql(u8, hdr.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;1156 if (!mem.eql(u8, hdr.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
lib/std/hash/benchmark.zig+2-6
...@@ -56,12 +56,8 @@ const hashes = [_]Hash{...@@ -56,12 +56,8 @@ const hashes = [_]Hash{
56 .name = "adler32",56 .name = "adler32",
57 },57 },
58 Hash{58 Hash{
59 .ty = hash.crc.Crc32WithPoly(.IEEE),59 .ty = hash.crc.Crc32,
60 .name = "crc32-slicing-by-8",60 .name = "crc32",
61 },
62 Hash{
63 .ty = hash.crc.Crc32SmallWithPoly(.IEEE),
64 .name = "crc32-half-byte-lookup",
65 },61 },
66 Hash{62 Hash{
67 .ty = hash.CityHash32,63 .ty = hash.CityHash32,
lib/std/hash/crc.zig+9-2
...@@ -7,8 +7,7 @@ pub const Polynomial = impl.Polynomial;...@@ -7,8 +7,7 @@ pub const Polynomial = impl.Polynomial;
7pub const Crc32WithPoly = impl.Crc32WithPoly;7pub const Crc32WithPoly = impl.Crc32WithPoly;
8pub const Crc32SmallWithPoly = impl.Crc32SmallWithPoly;8pub const Crc32SmallWithPoly = impl.Crc32SmallWithPoly;
99
10// IEEE is by far the most common CRC and so is aliased by default.10pub const Crc32 = Crc32IsoHdlc;
11pub const Crc32 = Crc32WithPoly(.IEEE);
1211
13test {12test {
14 _ = @import("crc/test.zig");13 _ = @import("crc/test.zig");
...@@ -822,6 +821,14 @@ pub const Crc32Jamcrc = Crc(u32, .{...@@ -822,6 +821,14 @@ pub const Crc32Jamcrc = Crc(u32, .{
822 .xor_output = 0x00000000,821 .xor_output = 0x00000000,
823});822});
824823
824pub const Crc32Koopman = Crc(u32, .{
825 .polynomial = 0x741b8cd7,
826 .initial = 0xffffffff,
827 .reflect_input = true,
828 .reflect_output = true,
829 .xor_output = 0xffffffff,
830});
831
825pub const Crc32Mef = Crc(u32, .{832pub const Crc32Mef = Crc(u32, .{
826 .polynomial = 0x741b8cd7,833 .polynomial = 0x741b8cd7,
827 .initial = 0xffffffff,834 .initial = 0xffffffff,
lib/std/hash/crc/impl.zig+6-135
...@@ -1,11 +1,5 @@...@@ -1,11 +1,5 @@
1// There is a generic CRC implementation "Crc()" which can be paramterized via1// There is a generic CRC implementation "Crc()" which can be paramterized via
2// the Algorithm struct for a plethora of uses, along with two implementations2// the Algorithm struct for a plethora of uses.
3// of CRC32 implemented with the following key characteristics:
4//
5// - Crc32WithPoly uses 8Kb of tables but is ~10x faster than the small method.
6//
7// - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is
8// still moderately fast just slow relative to the slicing approach.
9//3//
10// The primary interface for all of the standard CRC algorithms is the4// The primary interface for all of the standard CRC algorithms is the
11// generated file "crc.zig", which uses the implementation code here to define5// generated file "crc.zig", which uses the implementation code here to define
...@@ -108,134 +102,11 @@ pub fn Crc(comptime W: type, comptime algorithm: Algorithm(W)) type {...@@ -108,134 +102,11 @@ pub fn Crc(comptime W: type, comptime algorithm: Algorithm(W)) type {
108}102}
109103
110pub const Polynomial = enum(u32) {104pub const Polynomial = enum(u32) {
111 IEEE = 0xedb88320,105 IEEE = @compileError("use Crc with algorithm .Crc32IsoHdlc"),
112 Castagnoli = 0x82f63b78,106 Castagnoli = @compileError("use Crc with algorithm .Crc32Iscsi"),
113 Koopman = 0xeb31d82e,107 Koopman = @compileError("use Crc with algorithm .Crc32Koopman"),
114 _,108 _,
115};109};
116110
117// slicing-by-8 crc32 implementation.111pub const Crc32WithPoly = @compileError("use Crc instead");
118pub fn Crc32WithPoly(comptime poly: Polynomial) type {112pub const Crc32SmallWithPoly = @compileError("use Crc instead");
119 return struct {
120 const Self = @This();
121 const lookup_tables = block: {
122 @setEvalBranchQuota(20000);
123 var tables: [8][256]u32 = undefined;
124
125 for (&tables[0], 0..) |*e, i| {
126 var crc = @as(u32, @intCast(i));
127 var j: usize = 0;
128 while (j < 8) : (j += 1) {
129 if (crc & 1 == 1) {
130 crc = (crc >> 1) ^ @intFromEnum(poly);
131 } else {
132 crc = (crc >> 1);
133 }
134 }
135 e.* = crc;
136 }
137
138 var i: usize = 0;
139 while (i < 256) : (i += 1) {
140 var crc = tables[0][i];
141 var j: usize = 1;
142 while (j < 8) : (j += 1) {
143 const index: u8 = @truncate(crc);
144 crc = tables[0][index] ^ (crc >> 8);
145 tables[j][i] = crc;
146 }
147 }
148
149 break :block tables;
150 };
151
152 crc: u32,
153
154 pub fn init() Self {
155 return Self{ .crc = 0xffffffff };
156 }
157
158 pub fn update(self: *Self, input: []const u8) void {
159 var i: usize = 0;
160 while (i + 8 <= input.len) : (i += 8) {
161 const p = input[i..][0..8];
162
163 // Unrolling this way gives ~50Mb/s increase
164 self.crc ^= std.mem.readInt(u32, p[0..4], .little);
165
166 self.crc =
167 lookup_tables[0][p[7]] ^
168 lookup_tables[1][p[6]] ^
169 lookup_tables[2][p[5]] ^
170 lookup_tables[3][p[4]] ^
171 lookup_tables[4][@as(u8, @truncate(self.crc >> 24))] ^
172 lookup_tables[5][@as(u8, @truncate(self.crc >> 16))] ^
173 lookup_tables[6][@as(u8, @truncate(self.crc >> 8))] ^
174 lookup_tables[7][@as(u8, @truncate(self.crc >> 0))];
175 }
176
177 while (i < input.len) : (i += 1) {
178 const index = @as(u8, @truncate(self.crc)) ^ input[i];
179 self.crc = (self.crc >> 8) ^ lookup_tables[0][index];
180 }
181 }
182
183 pub fn final(self: *Self) u32 {
184 return ~self.crc;
185 }
186
187 pub fn hash(input: []const u8) u32 {
188 var c = Self.init();
189 c.update(input);
190 return c.final();
191 }
192 };
193}
194
195// half-byte lookup table implementation.
196pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type {
197 return struct {
198 const Self = @This();
199 const lookup_table = block: {
200 var table: [16]u32 = undefined;
201
202 for (&table, 0..) |*e, i| {
203 var crc = @as(u32, @intCast(i * 16));
204 var j: usize = 0;
205 while (j < 8) : (j += 1) {
206 if (crc & 1 == 1) {
207 crc = (crc >> 1) ^ @intFromEnum(poly);
208 } else {
209 crc = (crc >> 1);
210 }
211 }
212 e.* = crc;
213 }
214
215 break :block table;
216 };
217
218 crc: u32,
219
220 pub fn init() Self {
221 return Self{ .crc = 0xffffffff };
222 }
223
224 pub fn update(self: *Self, input: []const u8) void {
225 for (input) |b| {
226 self.crc = lookup_table[@as(u4, @truncate(self.crc ^ (b >> 0)))] ^ (self.crc >> 4);
227 self.crc = lookup_table[@as(u4, @truncate(self.crc ^ (b >> 4)))] ^ (self.crc >> 4);
228 }
229 }
230
231 pub fn final(self: *Self) u32 {
232 return ~self.crc;
233 }
234
235 pub fn hash(input: []const u8) u32 {
236 var c = Self.init();
237 c.update(input);
238 return c.final();
239 }
240 };
241}
lib/std/hash/crc/test.zig+28-14
...@@ -5,22 +5,25 @@ const testing = std.testing;...@@ -5,22 +5,25 @@ const testing = std.testing;
5const verify = @import("../verify.zig");5const verify = @import("../verify.zig");
6const crc = @import("../crc.zig");6const crc = @import("../crc.zig");
77
8test "crc32 ieee" {8test "crc32 ieee regression" {
9 inline for ([2]type{ crc.Crc32WithPoly(.IEEE), crc.Crc32SmallWithPoly(.IEEE) }) |ieee| {9 const crc32 = crc.Crc32IsoHdlc;
10 try testing.expect(ieee.hash("") == 0x00000000);10 try testing.expectEqual(crc32.hash(""), 0x00000000);
11 try testing.expect(ieee.hash("a") == 0xe8b7be43);11 try testing.expectEqual(crc32.hash("a"), 0xe8b7be43);
12 try testing.expect(ieee.hash("abc") == 0x352441c2);12 try testing.expectEqual(crc32.hash("abc"), 0x352441c2);
13 try verify.iterativeApi(ieee);
14 }
15}13}
1614
17test "crc32 castagnoli" {15test "crc32 castagnoli regression" {
18 inline for ([2]type{ crc.Crc32WithPoly(.Castagnoli), crc.Crc32SmallWithPoly(.Castagnoli) }) |casta| {16 const crc32 = crc.Crc32Iscsi;
19 try testing.expect(casta.hash("") == 0x00000000);17 try testing.expectEqual(crc32.hash(""), 0x00000000);
20 try testing.expect(casta.hash("a") == 0xc1d04330);18 try testing.expectEqual(crc32.hash("a"), 0xc1d04330);
21 try testing.expect(casta.hash("abc") == 0x364b3fb7);19 try testing.expectEqual(crc32.hash("abc"), 0x364b3fb7);
22 try verify.iterativeApi(casta);20}
23 }21
22test "crc32 koopman regression" {
23 const crc32 = crc.Crc32Koopman;
24 try testing.expectEqual(crc32.hash(""), 0x00000000);
25 try testing.expectEqual(crc32.hash("a"), 0x0da2aa8a);
26 try testing.expectEqual(crc32.hash("abc"), 0xba2322ac);
24}27}
2528
26test "CRC-3/GSM" {29test "CRC-3/GSM" {
...@@ -1134,6 +1137,17 @@ test "CRC-32/JAMCRC" {...@@ -1134,6 +1137,17 @@ test "CRC-32/JAMCRC" {
1134 try testing.expectEqual(@as(u32, 0x340bc6d9), c.final());1137 try testing.expectEqual(@as(u32, 0x340bc6d9), c.final());
1135}1138}
11361139
1140test "CRC-32/KOOPMAN" {
1141 const Crc32Koopman = crc.Crc32Koopman;
1142
1143 try testing.expectEqual(@as(u32, 0x2d3dd0ae), Crc32Koopman.hash("123456789"));
1144
1145 var c = Crc32Koopman.init();
1146 c.update("1234");
1147 c.update("56789");
1148 try testing.expectEqual(@as(u32, 0x2d3dd0ae), c.final());
1149}
1150
1137test "CRC-32/MEF" {1151test "CRC-32/MEF" {
1138 const Crc32Mef = crc.Crc32Mef;1152 const Crc32Mef = crc.Crc32Mef;
11391153
tools/crc/catalog.txt+1
...@@ -100,6 +100,7 @@ width=32 poly=0x04c11db7 init=0x00000000 refin=false refout=false xorout=0x...@@ -100,6 +100,7 @@ width=32 poly=0x04c11db7 init=0x00000000 refin=false refout=false xorout=0x
100width=32 poly=0x1edc6f41 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0xe3069283 residue=0xb798b438 name="CRC-32/ISCSI"100width=32 poly=0x1edc6f41 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0xe3069283 residue=0xb798b438 name="CRC-32/ISCSI"
101width=32 poly=0x04c11db7 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0xcbf43926 residue=0xdebb20e3 name="CRC-32/ISO-HDLC"101width=32 poly=0x04c11db7 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0xcbf43926 residue=0xdebb20e3 name="CRC-32/ISO-HDLC"
102width=32 poly=0x04c11db7 init=0xffffffff refin=true refout=true xorout=0x00000000 check=0x340bc6d9 residue=0x00000000 name="CRC-32/JAMCRC"102width=32 poly=0x04c11db7 init=0xffffffff refin=true refout=true xorout=0x00000000 check=0x340bc6d9 residue=0x00000000 name="CRC-32/JAMCRC"
103width=32 poly=0x741b8cd7 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0x2d3dd0ae residue=0x00000000 name="CRC-32/KOOPMAN"
103width=32 poly=0x741b8cd7 init=0xffffffff refin=true refout=true xorout=0x00000000 check=0xd2c22f51 residue=0x00000000 name="CRC-32/MEF"104width=32 poly=0x741b8cd7 init=0xffffffff refin=true refout=true xorout=0x00000000 check=0xd2c22f51 residue=0x00000000 name="CRC-32/MEF"
104width=32 poly=0x04c11db7 init=0xffffffff refin=false refout=false xorout=0x00000000 check=0x0376e6e7 residue=0x00000000 name="CRC-32/MPEG-2"105width=32 poly=0x04c11db7 init=0xffffffff refin=false refout=false xorout=0x00000000 check=0x0376e6e7 residue=0x00000000 name="CRC-32/MPEG-2"
105width=32 poly=0x000000af init=0x00000000 refin=false refout=false xorout=0x00000000 check=0xbd0be338 residue=0x00000000 name="CRC-32/XFER"106width=32 poly=0x000000af init=0x00000000 refin=false refout=false xorout=0x00000000 check=0xbd0be338 residue=0x00000000 name="CRC-32/XFER"
tools/update_crc_catalog.zig+18-16
...@@ -48,8 +48,7 @@ pub fn main() anyerror!void {...@@ -48,8 +48,7 @@ pub fn main() anyerror!void {
48 \\pub const Crc32WithPoly = impl.Crc32WithPoly;48 \\pub const Crc32WithPoly = impl.Crc32WithPoly;
49 \\pub const Crc32SmallWithPoly = impl.Crc32SmallWithPoly;49 \\pub const Crc32SmallWithPoly = impl.Crc32SmallWithPoly;
50 \\50 \\
51 \\// IEEE is by far the most common CRC and so is aliased by default.51 \\pub const Crc32 = Crc32IsoHdlc;
52 \\pub const Crc32 = Crc32WithPoly(.IEEE);
53 \\52 \\
54 \\test {53 \\test {
55 \\ _ = @import("crc/test.zig");54 \\ _ = @import("crc/test.zig");
...@@ -72,22 +71,25 @@ pub fn main() anyerror!void {...@@ -72,22 +71,25 @@ pub fn main() anyerror!void {
72 \\const verify = @import("../verify.zig");71 \\const verify = @import("../verify.zig");
73 \\const crc = @import("../crc.zig");72 \\const crc = @import("../crc.zig");
74 \\73 \\
75 \\test "crc32 ieee" {74 \\test "crc32 ieee regression" {
76 \\ inline for ([2]type{ crc.Crc32WithPoly(.IEEE), crc.Crc32SmallWithPoly(.IEEE) }) |ieee| {75 \\ const crc32 = crc.Crc32IsoHdlc;
77 \\ try testing.expect(ieee.hash("") == 0x00000000);76 \\ try testing.expectEqual(crc32.hash(""), 0x00000000);
78 \\ try testing.expect(ieee.hash("a") == 0xe8b7be43);77 \\ try testing.expectEqual(crc32.hash("a"), 0xe8b7be43);
79 \\ try testing.expect(ieee.hash("abc") == 0x352441c2);78 \\ try testing.expectEqual(crc32.hash("abc"), 0x352441c2);
80 \\ try verify.iterativeApi(ieee);
81 \\ }
82 \\}79 \\}
83 \\80 \\
84 \\test "crc32 castagnoli" {81 \\test "crc32 castagnoli regression" {
85 \\ inline for ([2]type{ crc.Crc32WithPoly(.Castagnoli), crc.Crc32SmallWithPoly(.Castagnoli) }) |casta| {82 \\ const crc32 = crc.Crc32Iscsi;
86 \\ try testing.expect(casta.hash("") == 0x00000000);83 \\ try testing.expectEqual(crc32.hash(""), 0x00000000);
87 \\ try testing.expect(casta.hash("a") == 0xc1d04330);84 \\ try testing.expectEqual(crc32.hash("a"), 0xc1d04330);
88 \\ try testing.expect(casta.hash("abc") == 0x364b3fb7);85 \\ try testing.expectEqual(crc32.hash("abc"), 0x364b3fb7);
89 \\ try verify.iterativeApi(casta);86 \\}
90 \\ }87 \\
88 \\test "crc32 koopman regression" {
89 \\ const crc32 = crc.Koopman;
90 \\ try testing.expectEqual(crc32.hash(""), 0x00000000);
91 \\ try testing.expectEqual(crc32.hash("a"), 0x0da2aa8a);
92 \\ try testing.expectEqual(crc32.hash("abc"), 0xba2322ac);
91 \\}93 \\}
92 \\94 \\
93 );95 );