1const std = @import("std");
2const Io = std.Io;
3const Dir = std.Io.Dir;
4const mem = std.mem;
5
6const catalog_txt = @embedFile("crc/catalog.txt");
7
8pub fn main(init: std.process.Init) !void {
9 const arena = init.arena.allocator();
10 const io = init.io;
11 const args = try init.minimal.args.toSlice(arena);
12 return @"i like cheese"(arena, io, args);
13}
14
15fn @"i like cheese"(arena: std.mem.Allocator, io: Io, args: []const []const u8) !void {
16 if (args.len <= 1) printUsageAndExit(args[0]);
17
18 const zig_src_root = args[1];
19 if (mem.startsWith(u8, zig_src_root, "-")) printUsageAndExit(args[0]);
20
21 var zig_src_dir = try Dir.cwd().openDir(io, zig_src_root, .{});
22 defer zig_src_dir.close(io);
23
24 const hash_sub_path = try Dir.path.join(arena, &.{ "lib", "std", "hash" });
25 var hash_target_dir = try zig_src_dir.createDirPathOpen(io, hash_sub_path, .{});
26 defer hash_target_dir.close(io);
27
28 const crc_sub_path = try Dir.path.join(arena, &.{ "lib", "std", "hash", "crc" });
29 var crc_target_dir = try zig_src_dir.createDirPathOpen(io, crc_sub_path, .{});
30 defer crc_target_dir.close(io);
31
32 var zig_code_file = try hash_target_dir.createFile(io, "crc.zig", .{});
33 defer zig_code_file.close(io);
34
35 var zig_code_file_buffer: [4096]u8 = undefined;
36 var zig_code_file_writer = zig_code_file.writer(io, &zig_code_file_buffer);
37 const code_writer = &zig_code_file_writer.interface;
38
39 try code_writer.writeAll(
40 \\//! This file is auto-generated by tools/update_crc_catalog.zig.
41 \\
42 \\const builtin = @import("builtin");
43 \\
44 );
45
46 var zig_test_file = try crc_target_dir.createFile(io, "test.zig", .{});
47 defer zig_test_file.close(io);
48
49 var zig_test_file_buffer: [4096]u8 = undefined;
50 var zig_test_file_writer = zig_test_file.writer(io, &zig_test_file_buffer);
51 const test_writer = &zig_test_file_writer.interface;
52
53 try test_writer.writeAll(
54 \\//! This file is auto-generated by tools/update_crc_catalog.zig.
55 \\
56 \\const std = @import("std");
57 \\const testing = std.testing;
58 \\const verify = @import("../verify.zig");
59 \\const crc = @import("../crc.zig");
60 \\
61 \\test "crc32 ieee regression" {
62 \\ const Crc = crc.@"CRC-32/ISO-HDLC";
63 \\ try testing.expectEqual(Crc.hash(""), 0x00000000);
64 \\ try testing.expectEqual(Crc.hash("a"), 0xe8b7be43);
65 \\ try testing.expectEqual(Crc.hash("abc"), 0x352441c2);
66 \\}
67 \\
68 \\test "crc32 castagnoli regression" {
69 \\ const Crc = crc.@"CRC-32/ISCSI";
70 \\ try testing.expectEqual(Crc.hash(""), 0x00000000);
71 \\ try testing.expectEqual(Crc.hash("a"), 0xc1d04330);
72 \\ try testing.expectEqual(Crc.hash("abc"), 0x364b3fb7);
73 \\}
74 \\
75 \\test "crc32 koopman regression" {
76 \\ const Crc = crc.@"CRC-32/KOOPMAN";
77 \\ try testing.expectEqual(Crc.hash(""), 0x00000000);
78 \\ try testing.expectEqual(Crc.hash("a"), 0x0da2aa8a);
79 \\ try testing.expectEqual(Crc.hash("abc"), 0xba2322ac);
80 \\}
81 \\
82 );
83
84 var reader: std.Io.Reader = .fixed(catalog_txt);
85
86 while (try reader.takeDelimiter('\n')) |line| {
87 if (line.len == 0 or line[0] == '#')
88 continue;
89
90 var width: []const u8 = undefined;
91 var poly: []const u8 = undefined;
92 var init: []const u8 = undefined;
93 var refin: []const u8 = undefined;
94 var refout: []const u8 = undefined;
95 var xorout: []const u8 = undefined;
96 var check: []const u8 = undefined;
97 var residue: []const u8 = undefined;
98 var name: []const u8 = undefined;
99
100 var it = mem.splitSequence(u8, line, " ");
101 while (it.next()) |property| {
102 const i = mem.find(u8, property, "=").?;
103 const key = property[0..i];
104 const value = property[i + 1 ..];
105 if (mem.eql(u8, key, "width")) {
106 width = value;
107 } else if (mem.eql(u8, key, "poly")) {
108 poly = value;
109 } else if (mem.eql(u8, key, "init")) {
110 init = value;
111 } else if (mem.eql(u8, key, "refin")) {
112 refin = value;
113 } else if (mem.eql(u8, key, "refout")) {
114 refout = value;
115 } else if (mem.eql(u8, key, "xorout")) {
116 xorout = value;
117 } else if (mem.eql(u8, key, "check")) {
118 check = value;
119 } else if (mem.eql(u8, key, "residue")) {
120 residue = value;
121 } else if (mem.eql(u8, key, "name")) {
122 name = mem.trim(u8, value, "\"");
123 } else {
124 unreachable;
125 }
126 }
127
128 if (mem.eql(u8, name, "CRC-32/ISCSI")) {
129 try code_writer.print(
130 \\
131 \\pub const {f} = if (builtin.cpu.hasAll(.x86, &.{{ .@"64bit", .crc32 }}))
132 \\ @import("crc/Crc32c.zig")
133 \\else
134 \\ Generic(u{s}, .{{
135 \\ .polynomial = {s},
136 \\ .initial = {s},
137 \\ .reflect_input = {s},
138 \\ .reflect_output = {s},
139 \\ .xor_output = {s},
140 \\ }});
141 \\
142 , .{ std.zig.fmtId(name), width, poly, init, refin, refout, xorout });
143 } else {
144 try code_writer.print(
145 \\
146 \\pub const {f} = Generic(u{s}, .{{
147 \\ .polynomial = {s},
148 \\ .initial = {s},
149 \\ .reflect_input = {s},
150 \\ .reflect_output = {s},
151 \\ .xor_output = {s},
152 \\}});
153 \\
154 , .{ std.zig.fmtId(name), width, poly, init, refin, refout, xorout });
155 }
156
157 try test_writer.print(
158 \\
159 \\test "{0s}" {{
160 \\ const Crc = crc.{1f};
161 \\
162 \\ try testing.expectEqual(@as(u{2s}, {3s}), Crc.hash("123456789"));
163 \\
164 \\ var c = Crc.init();
165 \\ c.update("1234");
166 \\ c.update("56789");
167 \\ try testing.expectEqual(@as(u{2s}, {3s}), c.final());
168 \\}}
169 \\
170 , .{ name, std.zig.fmtId(name), width, check });
171 }
172
173 try code_writer.writeAll(
174 \\
175 \\pub fn Algorithm(comptime W: type) type {
176 \\ return struct {
177 \\ polynomial: W,
178 \\ initial: W,
179 \\ reflect_input: bool,
180 \\ reflect_output: bool,
181 \\ xor_output: W,
182 \\ };
183 \\}
184 \\
185 \\pub fn Generic(comptime W: type, comptime algorithm: Algorithm(W)) type {
186 \\ return struct {
187 \\ const Self = @This();
188 \\ const I = if (@bitSizeOf(W) < 8) u8 else W;
189 \\ const lookup_table = blk: {
190 \\ @setEvalBranchQuota(2500);
191 \\ const poly = reflect(algorithm.polynomial);
192 \\ var table: [256]I = undefined;
193 \\ for (&table, 0..) |*e, i| {
194 \\ var crc: I = i;
195 \\ if (algorithm.reflect_input) {
196 \\ var j: usize = 0;
197 \\ while (j < 8) : (j += 1) {
198 \\ crc = (crc >> 1) ^ ((crc & 1) * poly);
199 \\ }
200 \\ } else {
201 \\ crc <<= @bitSizeOf(I) - 8;
202 \\ var j: usize = 0;
203 \\ while (j < 8) : (j += 1) {
204 \\ crc = (crc << 1) ^ (((crc >> (@bitSizeOf(I) - 1)) & 1) * poly);
205 \\ }
206 \\ }
207 \\ e.* = crc;
208 \\ }
209 \\ break :blk table;
210 \\ };
211 \\
212 \\ crc: I,
213 \\
214 \\ pub fn init() Self {
215 \\ const initial = reflect(algorithm.initial);
216 \\ return .{ .crc = initial };
217 \\ }
218 \\
219 \\ inline fn tableEntry(index: I) I {
220 \\ const short: u8 = @truncate(index);
221 \\ return lookup_table[short];
222 \\ }
223 \\
224 \\ pub fn update(self: *Self, bytes: []const u8) void {
225 \\ var i: usize = 0;
226 \\ if (@bitSizeOf(I) <= 8) {
227 \\ while (i < bytes.len) : (i += 1) {
228 \\ self.crc = tableEntry(self.crc ^ bytes[i]);
229 \\ }
230 \\ } else if (algorithm.reflect_input) {
231 \\ while (i < bytes.len) : (i += 1) {
232 \\ const table_index = self.crc ^ bytes[i];
233 \\ self.crc = tableEntry(table_index) ^ (self.crc >> 8);
234 \\ }
235 \\ } else {
236 \\ while (i < bytes.len) : (i += 1) {
237 \\ const table_index = (self.crc >> (@bitSizeOf(I) - 8)) ^ bytes[i];
238 \\ self.crc = tableEntry(table_index) ^ (self.crc << 8);
239 \\ }
240 \\ }
241 \\ }
242 \\
243 \\ pub fn final(self: Self) W {
244 \\ var c = self.crc;
245 \\ if (algorithm.reflect_input != algorithm.reflect_output) {
246 \\ c = @bitReverse(c);
247 \\ }
248 \\ if (!algorithm.reflect_output) {
249 \\ c >>= @bitSizeOf(I) - @bitSizeOf(W);
250 \\ }
251 \\ return @intCast(c ^ algorithm.xor_output);
252 \\ }
253 \\
254 \\ pub fn hash(bytes: []const u8) W {
255 \\ var c = Self.init();
256 \\ c.update(bytes);
257 \\ return c.final();
258 \\ }
259 \\
260 \\ fn reflect(x: I) I {
261 \\ const offset = @bitSizeOf(I) - @bitSizeOf(W);
262 \\ if (algorithm.reflect_input)
263 \\ return @bitReverse(x) >> offset
264 \\ else
265 \\ return x << offset;
266 \\ }
267 \\ };
268 \\}
269 \\
270 \\test {
271 \\ _ = @import("crc/test.zig");
272 \\}
273 \\
274 );
275
276 try code_writer.flush();
277 try test_writer.flush();
278}
279
280fn printUsageAndExit(arg0: []const u8) noreturn {
281 const stderr = std.debug.lockStderr(&.{});
282 const w = &stderr.file_writer.interface;
283 printUsage(w, arg0) catch std.process.exit(2);
284 std.process.exit(1);
285}
286
287fn printUsage(w: *std.Io.Writer, arg0: []const u8) std.Io.Writer.Error!void {
288 return w.print(
289 \\Usage: {s} /path/git/zig
290 \\
291 , .{arg0});
292}