| ... | @@ -325,30 +325,59 @@ pub const Manifest = struct { | ... | @@ -325,30 +325,59 @@ pub const Manifest = struct { |
| 325 | have_stat: bool, | 325 | have_stat: bool, |
| 326 | /// Determines whether `File.digest` is populated. | 326 | /// Determines whether `File.digest` is populated. |
| 327 | have_digest: bool, | 327 | have_digest: bool, |
| 328 | contents: packed union(u64) { | 328 | contents: struct { |
| 329 | status: enum(u64) { | 329 | pop_off: enum(u32) { unpopulated = std.math.maxInt(u32), _ }, |
| 330 | unrequested = std.math.maxInt(u64) - 1, | 330 | req_len: enum(u32) { unrequested = std.math.maxInt(u32) - 1, requested = std.math.maxInt(u32), _ }, |
| 331 | requested = std.math.maxInt(u64), | 331 | |
| 332 | _, | 332 | pub const requested: @This() = .{ |
| 333 | pub fn populated(this: @This()) bool { | 333 | .pop_off = .unpopulated, |
| 334 | return switch (this) { | 334 | .req_len = .requested, |
| 335 | .unrequested, .requested => false, | 335 | }; |
| 336 | _ => true, | 336 | |
| 337 | }; | 337 | pub const unrequested: @This() = .{ |
| 338 | } | 338 | .pop_off = .unpopulated, |
| 339 | }, | 339 | .req_len = .unrequested, |
| 340 | populated: packed struct(u64) { | 340 | }; |
| | 341 | |
| | 342 | pub fn populated(p: Populated) @This() { |
| | 343 | return .{ |
| | 344 | .pop_off = @fromBackingInt(p.off), |
| | 345 | .req_len = @fromBackingInt(p.len), |
| | 346 | }; |
| | 347 | } |
| | 348 | |
| | 349 | pub const Unwrapped = union(enum) { |
| | 350 | unrequested, |
| | 351 | requested, |
| | 352 | populated: Populated, |
| | 353 | }; |
| | 354 | |
| | 355 | pub const Populated = struct { |
| 341 | off: u32, | 356 | off: u32, |
| 342 | len: u32, | 357 | len: u32, |
| 343 | }, | 358 | }; |
| | 359 | |
| | 360 | pub fn unwrap(this: @This()) Unwrapped { |
| | 361 | return switch (this.pop_off) { |
| | 362 | .unpopulated => switch (this.req_len) { |
| | 363 | .unrequested => .unrequested, |
| | 364 | .requested => .requested, |
| | 365 | _ => unreachable, |
| | 366 | }, |
| | 367 | _ => .{ .populated = .{ |
| | 368 | .off = @backingInt(this.pop_off), |
| | 369 | .len = @backingInt(this.req_len), |
| | 370 | } }, |
| | 371 | }; |
| | 372 | } |
| 344 | | 373 | |
| 345 | pub fn get(this: @This(), all_contents: []const u8) ?[]const u8 { | 374 | pub fn get(this: @This(), all_contents: []const u8) ?[]const u8 { |
| 346 | return if (this.status.populated()) | 375 | return switch (this.unwrap()) { |
| 347 | all_contents[this.status.populated.off..][0..this.status.populated.len] | 376 | .unrequested, .requested => null, |
| 348 | else | 377 | .populated => |p| all_contents[p.off..][0..p.len], |
| 349 | null; | 378 | }; |
| 350 | } | 379 | } |
| 351 | } align(@alignOf(u32)), | 380 | }, |
| 352 | /// `have_handle` determines whether this is populated. | 381 | /// `have_handle` determines whether this is populated. |
| 353 | handle: union { | 382 | handle: union { |
| 354 | file: Io.File, | 383 | file: Io.File, |
| ... | @@ -369,14 +398,13 @@ pub const Manifest = struct { | ... | @@ -369,14 +398,13 @@ pub const Manifest = struct { |
| 369 | return manifest.input_paths.items[@backingInt(i)]; | 398 | return manifest.input_paths.items[@backingInt(i)]; |
| 370 | } | 399 | } |
| 371 | | 400 | |
| | 401 | pub fn offset(i: @This(), manifest: *const Manifest) File.Offset { |
| | 402 | return manifest.files.keys()[@backingInt(i)]; |
| | 403 | } |
| | 404 | |
| 372 | pub fn contents(i: @This(), manifest: *const Manifest) []const u8 { | 405 | pub fn contents(i: @This(), manifest: *const Manifest) []const u8 { |
| 373 | const input_path = get(i, manifest); | 406 | const input_path = get(i, manifest); |
| 374 | const populated = switch (input_path.contents.status) { | 407 | return input_path.contents.get(manifest.all_input_content.items).?; |
| 375 | .unrequested => unreachable, // Must request contents before calling `check`. | | |
| 376 | .requested => unreachable, // Must call `check` first. | | |
| 377 | _ => input_path.contents.populated, | | |
| 378 | }; | | |
| 379 | return manifest.all_input_content.items[populated.off..][0..populated.len]; | | |
| 380 | } | 408 | } |
| 381 | }; | 409 | }; |
| 382 | | 410 | |
| ... | @@ -454,7 +482,7 @@ pub const Manifest = struct { | ... | @@ -454,7 +482,7 @@ pub const Manifest = struct { |
| 454 | comptime assert(@offsetOf(File, "path_start") - flags_off == 1); | 482 | comptime assert(@offsetOf(File, "path_start") - flags_off == 1); |
| 455 | const hash_start = @backingInt(off) + flags_off; | 483 | const hash_start = @backingInt(off) + flags_off; |
| 456 | const hash_end = mem.findScalarPos(u8, contents, hash_start, 0).?; | 484 | const hash_end = mem.findScalarPos(u8, contents, hash_start, 0).?; |
| 457 | return contents[hash_start..hash_end]; | 485 | return contents[hash_start..hash_end :0]; |
| 458 | } | 486 | } |
| 459 | | 487 | |
| 460 | pub fn pathFallible(off: File.Offset, contents: []const u8) error{InvalidFormat}![:0]const u8 { | 488 | pub fn pathFallible(off: File.Offset, contents: []const u8) error{InvalidFormat}![:0]const u8 { |
| ... | @@ -464,7 +492,7 @@ pub const Manifest = struct { | ... | @@ -464,7 +492,7 @@ pub const Manifest = struct { |
| 464 | } | 492 | } |
| 465 | | 493 | |
| 466 | pub fn path(off: File.Offset, contents: []const u8) [:0]const u8 { | 494 | pub fn path(off: File.Offset, contents: []const u8) [:0]const u8 { |
| 467 | return pathFallible(contents, off) catch unreachable; | 495 | return pathFallible(off, contents) catch unreachable; |
| 468 | } | 496 | } |
| 469 | }; | 497 | }; |
| 470 | | 498 | |
| ... | @@ -501,7 +529,7 @@ pub const Manifest = struct { | ... | @@ -501,7 +529,7 @@ pub const Manifest = struct { |
| 501 | return .{ | 529 | return .{ |
| 502 | .size = file.size, | 530 | .size = file.size, |
| 503 | .inode = file.inode, | 531 | .inode = file.inode, |
| 504 | .mtime = file.mtime, | 532 | .mtime = .fromNanoseconds(file.mtime), |
| 505 | }; | 533 | }; |
| 506 | } | 534 | } |
| 507 | | 535 | |
| ... | @@ -664,9 +692,9 @@ pub const Manifest = struct { | ... | @@ -664,9 +692,9 @@ pub const Manifest = struct { |
| 664 | existing_input_file.have_handle = true; | 692 | existing_input_file.have_handle = true; |
| 665 | }, | 693 | }, |
| 666 | } | 694 | } |
| 667 | if (options.request_contents) switch (existing_input_file.contents.status) { | 695 | if (options.request_contents) switch (existing_input_file.contents.unwrap()) { |
| 668 | .requested, .unrequested => existing_input_file.contents = .{ .status = .requested }, | 696 | .requested, .unrequested => existing_input_file.contents = .requested, |
| 669 | _ => {}, | 697 | .populated => {}, |
| 670 | }; | 698 | }; |
| 671 | const existing_header = m.files.keys()[gop.index].get(m.contents.items); | 699 | const existing_header = m.files.keys()[gop.index].get(m.contents.items); |
| 672 | if (options.stat) |stat| { | 700 | if (options.stat) |stat| { |
| ... | @@ -689,7 +717,7 @@ pub const Manifest = struct { | ... | @@ -689,7 +717,7 @@ pub const Manifest = struct { |
| 689 | .file => |opt_file| if (opt_file) |file| .{ .file = file } else undefined, | 717 | .file => |opt_file| if (opt_file) |file| .{ .file = file } else undefined, |
| 690 | .dir => |opt_dir| if (opt_dir) |dir| .{ .dir = dir } else undefined, | 718 | .dir => |opt_dir| if (opt_dir) |dir| .{ .dir = dir } else undefined, |
| 691 | }, | 719 | }, |
| 692 | .contents = .{ .status = if (options.request_contents) .requested else .unrequested }, | 720 | .contents = if (options.request_contents) .requested else .unrequested, |
| 693 | .have_digest = false, | 721 | .have_digest = false, |
| 694 | .have_stat = options.stat != null, | 722 | .have_stat = options.stat != null, |
| 695 | }); | 723 | }); |
| ... | @@ -712,6 +740,7 @@ pub const Manifest = struct { | ... | @@ -712,6 +740,7 @@ pub const Manifest = struct { |
| 712 | pub const CheckResult = union(enum) { | 740 | pub const CheckResult = union(enum) { |
| 713 | hit, | 741 | hit, |
| 714 | incomplete_manifest, | 742 | incomplete_manifest, |
| | 743 | invalid_manifest, |
| 715 | path_deleted: File.Offset, | 744 | path_deleted: File.Offset, |
| 716 | directory_status_changed: File.Offset, | 745 | directory_status_changed: File.Offset, |
| 717 | metadata_changed: File.Offset, | 746 | metadata_changed: File.Offset, |
| ... | @@ -858,7 +887,7 @@ pub const Manifest = struct { | ... | @@ -858,7 +887,7 @@ pub const Manifest = struct { |
| 858 | // We've just had a miss with the shared lock, and upgraded to an exclusive lock. Someone | 887 | // We've just had a miss with the shared lock, and upgraded to an exclusive lock. Someone |
| 859 | // else might have modified the digest, so we need to check again before deciding to miss. | 888 | // else might have modified the digest, so we need to check again before deciding to miss. |
| 860 | // Before trying again, we must reset `man.hash.hasher` and `man.files`. | 889 | // Before trying again, we must reset `man.hash.hasher` and `man.files`. |
| 861 | // This is basically just the first half of `unhit`. | 890 | // This is basically just the first half of `undoCheck`. |
| 862 | man.hash.hasher = hasher_init; | 891 | man.hash.hasher = hasher_init; |
| 863 | man.hash.hasher.update(&input_digest); | 892 | man.hash.hasher.update(&input_digest); |
| 864 | man.shrinkFilesToInput(); | 893 | man.shrinkFilesToInput(); |
| ... | @@ -870,7 +899,7 @@ pub const Manifest = struct { | ... | @@ -870,7 +899,7 @@ pub const Manifest = struct { |
| 870 | // Cache miss, but `checkLocked` guarantees that all input files have their digests computed, even on a | 899 | // Cache miss, but `checkLocked` guarantees that all input files have their digests computed, even on a |
| 871 | // cache miss, which is needed because they will be used in the manifest digest. | 900 | // cache miss, which is needed because they will be used in the manifest digest. |
| 872 | man.dirty = true; | 901 | man.dirty = true; |
| 873 | unhit(man, &input_digest); | 902 | undoCheck(man, &input_digest); |
| 874 | return miss_result; | 903 | return miss_result; |
| 875 | } | 904 | } |
| 876 | | 905 | |
| ... | @@ -918,7 +947,7 @@ pub const Manifest = struct { | ... | @@ -918,7 +947,7 @@ pub const Manifest = struct { |
| 918 | // contents on disk, and then later truncate the contents array. | 947 | // contents on disk, and then later truncate the contents array. |
| 919 | // | 948 | // |
| 920 | // The file on disk already supposedly includes the extra null byte that we have to maintain. | 949 | // The file on disk already supposedly includes the extra null byte that we have to maintain. |
| 921 | const input_contents_len = m.contents.len; | 950 | const input_contents_len = m.contents.items.len; |
| 922 | // After this resize, every return statement needs to modify m.contents length. | 951 | // After this resize, every return statement needs to modify m.contents length. |
| 923 | try m.contents.resize(gpa, input_contents_len + manifest_size); | 952 | try m.contents.resize(gpa, input_contents_len + manifest_size); |
| 924 | errdefer m.contents.shrinkRetainingCapacity(input_contents_len); | 953 | errdefer m.contents.shrinkRetainingCapacity(input_contents_len); |
| ... | @@ -931,7 +960,7 @@ pub const Manifest = struct { | ... | @@ -931,7 +960,7 @@ pub const Manifest = struct { |
| 931 | if (n != manifest_size) return checkLockedMiss(m, input_contents_len, .incomplete_manifest); | 960 | if (n != manifest_size) return checkLockedMiss(m, input_contents_len, .incomplete_manifest); |
| 932 | } | 961 | } |
| 933 | const disk_contents = m.contents.items[0..manifest_size]; | 962 | const disk_contents = m.contents.items[0..manifest_size]; |
| 934 | const input_contents = m.contents.items[manifest_size..input_contents_len]; | 963 | const input_contents = m.contents.items[manifest_size..][0..input_contents_len]; |
| 935 | | 964 | |
| 936 | // First the input files section, which must match our input files, otherwise it's invalid or incomplete. | 965 | // First the input files section, which must match our input files, otherwise it's invalid or incomplete. |
| 937 | var off: usize = 0; | 966 | var off: usize = 0; |
| ... | @@ -955,14 +984,16 @@ pub const Manifest = struct { | ... | @@ -955,14 +984,16 @@ pub const Manifest = struct { |
| 955 | // Validate and check discovered files. | 984 | // Validate and check discovered files. |
| 956 | while (off + 1 < disk_contents.len) { | 985 | while (off + 1 < disk_contents.len) { |
| 957 | const file_off: File.Offset = @fromBackingInt(@intCast(off)); | 986 | const file_off: File.Offset = @fromBackingInt(@intCast(off)); |
| 958 | const file = try file_off.getFallible(disk_contents); | 987 | const file = file_off.getFallible(disk_contents) catch |
| | 988 | return checkLockedMiss(m, input_contents_len, .invalid_manifest); |
| 959 | if (file.flags.prefix >= m.cache.prefixes_len) | 989 | if (file.flags.prefix >= m.cache.prefixes_len) |
| 960 | return checkLockedMiss(m, input_contents_len, .invalid_manifest); | 990 | return checkLockedMiss(m, input_contents_len, .invalid_manifest); |
| 961 | const path = try file_off.pathFallible(disk_contents); | 991 | const path = file_off.pathFallible(disk_contents) catch |
| | 992 | return checkLockedMiss(m, input_contents_len, .invalid_manifest); |
| 962 | if (path.len == 0) return checkLockedMiss(m, input_contents_len, .invalid_manifest); | 993 | if (path.len == 0) return checkLockedMiss(m, input_contents_len, .invalid_manifest); |
| 963 | | 994 | |
| 964 | try m.files.putContext(gpa, file_off, {}, .{ .contents = disk_contents }); | 995 | try m.files.putContext(gpa, file_off, {}, .{ .contents = disk_contents }); |
| 965 | const result = try checkDiscoveredPath(file_off, file_off.path(disk_contents)); | 996 | const result = try checkDiscoveredPath(m, file_off, disk_contents); |
| 966 | if (result != .hit) return checkLockedMiss(m, input_contents_len, result); | 997 | if (result != .hit) return checkLockedMiss(m, input_contents_len, result); |
| 967 | | 998 | |
| 968 | off += File.sizeOf(path.len); | 999 | off += File.sizeOf(path.len); |
| ... | @@ -994,7 +1025,10 @@ pub const Manifest = struct { | ... | @@ -994,7 +1025,10 @@ pub const Manifest = struct { |
| 994 | result: CheckResult, | 1025 | result: CheckResult, |
| 995 | ) CheckError!CheckResult { | 1026 | ) CheckError!CheckResult { |
| 996 | const file_offs = m.files.keys(); | 1027 | const file_offs = m.files.keys(); |
| 997 | const off = if (file_offs.len - next_file_index == 0) input_contents_len else file_offs[next_file_index]; | 1028 | const off = if (file_offs.len - next_file_index == 0) |
| | 1029 | input_contents_len |
| | 1030 | else |
| | 1031 | @backingInt(file_offs[next_file_index]); |
| 998 | const copy_len = input_contents_len - off; | 1032 | const copy_len = input_contents_len - off; |
| 999 | @memcpy( | 1033 | @memcpy( |
| 1000 | m.contents.items[off..][0..copy_len], | 1034 | m.contents.items[off..][0..copy_len], |
| ... | @@ -1021,7 +1055,7 @@ pub const Manifest = struct { | ... | @@ -1021,7 +1055,7 @@ pub const Manifest = struct { |
| 1021 | m: *Manifest, | 1055 | m: *Manifest, |
| 1022 | file_off: File.Offset, | 1056 | file_off: File.Offset, |
| 1023 | input_path: *InputPath, | 1057 | input_path: *InputPath, |
| 1024 | contents: []const u8, | 1058 | contents: []u8, |
| 1025 | ) CheckError!void { | 1059 | ) CheckError!void { |
| 1026 | const cache = m.cache; | 1060 | const cache = m.cache; |
| 1027 | const io = cache.io; | 1061 | const io = cache.io; |
| ... | @@ -1031,7 +1065,10 @@ pub const Manifest = struct { | ... | @@ -1031,7 +1065,10 @@ pub const Manifest = struct { |
| 1031 | | 1065 | |
| 1032 | if (input_path.have_digest) return; | 1066 | if (input_path.have_digest) return; |
| 1033 | | 1067 | |
| 1034 | const stat_path_ok = input_path.contents.status.populated() or input_file.flags.metadata_only; | 1068 | const stat_path_ok = switch (input_path.contents.unwrap()) { |
| | 1069 | .populated => true, |
| | 1070 | .unrequested, .requested => input_file.flags.metadata_only, |
| | 1071 | }; |
| 1035 | if (!input_path.have_stat and stat_path_ok) { | 1072 | if (!input_path.have_stat and stat_path_ok) { |
| 1036 | // Since this is an input file, FileNotFound counts as a failure, not a cache miss. | 1073 | // Since this is an input file, FileNotFound counts as a failure, not a cache miss. |
| 1037 | const actual_stat = switch (input_path.getHandle(input_file)) { | 1074 | const actual_stat = switch (input_path.getHandle(input_file)) { |
| ... | @@ -1074,7 +1111,7 @@ pub const Manifest = struct { | ... | @@ -1074,7 +1111,7 @@ pub const Manifest = struct { |
| 1074 | return; | 1111 | return; |
| 1075 | } | 1112 | } |
| 1076 | | 1113 | |
| 1077 | if (input_path.contents.get()) |file_or_dir_contents| { | 1114 | if (input_path.contents.get(m.all_input_content.items)) |file_or_dir_contents| { |
| 1078 | assert(stat_path_ok); | 1115 | assert(stat_path_ok); |
| 1079 | var hasher = hasher_init; | 1116 | var hasher = hasher_init; |
| 1080 | hasher.update(file_or_dir_contents); | 1117 | hasher.update(file_or_dir_contents); |
| ... | @@ -1122,12 +1159,14 @@ pub const Manifest = struct { | ... | @@ -1122,12 +1159,14 @@ pub const Manifest = struct { |
| 1122 | .err = e, | 1159 | .err = e, |
| 1123 | } }), | 1160 | } }), |
| 1124 | }; | 1161 | }; |
| 1125 | defer if (input_path.contents.status == .requested) { | 1162 | defer switch (input_path.contents.unwrap()) { |
| 1126 | input_path.contents = .{ .populated = .{ | 1163 | .requested => input_path.contents = .populated(.{ |
| 1127 | .off = @intCast(dir_contents_start), | 1164 | .off = @intCast(dir_contents_start), |
| 1128 | .len = @intCast(m.all_input_content.items.len - dir_contents_start), | 1165 | .len = @intCast(m.all_input_content.items.len - dir_contents_start), |
| 1129 | } }; | 1166 | }), |
| 1130 | } else m.all_input_content.shrinkRetainingCapacity(dir_contents_start); | 1167 | .unrequested => m.all_input_content.shrinkRetainingCapacity(dir_contents_start), |
| | 1168 | .populated => unreachable, |
| | 1169 | }; |
| 1131 | } else { | 1170 | } else { |
| 1132 | const opened_file = if (input_path.have_handle) | 1171 | const opened_file = if (input_path.have_handle) |
| 1133 | input_path.handle.file | 1172 | input_path.handle.file |
| ... | @@ -1164,7 +1203,11 @@ pub const Manifest = struct { | ... | @@ -1164,7 +1203,11 @@ pub const Manifest = struct { |
| 1164 | } }), | 1203 | } }), |
| 1165 | }; | 1204 | }; |
| 1166 | | 1205 | |
| 1167 | if (input_path.contents.status == .requested) @panic("TODO"); | 1206 | switch (input_path.contents.unwrap()) { |
| | 1207 | .requested => @panic("TODO"), |
| | 1208 | .unrequested => {}, |
| | 1209 | .populated => unreachable, |
| | 1210 | } |
| 1168 | } | 1211 | } |
| 1169 | } | 1212 | } |
| 1170 | | 1213 | |
| ... | @@ -1173,8 +1216,8 @@ pub const Manifest = struct { | ... | @@ -1173,8 +1216,8 @@ pub const Manifest = struct { |
| 1173 | m: *Manifest, | 1216 | m: *Manifest, |
| 1174 | file_off: File.Offset, | 1217 | file_off: File.Offset, |
| 1175 | input_path: *InputPath, | 1218 | input_path: *InputPath, |
| 1176 | disk_contents: []const u8, | 1219 | disk_contents: []u8, |
| 1177 | input_contents: []const u8, | 1220 | input_contents: []u8, |
| 1178 | ) CheckError!CheckResult { | 1221 | ) CheckError!CheckResult { |
| 1179 | const cache = m.cache; | 1222 | const cache = m.cache; |
| 1180 | const io = cache.io; | 1223 | const io = cache.io; |
| ... | @@ -1191,7 +1234,7 @@ pub const Manifest = struct { | ... | @@ -1191,7 +1234,7 @@ pub const Manifest = struct { |
| 1191 | } | 1234 | } |
| 1192 | | 1235 | |
| 1193 | if (input_path.have_stat) { | 1236 | if (input_path.have_stat) { |
| 1194 | const changed = try disk_file.setStatChanged(m, input_file.stat); | 1237 | const changed = try disk_file.setStatChanged(m, input_file.stat()); |
| 1195 | if (!changed) return .hit; | 1238 | if (!changed) return .hit; |
| 1196 | } | 1239 | } |
| 1197 | | 1240 | |
| ... | @@ -1239,7 +1282,7 @@ pub const Manifest = struct { | ... | @@ -1239,7 +1282,7 @@ pub const Manifest = struct { |
| 1239 | return .{ .metadata_changed = file_off }; | 1282 | return .{ .metadata_changed = file_off }; |
| 1240 | } | 1283 | } |
| 1241 | | 1284 | |
| 1242 | if (input_path.contents.get()) |file_or_dir_contents| { | 1285 | if (input_path.contents.get(m.all_input_content.items)) |file_or_dir_contents| { |
| 1243 | var hasher = hasher_init; | 1286 | var hasher = hasher_init; |
| 1244 | hasher.update(file_or_dir_contents); | 1287 | hasher.update(file_or_dir_contents); |
| 1245 | hasher.final(&disk_file.digest); | 1288 | hasher.final(&disk_file.digest); |
| ... | @@ -1287,12 +1330,14 @@ pub const Manifest = struct { | ... | @@ -1287,12 +1330,14 @@ pub const Manifest = struct { |
| 1287 | .err = e, | 1330 | .err = e, |
| 1288 | } }), | 1331 | } }), |
| 1289 | }; | 1332 | }; |
| 1290 | defer if (input_path.contents.status == .requested) { | 1333 | defer switch (input_path.contents.unwrap()) { |
| 1291 | input_path.contents = .{ .populated = .{ | 1334 | .requested => input_path.contents = .populated(.{ |
| 1292 | .off = @intCast(dir_contents_start), | 1335 | .off = @intCast(dir_contents_start), |
| 1293 | .len = @intCast(m.all_input_content.items.len - dir_contents_start), | 1336 | .len = @intCast(m.all_input_content.items.len - dir_contents_start), |
| 1294 | } }; | 1337 | }), |
| 1295 | } else m.all_input_content.shrinkRetainingCapacity(dir_contents_start); | 1338 | .unrequested => m.all_input_content.shrinkRetainingCapacity(dir_contents_start), |
| | 1339 | .populated => unreachable, |
| | 1340 | }; |
| 1296 | | 1341 | |
| 1297 | if (mem.eql(u8, &disk_file.digest, &input_file.digest)) return .hit; | 1342 | if (mem.eql(u8, &disk_file.digest, &input_file.digest)) return .hit; |
| 1298 | return .{ .contents_changed = file_off }; | 1343 | return .{ .contents_changed = file_off }; |
| ... | @@ -1330,30 +1375,30 @@ pub const Manifest = struct { | ... | @@ -1330,30 +1375,30 @@ pub const Manifest = struct { |
| 1330 | .err = e, | 1375 | .err = e, |
| 1331 | } }), | 1376 | } }), |
| 1332 | }; | 1377 | }; |
| 1333 | if (input_path.contents.status == .requested) @panic("TODO"); | 1378 | switch (input_path.contents.unwrap()) { |
| | 1379 | .requested => @panic("TODO"), |
| | 1380 | .unrequested => {}, |
| | 1381 | .populated => unreachable, |
| | 1382 | } |
| 1334 | | 1383 | |
| 1335 | if (mem.eql(u8, &disk_file.digest, &input_file.digest)) return .hit; | 1384 | if (mem.eql(u8, &disk_file.digest, &input_file.digest)) return .hit; |
| 1336 | return .{ .contents_changed = file_off }; | 1385 | return .{ .contents_changed = file_off }; |
| 1337 | } | 1386 | } |
| 1338 | } | 1387 | } |
| 1339 | | 1388 | |
| 1340 | fn checkDiscoveredPath( | 1389 | fn checkDiscoveredPath(m: *Manifest, file_off: File.Offset, contents: []u8) CheckError!CheckResult { |
| 1341 | m: *Manifest, | | |
| 1342 | file_off: File.Offset, | | |
| 1343 | file_path: [:0]const u8, | | |
| 1344 | contents: []const u8, | | |
| 1345 | ) CheckError!CheckResult { | | |
| 1346 | const file = file_off.get(contents); | 1390 | const file = file_off.get(contents); |
| 1347 | const cache = m.cache; | 1391 | const cache = m.cache; |
| 1348 | const gpa = cache.gpa; | 1392 | const gpa = cache.gpa; |
| 1349 | const io = cache.io; | 1393 | const io = cache.io; |
| 1350 | const parent_dir = cache.prefixes()[file.flags.prefix].handle; | 1394 | const parent_dir = cache.prefixes()[file.flags.prefix].handle; |
| | 1395 | const file_path = file_off.path(contents); |
| 1351 | | 1396 | |
| 1352 | if (file.flags.metadata_only) { | 1397 | if (file.flags.metadata_only) { |
| 1353 | const actual_stat = parent_dir.statFile(io, file_path, .{}) catch |err| switch (err) { | 1398 | const actual_stat = parent_dir.statFile(io, file_path, .{}) catch |err| switch (err) { |
| 1354 | error.FileNotFound => return .{ .path_deleted = file_off }, | 1399 | error.FileNotFound => return .{ .path_deleted = file_off }, |
| 1355 | error.Canceled => |e| return e, | 1400 | error.Canceled => |e| return e, |
| 1356 | else => |e| return fail(m, .{ .file_stat = .{ | 1401 | else => |e| return fail(&m.diagnostic, .{ .file_stat = .{ |
| 1357 | .file_offset = file_off, | 1402 | .file_offset = file_off, |
| 1358 | .err = e, | 1403 | .err = e, |
| 1359 | } }), | 1404 | } }), |
| ... | @@ -1376,7 +1421,7 @@ pub const Manifest = struct { | ... | @@ -1376,7 +1421,7 @@ pub const Manifest = struct { |
| 1376 | error.FileNotFound => return .{ .path_deleted = file_off }, | 1421 | error.FileNotFound => return .{ .path_deleted = file_off }, |
| 1377 | error.NotDir => return .{ .directory_status_changed = file_off }, | 1422 | error.NotDir => return .{ .directory_status_changed = file_off }, |
| 1378 | error.Canceled => |e| return e, | 1423 | error.Canceled => |e| return e, |
| 1379 | else => |e| return fail(m, .{ .file_open = .{ | 1424 | else => |e| return fail(&m.diagnostic, .{ .file_open = .{ |
| 1380 | .file_offset = file_off, | 1425 | .file_offset = file_off, |
| 1381 | .err = e, | 1426 | .err = e, |
| 1382 | } }), | 1427 | } }), |
| ... | @@ -1385,7 +1430,7 @@ pub const Manifest = struct { | ... | @@ -1385,7 +1430,7 @@ pub const Manifest = struct { |
| 1385 | | 1430 | |
| 1386 | const actual_stat = opened_dir.stat(io) catch |err| switch (err) { | 1431 | const actual_stat = opened_dir.stat(io) catch |err| switch (err) { |
| 1387 | error.Canceled => |e| return e, | 1432 | error.Canceled => |e| return e, |
| 1388 | else => |e| return fail(m, .{ .file_stat = .{ | 1433 | else => |e| return fail(&m.diagnostic, .{ .file_stat = .{ |
| 1389 | .file_offset = file_off, | 1434 | .file_offset = file_off, |
| 1390 | .err = e, | 1435 | .err = e, |
| 1391 | } }), | 1436 | } }), |
| ... | @@ -1394,9 +1439,9 @@ pub const Manifest = struct { | ... | @@ -1394,9 +1439,9 @@ pub const Manifest = struct { |
| 1394 | const prev_digest: BinDigest = file.digest; | 1439 | const prev_digest: BinDigest = file.digest; |
| 1395 | const dir_contents_start = m.all_input_content.items.len; | 1440 | const dir_contents_start = m.all_input_content.items.len; |
| 1396 | defer m.all_input_content.shrinkRetainingCapacity(dir_contents_start); | 1441 | defer m.all_input_content.shrinkRetainingCapacity(dir_contents_start); |
| 1397 | hashDir(gpa, io, opened_dir, &file.digest, &m.all_input_contents) catch |err| switch (err) { | 1442 | hashDir(gpa, io, opened_dir, &file.digest, &m.all_input_content) catch |err| switch (err) { |
| 1398 | error.Canceled, error.OutOfMemory => |e| return e, | 1443 | error.Canceled, error.OutOfMemory => |e| return e, |
| 1399 | else => |e| return fail(m, .{ .file_read = .{ | 1444 | else => |e| return fail(&m.diagnostic, .{ .file_read = .{ |
| 1400 | .file_offset = file_off, | 1445 | .file_offset = file_off, |
| 1401 | .err = e, | 1446 | .err = e, |
| 1402 | } }), | 1447 | } }), |
| ... | @@ -1411,7 +1456,7 @@ pub const Manifest = struct { | ... | @@ -1411,7 +1456,7 @@ pub const Manifest = struct { |
| 1411 | error.FileNotFound => return .{ .path_deleted = file_off }, | 1456 | error.FileNotFound => return .{ .path_deleted = file_off }, |
| 1412 | error.IsDir => return .{ .directory_status_changed = file_off }, | 1457 | error.IsDir => return .{ .directory_status_changed = file_off }, |
| 1413 | error.Canceled => |e| return e, | 1458 | error.Canceled => |e| return e, |
| 1414 | else => |e| return fail(m, .{ .file_open = .{ | 1459 | else => |e| return fail(&m.diagnostic, .{ .file_open = .{ |
| 1415 | .file_offset = file_off, | 1460 | .file_offset = file_off, |
| 1416 | .err = e, | 1461 | .err = e, |
| 1417 | } }), | 1462 | } }), |
| ... | @@ -1420,7 +1465,7 @@ pub const Manifest = struct { | ... | @@ -1420,7 +1465,7 @@ pub const Manifest = struct { |
| 1420 | | 1465 | |
| 1421 | const actual_stat = opened_file.stat(io) catch |err| switch (err) { | 1466 | const actual_stat = opened_file.stat(io) catch |err| switch (err) { |
| 1422 | error.Canceled => |e| return e, | 1467 | error.Canceled => |e| return e, |
| 1423 | else => |e| return fail(m, .{ .file_stat = .{ | 1468 | else => |e| return fail(&m.diagnostic, .{ .file_stat = .{ |
| 1424 | .file_offset = file_off, | 1469 | .file_offset = file_off, |
| 1425 | .err = e, | 1470 | .err = e, |
| 1426 | } }), | 1471 | } }), |
| ... | @@ -1430,7 +1475,7 @@ pub const Manifest = struct { | ... | @@ -1430,7 +1475,7 @@ pub const Manifest = struct { |
| 1430 | const prev_digest: BinDigest = file.digest; | 1475 | const prev_digest: BinDigest = file.digest; |
| 1431 | hashFile(io, opened_file, &file.digest) catch |err| switch (err) { | 1476 | hashFile(io, opened_file, &file.digest) catch |err| switch (err) { |
| 1432 | error.Canceled => |e| return e, | 1477 | error.Canceled => |e| return e, |
| 1433 | else => |e| return fail(m, .{ .file_read = .{ | 1478 | else => |e| return fail(&m.diagnostic, .{ .file_read = .{ |
| 1434 | .file_offset = file_off, | 1479 | .file_offset = file_off, |
| 1435 | .err = e, | 1480 | .err = e, |
| 1436 | } }), | 1481 | } }), |
| ... | @@ -1453,7 +1498,7 @@ pub const Manifest = struct { | ... | @@ -1453,7 +1498,7 @@ pub const Manifest = struct { |
| 1453 | /// not including discovered files). | 1498 | /// not including discovered files). |
| 1454 | /// | 1499 | /// |
| 1455 | /// Assumes that `digest` is populated for all input files. | 1500 | /// Assumes that `digest` is populated for all input files. |
| 1456 | pub fn unhit(man: *Manifest, input_digest: *const BinDigest) void { | 1501 | pub fn undoCheck(man: *Manifest, input_digest: *const BinDigest) void { |
| 1457 | // Reset the hash. | 1502 | // Reset the hash. |
| 1458 | man.hash.hasher = hasher_init; | 1503 | man.hash.hasher = hasher_init; |
| 1459 | man.hash.hasher.update(input_digest); | 1504 | man.hash.hasher.update(input_digest); |
| ... | @@ -2117,8 +2162,7 @@ test "cache file and then recall it" { | ... | @@ -2117,8 +2162,7 @@ test "cache file and then recall it" { |
| 2117 | ch.hash.addBytes("1234"); | 2162 | ch.hash.addBytes("1234"); |
| 2118 | _ = try ch.addInputPath(.initCwd(temp_file), .{}); | 2163 | _ = try ch.addInputPath(.initCwd(temp_file), .{}); |
| 2119 | | 2164 | |
| 2120 | // There should be nothing in the cache | 2165 | try testing.expectEqual(.incomplete_manifest, try ch.check(.none)); |
| 2121 | try testing.expectEqual(.miss, try ch.check(.none)); | | |
| 2122 | | 2166 | |
| 2123 | digest1 = ch.final(); | 2167 | digest1 = ch.final(); |
| 2124 | try ch.finalize(); | 2168 | try ch.finalize(); |
| ... | @@ -2185,8 +2229,7 @@ test "check that changing a file makes cache fail" { | ... | @@ -2185,8 +2229,7 @@ test "check that changing a file makes cache fail" { |
| 2185 | man.hash.addBytes("1234"); | 2229 | man.hash.addBytes("1234"); |
| 2186 | const temp_file_idx = try man.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); | 2230 | const temp_file_idx = try man.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); |
| 2187 | | 2231 | |
| 2188 | // There should be nothing in the cache | 2232 | try testing.expectEqual(.incomplete_manifest, try man.check(.none)); |
| 2189 | try testing.expectEqual(.miss, try man.check(.none)); | | |
| 2190 | | 2233 | |
| 2191 | try testing.expectEqualStrings(original_temp_file_contents, temp_file_idx.contents(&man)); | 2234 | try testing.expectEqualStrings(original_temp_file_contents, temp_file_idx.contents(&man)); |
| 2192 | | 2235 | |
| ... | @@ -2204,11 +2247,13 @@ test "check that changing a file makes cache fail" { | ... | @@ -2204,11 +2247,13 @@ test "check that changing a file makes cache fail" { |
| 2204 | man.hash.addBytes("1234"); | 2247 | man.hash.addBytes("1234"); |
| 2205 | const temp_file_idx = try man.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); | 2248 | const temp_file_idx = try man.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); |
| 2206 | | 2249 | |
| 2207 | // A file that we depend on has been updated, so the cache should not contain an entry for it | 2250 | // The one input file changed. |
| 2208 | try testing.expectEqual(.miss, try man.check(.none)); | 2251 | try testing.expectEqual( |
| | 2252 | @as(Manifest.CheckResult, .{ .contents_changed = temp_file_idx.offset(&man) }), |
| | 2253 | try man.check(.none), |
| | 2254 | ); |
| 2209 | | 2255 | |
| 2210 | // The cache system does not keep the contents of re-hashed input files. | 2256 | try testing.expectEqualStrings(updated_temp_file_contents, temp_file_idx.contents(&man)); |
| 2211 | try testing.expectEqual(false, temp_file_idx.get(&man).contents.status.populated()); | | |
| 2212 | | 2257 | |
| 2213 | digest2 = man.final(); | 2258 | digest2 = man.final(); |
| 2214 | | 2259 | |
| ... | @@ -2248,8 +2293,7 @@ test "no file inputs" { | ... | @@ -2248,8 +2293,7 @@ test "no file inputs" { |
| 2248 | | 2293 | |
| 2249 | man.hash.addBytes("1234"); | 2294 | man.hash.addBytes("1234"); |
| 2250 | | 2295 | |
| 2251 | // There should be nothing in the cache | 2296 | try testing.expectEqual(.incomplete_manifest, try man.check(.none)); |
| 2252 | try testing.expectEqual(.miss, try man.check(.none)); | | |
| 2253 | | 2297 | |
| 2254 | digest1 = man.final(); | 2298 | digest1 = man.final(); |
| 2255 | | 2299 | |
| ... | @@ -2306,31 +2350,30 @@ test "Manifest with files added after initial hash work" { | ... | @@ -2306,31 +2350,30 @@ test "Manifest with files added after initial hash work" { |
| 2306 | defer cache.manifest_dir.close(io); | 2350 | defer cache.manifest_dir.close(io); |
| 2307 | | 2351 | |
| 2308 | { | 2352 | { |
| 2309 | var ch = cache.obtain(); | 2353 | var man = cache.obtain(); |
| 2310 | defer ch.deinit(); | 2354 | defer man.deinit(); |
| 2311 | | 2355 | |
| 2312 | ch.hash.addBytes("1234"); | 2356 | man.hash.addBytes("1234"); |
| 2313 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); | 2357 | _ = try man.addInputPath(.initCwd(temp_file1), .{}); |
| 2314 | | 2358 | |
| 2315 | // There should be nothing in the cache | 2359 | try testing.expectEqual(.incomplete_manifest, try man.check(.none)); |
| 2316 | try testing.expectEqual(.miss, try ch.check(.none)); | | |
| 2317 | | 2360 | |
| 2318 | _ = try ch.addDiscoveredPath(.{ .discovered_path = .{ .unresolved = .initCwd(temp_file2) } }); | 2361 | try man.addDiscoveredPath(.{ .discovered_path = .{ .unresolved = .initCwd(temp_file2) } }); |
| 2319 | | 2362 | |
| 2320 | digest1 = ch.final(); | 2363 | digest1 = man.final(); |
| 2321 | try ch.finalize(); | 2364 | try man.finalize(); |
| 2322 | } | 2365 | } |
| 2323 | { | 2366 | { |
| 2324 | var ch = cache.obtain(); | 2367 | var man = cache.obtain(); |
| 2325 | defer ch.deinit(); | 2368 | defer man.deinit(); |
| 2326 | | 2369 | |
| 2327 | ch.hash.addBytes("1234"); | 2370 | man.hash.addBytes("1234"); |
| 2328 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); | 2371 | _ = try man.addInputPath(.initCwd(temp_file1), .{}); |
| 2329 | | 2372 | |
| 2330 | try testing.expect(.hit == try ch.check(.none)); | 2373 | try testing.expect(.hit == try man.check(.none)); |
| 2331 | digest2 = ch.final(); | 2374 | digest2 = man.final(); |
| 2332 | | 2375 | |
| 2333 | try testing.expectEqual(false, ch.have_exclusive_lock); | 2376 | try testing.expectEqual(false, man.have_exclusive_lock); |
| 2334 | } | 2377 | } |
| 2335 | try testing.expect(mem.eql(u8, &digest1, &digest2)); | 2378 | try testing.expect(mem.eql(u8, &digest1, &digest2)); |
| 2336 | | 2379 | |
| ... | @@ -2344,20 +2387,24 @@ test "Manifest with files added after initial hash work" { | ... | @@ -2344,20 +2387,24 @@ test "Manifest with files added after initial hash work" { |
| 2344 | } | 2387 | } |
| 2345 | | 2388 | |
| 2346 | { | 2389 | { |
| 2347 | var ch = cache.obtain(); | 2390 | var man = cache.obtain(); |
| 2348 | defer ch.deinit(); | 2391 | defer man.deinit(); |
| 2349 | | 2392 | |
| 2350 | ch.hash.addBytes("1234"); | 2393 | man.hash.addBytes("1234"); |
| 2351 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); | 2394 | _ = try man.addInputPath(.initCwd(temp_file1), .{}); |
| 2352 | | 2395 | |
| 2353 | // A file that we depend on has been updated, so the cache should not contain an entry for it | 2396 | switch (try man.check(.none)) { |
| 2354 | try testing.expectEqual(.miss, try ch.check(.none)); | 2397 | .contents_changed => |off| { |
| | 2398 | try testing.expectEqualStrings(temp_file2, off.path(man.contents.items)); |
| | 2399 | }, |
| | 2400 | else => return error.TestFailed, |
| | 2401 | } |
| 2355 | | 2402 | |
| 2356 | _ = try ch.addDiscoveredPath(.{ .discovered_path = .{ .unresolved = .initCwd(temp_file2) } }); | 2403 | try man.addDiscoveredPath(.{ .discovered_path = .{ .unresolved = .initCwd(temp_file2) } }); |
| 2357 | | 2404 | |
| 2358 | digest3 = ch.final(); | 2405 | digest3 = man.final(); |
| 2359 | | 2406 | |
| 2360 | try ch.finalize(); | 2407 | try man.finalize(); |
| 2361 | } | 2408 | } |
| 2362 | | 2409 | |
| 2363 | try testing.expect(!mem.eql(u8, &digest1, &digest3)); | 2410 | try testing.expect(!mem.eql(u8, &digest1, &digest3)); |