| ... | ... | @@ -67,7 +67,8 @@ fn appendPrefixedPath(cache: *const Cache, contents: *std.ArrayList(u8), prefixe |
| 67 | 67 | const end = contents.items.len + prefixed_path.sub_path.len; |
| 68 | 68 | const needed_alignment = @alignOf(Manifest.File) - (end % @alignOf(Manifest.File)); |
| 69 | 69 | assert(needed_alignment >= 1); // Always need at least a null byte. |
| 70 | | try contents.ensureTotalCapacity(cache.gpa, end + needed_alignment); |
| 70 | // Extra +1 here is due to the requirement to keep at least 1 unused capacity in `Manifest.contents` at all times. |
| 71 | try contents.ensureTotalCapacity(cache.gpa, end + needed_alignment + 1); |
| 71 | 72 | contents.appendSliceAssumeCapacity(prefixed_path.sub_path); |
| 72 | 73 | contents.appendNTimesAssumeCapacity(0, needed_alignment); |
| 73 | 74 | return prefixed_path.prefix; |
| ... | ... | @@ -97,7 +98,10 @@ fn resolveAppendPath(cache: *const Cache, contents: *std.ArrayList(u8), path: Pa |
| 97 | 98 | |
| 98 | 99 | const needed_alignment = @alignOf(Manifest.File) - (contents.items.len % @alignOf(Manifest.File)); |
| 99 | 100 | assert(needed_alignment >= 1); // Always need at least a null byte. |
| 100 | | try contents.appendNTimes(gpa, 0, needed_alignment); |
| 101 | // Extra +1 here is due to the requirement to keep at least 1 unused capacity in `Manifest.contents` at all |
| 102 | // times. |
| 103 | try contents.ensureUnusedCapacity(gpa, needed_alignment + 1); |
| 104 | contents.appendNTimesAssumeCapacity(0, needed_alignment); |
| 101 | 105 | |
| 102 | 106 | return @intCast(i); |
| 103 | 107 | } |
| ... | ... | @@ -107,7 +111,9 @@ fn resolveAppendPath(cache: *const Cache, contents: *std.ArrayList(u8), path: Pa |
| 107 | 111 | |
| 108 | 112 | const needed_alignment = @alignOf(Manifest.File) - (contents.items.len % @alignOf(Manifest.File)); |
| 109 | 113 | assert(needed_alignment >= 1); // Always need at least a null byte. |
| 110 | | try contents.appendNTimes(gpa, 0, needed_alignment); |
| 114 | // Extra +1 here is due to the requirement to keep at least 1 unused capacity in `Manifest.contents` at all times. |
| 115 | try contents.ensureUnusedCapacity(gpa, needed_alignment + 1); |
| 116 | contents.appendNTimesAssumeCapacity(0, needed_alignment); |
| 111 | 117 | |
| 112 | 118 | return 0; |
| 113 | 119 | } |
| ... | ... | @@ -127,10 +133,10 @@ pub const Hasher = crypto.auth.siphash.SipHash128(1, 3); |
| 127 | 133 | /// Refresh this with new random bytes when the manifest |
| 128 | 134 | /// format is modified in a non-backwards-compatible way. |
| 129 | 135 | pub const hasher_init: Hasher = Hasher.init(&.{ |
| 130 | | 0x33, 0x52, 0xa2, 0x84, |
| 131 | | 0xcf, 0x17, 0x56, 0x57, |
| 132 | | 0x01, 0xbb, 0xcd, 0xe4, |
| 133 | | 0x77, 0xd6, 0xf0, 0x60, |
| 136 | 0x02, 0xe9, 0xfa, 0xfe, |
| 137 | 0xe0, 0x95, 0x81, 0x55, |
| 138 | 0xf5, 0x5a, 0x15, 0xcb, |
| 139 | 0x4a, 0xf4, 0x00, 0x09, |
| 134 | 140 | }); |
| 135 | 141 | |
| 136 | 142 | pub const HashHelper = struct { |
| ... | ... | @@ -487,7 +493,8 @@ pub const Manifest = struct { |
| 487 | 493 | pub const Diagnostic = union(enum) { |
| 488 | 494 | none, |
| 489 | 495 | manifest_create: Io.File.OpenError, |
| 490 | | manifest_read: Io.File.Reader.Error, |
| 496 | manifest_stat: Io.File.StatError, |
| 497 | manifest_read: Io.File.ReadPositionalError, |
| 491 | 498 | manifest_lock: Io.File.LockError, |
| 492 | 499 | file_open: FileOp, |
| 493 | 500 | file_stat: FileOp, |
| ... | ... | @@ -644,7 +651,7 @@ pub const Manifest = struct { |
| 644 | 651 | return @fromBackingInt(@intCast(gop.index)); |
| 645 | 652 | } |
| 646 | 653 | |
| 647 | | pub fn addInputFileOptional(m: *Manifest, opt_path: ?Path, options: AddInputPathOptions) Allocator.Error!void { |
| 654 | pub fn addInputPathOptional(m: *Manifest, opt_path: ?Path, options: AddInputPathOptions) Allocator.Error!void { |
| 648 | 655 | m.hash.add(opt_path != null); |
| 649 | 656 | _ = try addInputPath(m, opt_path orelse return, options); |
| 650 | 657 | } |
| ... | ... | @@ -824,24 +831,43 @@ pub const Manifest = struct { |
| 824 | 831 | fn checkLocked(m: *Manifest) Check.Error!Check.Status { |
| 825 | 832 | const gpa = m.cache.gpa; |
| 826 | 833 | const io = m.cache.io; |
| 834 | const manifest_file = m.manifest_file.?; |
| 827 | 835 | |
| 828 | | var manifest_reader = m.manifest_file.?.reader(io, &.{}); // Reads positionally from zero. |
| 829 | | m.contents.clearRetainingCapacity(); |
| 830 | | manifest_reader.interface.appendRemainingUnlimited(gpa, &m.contents) catch |err| switch (err) { |
| 831 | | error.OutOfMemory => |e| return e, |
| 832 | | error.ReadFailed => { |
| 833 | | m.diagnostic = .{ .manifest_read = manifest_reader.err.? }; |
| 836 | const manifest_size = if (manifest_file.stat(io)) |stat| stat.size else |err| switch (err) { |
| 837 | error.Canceled => |e| return e, |
| 838 | else => |e| { |
| 839 | m.diagnostic = .{ .manifest_stat = e }; |
| 834 | 840 | return error.CacheCheckFailed; |
| 835 | 841 | }, |
| 836 | 842 | }; |
| 843 | |
| 844 | // We always want to compute the input file hash digests, even on a cache miss, because they will be |
| 845 | // used in the manifest digest. |
| 846 | |
| 847 | if (manifest_size == 0 or manifest_size < m.contents.items.len) { |
| 848 | // Don't clobber our input files by overwriting contents since it's a cache miss. |
| 849 | try m.contents.ensureUnusedCapacity(gpa, 1); |
| 850 | return .miss; |
| 851 | } |
| 852 | |
| 853 | // Already supposedly includes the extra null byte that we have to maintain. |
| 854 | try m.contents.resize(gpa, manifest_size); |
| 855 | |
| 856 | { |
| 857 | const n = manifest_file.readPositionalAll(io, m.contents.items, 0) catch |err| switch (err) { |
| 858 | error.Canceled => |e| return e, |
| 859 | else => |e| { |
| 860 | m.diagnostic = .{ .manifest_read = e }; |
| 861 | return error.CacheCheckFailed; |
| 862 | }, |
| 863 | }; |
| 864 | m.contents.shrinkRetainingCapacity(n); |
| 865 | } |
| 837 | 866 | const contents = m.contents.items; |
| 838 | 867 | |
| 839 | 868 | var off: usize = 0; |
| 840 | 869 | var c: Check = .{}; |
| 841 | 870 | |
| 842 | | // We always want to compute the input file hash digests, even on a cache miss, because they will be |
| 843 | | // used in the manifest digest. |
| 844 | | |
| 845 | 871 | // First the input files section, which must match our input files, otherwise it's invalid format. |
| 846 | 872 | for (m.input_paths.items, m.files.keys()[0..m.input_paths.items.len]) |*input_path, input_file_off| { |
| 847 | 873 | if (off + 1 >= contents.len) return error.InvalidFormat; |
| ... | ... | @@ -1466,7 +1492,7 @@ pub const Manifest = struct { |
| 1466 | 1492 | const prev_contents_len = other.contents.items.len; |
| 1467 | 1493 | try other.contents.appendSlice(gpa, copy_bytes); |
| 1468 | 1494 | |
| 1469 | | const gop = other.files.getOrPutAssumeCapacity(@fromBackingInt(prev_contents_len), .{ |
| 1495 | const gop = other.files.getOrPutAssumeCapacityContext(@fromBackingInt(prev_contents_len), .{ |
| 1470 | 1496 | .manifest = other, |
| 1471 | 1497 | }); |
| 1472 | 1498 | |
| ... | ... | @@ -1514,7 +1540,7 @@ pub const Manifest = struct { |
| 1514 | 1540 | var entries_list: std.ArrayList(u32) = .empty; |
| 1515 | 1541 | defer entries_list.deinit(gpa); |
| 1516 | 1542 | |
| 1517 | | while (true) { |
| 1543 | while (reader.state != .finished) { |
| 1518 | 1544 | const entries = entry_buffer[0..try reader.read(io, &entry_buffer)]; |
| 1519 | 1545 | for (try entries_list.addManyAsSlice(gpa, entries.len), entries) |*off, entry| { |
| 1520 | 1546 | off.* = @intCast(contents.items.len); |
| ... | ... | @@ -1528,12 +1554,15 @@ pub const Manifest = struct { |
| 1528 | 1554 | } |
| 1529 | 1555 | |
| 1530 | 1556 | const Sort = struct { |
| 1531 | | contents: []const u8, |
| 1557 | contents: [*:0]const u8, |
| 1532 | 1558 | pub fn lessThan(this: @This(), lhs: u32, rhs: u32) bool { |
| 1533 | | return mem.lessThanZ(u8, this.contents[lhs + 1 ..], this.contents[rhs + 1 ..]); // +1 for kind byte |
| 1559 | // This comparison includes the kind byte. |
| 1560 | return mem.lessThanZ(u8, this.contents + lhs, this.contents + rhs); |
| 1534 | 1561 | } |
| 1535 | 1562 | }; |
| 1536 | | mem.sortUnstable(u32, entries_list.items, @as(Sort, .{ .contents = contents.items }), Sort.lessThan); |
| 1563 | mem.sortUnstable(u32, entries_list.items, @as(Sort, .{ |
| 1564 | .contents = @ptrCast(contents.items.ptr), |
| 1565 | }), Sort.lessThan); |
| 1537 | 1566 | |
| 1538 | 1567 | // Duplicate the contents such that we may refer to it while creating a |
| 1539 | 1568 | // sorted copy in the original position (at contents_start). We will then |
| ... | ... | @@ -1546,7 +1575,7 @@ pub const Manifest = struct { |
| 1546 | 1575 | const offset = wrong_offset + contents_len; |
| 1547 | 1576 | // Includes the kind prefix which we also want to copy. |
| 1548 | 1577 | const entry: [*:0]const u8 = @ptrCast(contents.items[offset..]); |
| 1549 | | new_offset += mem.copySentinel(u8, 0, contents.items[new_offset..], entry); |
| 1578 | new_offset += mem.copySentinelInclusive(u8, 0, contents.items[new_offset..], entry); |
| 1550 | 1579 | } |
| 1551 | 1580 | assert(new_offset == contents_start + contents_len); |
| 1552 | 1581 | contents.shrinkRetainingCapacity(contents_start + contents_len); |