Commit 4bca5faca6
lib/std/Build/Cache.zig
@@ -2,6 +2,18 @@
//! This is not a general-purpose cache. It is designed to be fast and simple,
//! not to withstand attacks using specially-crafted input.
+const Cache = @This();
+const std = @import("std");
+const builtin = @import("builtin");
+const crypto = std.crypto;
+const fs = std.fs;
+const assert = std.debug.assert;
+const testing = std.testing;
+const mem = std.mem;
+const fmt = std.fmt;
+const Allocator = std.mem.Allocator;
+const log = std.log.scoped(.cache);
+
gpa: Allocator,
manifest_dir: fs.Dir,
hash: HashHelper = .{},
@@ -21,18 +33,6 @@ pub const Path = @import("Cache/Path.zig");
pub const Directory = @import("Cache/Directory.zig");
pub const DepTokenizer = @import("Cache/DepTokenizer.zig");
-const Cache = @This();
-const std = @import("std");
-const builtin = @import("builtin");
-const crypto = std.crypto;
-const fs = std.fs;
-const assert = std.debug.assert;
-const testing = std.testing;
-const mem = std.mem;
-const fmt = std.fmt;
-const Allocator = std.mem.Allocator;
-const log = std.log.scoped(.cache);
-
pub fn addPrefix(cache: *Cache, directory: Directory) void {
cache.prefixes_buffer[cache.prefixes_len] = directory;
cache.prefixes_len += 1;
@@ -1118,25 +1118,12 @@ pub const Manifest = struct {
if (self.manifest_dirty) {
self.manifest_dirty = false;
- const gpa = self.cache.gpa;
- var contents: std.ArrayListUnmanaged(u8) = .empty;
- defer contents.deinit(gpa);
-
- try contents.appendSlice(gpa, manifest_header ++ "\n");
- for (self.files.keys()) |file| {
- try contents.print(gpa, "{d} {d} {d} {x} {d} {s}\n", .{
- file.stat.size,
- file.stat.inode,
- file.stat.mtime,
- &file.bin_digest,
- file.prefixed_path.prefix,
- file.prefixed_path.sub_path,
- });
- }
-
- try manifest_file.setEndPos(contents.items.len);
- var pos: usize = 0;
- while (pos < contents.items.len) pos += try manifest_file.pwrite(contents.items[pos..], pos);
+ var buffer: [4000]u8 = undefined;
+ var fw = manifest_file.writer(&buffer);
+ writeDirtyManifestToStream(self, &fw) catch |err| switch (err) {
+ error.WriteFailed => return fw.err.?,
+ else => |e| return e,
+ };
}
if (self.want_shared_lock) {
@@ -1144,6 +1131,21 @@ pub const Manifest = struct {
}
}
+ fn writeDirtyManifestToStream(self: *Manifest, fw: *fs.File.Writer) !void {
+ try fw.interface.writeAll(manifest_header ++ "\n");
+ for (self.files.keys()) |file| {
+ try fw.interface.print("{d} {d} {d} {x} {d} {s}\n", .{
+ file.stat.size,
+ file.stat.inode,
+ file.stat.mtime,
+ &file.bin_digest,
+ file.prefixed_path.prefix,
+ file.prefixed_path.sub_path,
+ });
+ }
+ try fw.end();
+ }
+
fn downgradeToSharedLock(self: *Manifest) !void {
if (!self.have_exclusive_lock) return;
lib/std/fs/File.zig
@@ -1894,6 +1894,20 @@ pub const Writer = struct {
},
}
}
+
+ pub const EndError = SetEndPosError || std.io.Writer.Error;
+
+ /// Flushes any buffered data and sets the end position of the file.
+ ///
+ /// If not overwriting existing contents, then calling `interface.flush`
+ /// directly is sufficient.
+ ///
+ /// Flush failure is handled by setting `err` so that it can be handled
+ /// along with other write failures.
+ pub fn end(w: *Writer) EndError!void {
+ try w.interface.flush();
+ return w.file.setEndPos(w.pos);
+ }
};
/// Defaults to positional reading; falls back to streaming.