authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 09:06:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-07 22:43:52-07:00
log4bca5faca60df971712e538758315a0e0afe89a0
treefe0b8bac0ec070c00b46706edc84ee4c7b997e37
parentd6ac04c47811f162ca662f43d3c93a5dc0dcce86

std.Build.Cache: write manifest without heap allocating

Now that the buffered writing interface is not generic.

2 files changed, 47 insertions(+), 31 deletions(-)

lib/std/Build/Cache.zig+33-31
......@@ -2,6 +2,18 @@
22//! This is not a general-purpose cache. It is designed to be fast and simple,
33//! not to withstand attacks using specially-crafted input.
44
5const Cache = @This();
6const std = @import("std");
7const builtin = @import("builtin");
8const crypto = std.crypto;
9const fs = std.fs;
10const assert = std.debug.assert;
11const testing = std.testing;
12const mem = std.mem;
13const fmt = std.fmt;
14const Allocator = std.mem.Allocator;
15const log = std.log.scoped(.cache);
16
517gpa: Allocator,
618manifest_dir: fs.Dir,
719hash: HashHelper = .{},
......@@ -21,18 +33,6 @@ pub const Path = @import("Cache/Path.zig");
2133pub const Directory = @import("Cache/Directory.zig");
2234pub const DepTokenizer = @import("Cache/DepTokenizer.zig");
2335
24const Cache = @This();
25const std = @import("std");
26const builtin = @import("builtin");
27const crypto = std.crypto;
28const fs = std.fs;
29const assert = std.debug.assert;
30const testing = std.testing;
31const mem = std.mem;
32const fmt = std.fmt;
33const Allocator = std.mem.Allocator;
34const log = std.log.scoped(.cache);
35
3636pub fn addPrefix(cache: *Cache, directory: Directory) void {
3737 cache.prefixes_buffer[cache.prefixes_len] = directory;
3838 cache.prefixes_len += 1;
......@@ -1118,25 +1118,12 @@ pub const Manifest = struct {
11181118 if (self.manifest_dirty) {
11191119 self.manifest_dirty = false;
11201120
1121 const gpa = self.cache.gpa;
1122 var contents: std.ArrayListUnmanaged(u8) = .empty;
1123 defer contents.deinit(gpa);
1124
1125 try contents.appendSlice(gpa, manifest_header ++ "\n");
1126 for (self.files.keys()) |file| {
1127 try contents.print(gpa, "{d} {d} {d} {x} {d} {s}\n", .{
1128 file.stat.size,
1129 file.stat.inode,
1130 file.stat.mtime,
1131 &file.bin_digest,
1132 file.prefixed_path.prefix,
1133 file.prefixed_path.sub_path,
1134 });
1135 }
1136
1137 try manifest_file.setEndPos(contents.items.len);
1138 var pos: usize = 0;
1139 while (pos < contents.items.len) pos += try manifest_file.pwrite(contents.items[pos..], pos);
1121 var buffer: [4000]u8 = undefined;
1122 var fw = manifest_file.writer(&buffer);
1123 writeDirtyManifestToStream(self, &fw) catch |err| switch (err) {
1124 error.WriteFailed => return fw.err.?,
1125 else => |e| return e,
1126 };
11401127 }
11411128
11421129 if (self.want_shared_lock) {
......@@ -1144,6 +1131,21 @@ pub const Manifest = struct {
11441131 }
11451132 }
11461133
1134 fn writeDirtyManifestToStream(self: *Manifest, fw: *fs.File.Writer) !void {
1135 try fw.interface.writeAll(manifest_header ++ "\n");
1136 for (self.files.keys()) |file| {
1137 try fw.interface.print("{d} {d} {d} {x} {d} {s}\n", .{
1138 file.stat.size,
1139 file.stat.inode,
1140 file.stat.mtime,
1141 &file.bin_digest,
1142 file.prefixed_path.prefix,
1143 file.prefixed_path.sub_path,
1144 });
1145 }
1146 try fw.end();
1147 }
1148
11471149 fn downgradeToSharedLock(self: *Manifest) !void {
11481150 if (!self.have_exclusive_lock) return;
11491151
lib/std/fs/File.zig+14
......@@ -1894,6 +1894,20 @@ pub const Writer = struct {
18941894 },
18951895 }
18961896 }
1897
1898 pub const EndError = SetEndPosError || std.io.Writer.Error;
1899
1900 /// Flushes any buffered data and sets the end position of the file.
1901 ///
1902 /// If not overwriting existing contents, then calling `interface.flush`
1903 /// directly is sufficient.
1904 ///
1905 /// Flush failure is handled by setting `err` so that it can be handled
1906 /// along with other write failures.
1907 pub fn end(w: *Writer) EndError!void {
1908 try w.interface.flush();
1909 return w.file.setEndPos(w.pos);
1910 }
18971911};
18981912
18991913/// Defaults to positional reading; falls back to streaming.