| 1 | const std = @import("std"); |
| 2 | const Io = std.Io; |
| 3 | const Dir = std.Io.Dir; |
| 4 | const mem = std.mem; |
| 5 | |
| 6 | const catalog_txt = @embedFile("crc/catalog.txt"); |
| 7 | |
| 8 | pub 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 | |
| 15 | fn @"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 | |
| 280 | fn 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 | |
| 287 | fn 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 | } |