| ... | @@ -67,7 +67,8 @@ fn appendPrefixedPath(cache: *const Cache, contents: *std.ArrayList(u8), prefixe | ... | @@ -67,7 +67,8 @@ fn appendPrefixedPath(cache: *const Cache, contents: *std.ArrayList(u8), prefixe |
| 67 | const end = contents.items.len + prefixed_path.sub_path.len; | 67 | const end = contents.items.len + prefixed_path.sub_path.len; |
| 68 | const needed_alignment = @alignOf(Manifest.File) - (end % @alignOf(Manifest.File)); | 68 | const needed_alignment = @alignOf(Manifest.File) - (end % @alignOf(Manifest.File)); |
| 69 | assert(needed_alignment >= 1); // Always need at least a null byte. | 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 | contents.appendSliceAssumeCapacity(prefixed_path.sub_path); | 72 | contents.appendSliceAssumeCapacity(prefixed_path.sub_path); |
| 72 | contents.appendNTimesAssumeCapacity(0, needed_alignment); | 73 | contents.appendNTimesAssumeCapacity(0, needed_alignment); |
| 73 | return prefixed_path.prefix; | 74 | return prefixed_path.prefix; |
| ... | @@ -97,7 +98,10 @@ fn resolveAppendPath(cache: *const Cache, contents: *std.ArrayList(u8), path: Pa | ... | @@ -97,7 +98,10 @@ fn resolveAppendPath(cache: *const Cache, contents: *std.ArrayList(u8), path: Pa |
| 97 | | 98 | |
| 98 | const needed_alignment = @alignOf(Manifest.File) - (contents.items.len % @alignOf(Manifest.File)); | 99 | const needed_alignment = @alignOf(Manifest.File) - (contents.items.len % @alignOf(Manifest.File)); |
| 99 | assert(needed_alignment >= 1); // Always need at least a null byte. | 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 | return @intCast(i); | 106 | return @intCast(i); |
| 103 | } | 107 | } |
| ... | @@ -107,7 +111,9 @@ fn resolveAppendPath(cache: *const Cache, contents: *std.ArrayList(u8), path: Pa | ... | @@ -107,7 +111,9 @@ fn resolveAppendPath(cache: *const Cache, contents: *std.ArrayList(u8), path: Pa |
| 107 | | 111 | |
| 108 | const needed_alignment = @alignOf(Manifest.File) - (contents.items.len % @alignOf(Manifest.File)); | 112 | const needed_alignment = @alignOf(Manifest.File) - (contents.items.len % @alignOf(Manifest.File)); |
| 109 | assert(needed_alignment >= 1); // Always need at least a null byte. | 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 | return 0; | 118 | return 0; |
| 113 | } | 119 | } |
| ... | @@ -127,10 +133,10 @@ pub const Hasher = crypto.auth.siphash.SipHash128(1, 3); | ... | @@ -127,10 +133,10 @@ pub const Hasher = crypto.auth.siphash.SipHash128(1, 3); |
| 127 | /// Refresh this with new random bytes when the manifest | 133 | /// Refresh this with new random bytes when the manifest |
| 128 | /// format is modified in a non-backwards-compatible way. | 134 | /// format is modified in a non-backwards-compatible way. |
| 129 | pub const hasher_init: Hasher = Hasher.init(&.{ | 135 | pub const hasher_init: Hasher = Hasher.init(&.{ |
| 130 | 0x33, 0x52, 0xa2, 0x84, | 136 | 0x02, 0xe9, 0xfa, 0xfe, |
| 131 | 0xcf, 0x17, 0x56, 0x57, | 137 | 0xe0, 0x95, 0x81, 0x55, |
| 132 | 0x01, 0xbb, 0xcd, 0xe4, | 138 | 0xf5, 0x5a, 0x15, 0xcb, |
| 133 | 0x77, 0xd6, 0xf0, 0x60, | 139 | 0x4a, 0xf4, 0x00, 0x09, |
| 134 | }); | 140 | }); |
| 135 | | 141 | |
| 136 | pub const HashHelper = struct { | 142 | pub const HashHelper = struct { |
| ... | @@ -487,7 +493,8 @@ pub const Manifest = struct { | ... | @@ -487,7 +493,8 @@ pub const Manifest = struct { |
| 487 | pub const Diagnostic = union(enum) { | 493 | pub const Diagnostic = union(enum) { |
| 488 | none, | 494 | none, |
| 489 | manifest_create: Io.File.OpenError, | 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 | manifest_lock: Io.File.LockError, | 498 | manifest_lock: Io.File.LockError, |
| 492 | file_open: FileOp, | 499 | file_open: FileOp, |
| 493 | file_stat: FileOp, | 500 | file_stat: FileOp, |
| ... | @@ -644,7 +651,7 @@ pub const Manifest = struct { | ... | @@ -644,7 +651,7 @@ pub const Manifest = struct { |
| 644 | return @fromBackingInt(@intCast(gop.index)); | 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 | m.hash.add(opt_path != null); | 655 | m.hash.add(opt_path != null); |
| 649 | _ = try addInputPath(m, opt_path orelse return, options); | 656 | _ = try addInputPath(m, opt_path orelse return, options); |
| 650 | } | 657 | } |
| ... | @@ -824,24 +831,43 @@ pub const Manifest = struct { | ... | @@ -824,24 +831,43 @@ pub const Manifest = struct { |
| 824 | fn checkLocked(m: *Manifest) Check.Error!Check.Status { | 831 | fn checkLocked(m: *Manifest) Check.Error!Check.Status { |
| 825 | const gpa = m.cache.gpa; | 832 | const gpa = m.cache.gpa; |
| 826 | const io = m.cache.io; | 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. | 836 | const manifest_size = if (manifest_file.stat(io)) |stat| stat.size else |err| switch (err) { |
| 829 | m.contents.clearRetainingCapacity(); | 837 | error.Canceled => |e| return e, |
| 830 | manifest_reader.interface.appendRemainingUnlimited(gpa, &m.contents) catch |err| switch (err) { | 838 | else => |e| { |
| 831 | error.OutOfMemory => |e| return e, | 839 | m.diagnostic = .{ .manifest_stat = e }; |
| 832 | error.ReadFailed => { | | |
| 833 | m.diagnostic = .{ .manifest_read = manifest_reader.err.? }; | | |
| 834 | return error.CacheCheckFailed; | 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 | const contents = m.contents.items; | 866 | const contents = m.contents.items; |
| 838 | | 867 | |
| 839 | var off: usize = 0; | 868 | var off: usize = 0; |
| 840 | var c: Check = .{}; | 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 | // First the input files section, which must match our input files, otherwise it's invalid format. | 871 | // First the input files section, which must match our input files, otherwise it's invalid format. |
| 846 | for (m.input_paths.items, m.files.keys()[0..m.input_paths.items.len]) |*input_path, input_file_off| { | 872 | for (m.input_paths.items, m.files.keys()[0..m.input_paths.items.len]) |*input_path, input_file_off| { |
| 847 | if (off + 1 >= contents.len) return error.InvalidFormat; | 873 | if (off + 1 >= contents.len) return error.InvalidFormat; |
| ... | @@ -1466,7 +1492,7 @@ pub const Manifest = struct { | ... | @@ -1466,7 +1492,7 @@ pub const Manifest = struct { |
| 1466 | const prev_contents_len = other.contents.items.len; | 1492 | const prev_contents_len = other.contents.items.len; |
| 1467 | try other.contents.appendSlice(gpa, copy_bytes); | 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 | .manifest = other, | 1496 | .manifest = other, |
| 1471 | }); | 1497 | }); |
| 1472 | | 1498 | |
| ... | @@ -1514,7 +1540,7 @@ pub const Manifest = struct { | ... | @@ -1514,7 +1540,7 @@ pub const Manifest = struct { |
| 1514 | var entries_list: std.ArrayList(u32) = .empty; | 1540 | var entries_list: std.ArrayList(u32) = .empty; |
| 1515 | defer entries_list.deinit(gpa); | 1541 | defer entries_list.deinit(gpa); |
| 1516 | | 1542 | |
| 1517 | while (true) { | 1543 | while (reader.state != .finished) { |
| 1518 | const entries = entry_buffer[0..try reader.read(io, &entry_buffer)]; | 1544 | const entries = entry_buffer[0..try reader.read(io, &entry_buffer)]; |
| 1519 | for (try entries_list.addManyAsSlice(gpa, entries.len), entries) |*off, entry| { | 1545 | for (try entries_list.addManyAsSlice(gpa, entries.len), entries) |*off, entry| { |
| 1520 | off.* = @intCast(contents.items.len); | 1546 | off.* = @intCast(contents.items.len); |
| ... | @@ -1528,12 +1554,15 @@ pub const Manifest = struct { | ... | @@ -1528,12 +1554,15 @@ pub const Manifest = struct { |
| 1528 | } | 1554 | } |
| 1529 | | 1555 | |
| 1530 | const Sort = struct { | 1556 | const Sort = struct { |
| 1531 | contents: []const u8, | 1557 | contents: [*:0]const u8, |
| 1532 | pub fn lessThan(this: @This(), lhs: u32, rhs: u32) bool { | 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 | // Duplicate the contents such that we may refer to it while creating a | 1567 | // Duplicate the contents such that we may refer to it while creating a |
| 1539 | // sorted copy in the original position (at contents_start). We will then | 1568 | // sorted copy in the original position (at contents_start). We will then |
| ... | @@ -1546,7 +1575,7 @@ pub const Manifest = struct { | ... | @@ -1546,7 +1575,7 @@ pub const Manifest = struct { |
| 1546 | const offset = wrong_offset + contents_len; | 1575 | const offset = wrong_offset + contents_len; |
| 1547 | // Includes the kind prefix which we also want to copy. | 1576 | // Includes the kind prefix which we also want to copy. |
| 1548 | const entry: [*:0]const u8 = @ptrCast(contents.items[offset..]); | 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 | assert(new_offset == contents_start + contents_len); | 1580 | assert(new_offset == contents_start + contents_len); |
| 1552 | contents.shrinkRetainingCapacity(contents_start + contents_len); | 1581 | contents.shrinkRetainingCapacity(contents_start + contents_len); |