aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-12-08 15:23:18 -0800
committerAndrew Kelley <andrew@ziglang.org>2025-12-23 22:15:08 -0800
commit6f46570958af8ae27308eb4a9470e05f33aaa522 (patch)
tree19aec2fa52364c78ffa9a9d8dc14d335f664fe06 /lib/std
parent181ac08459f8d4001c504330ee66037135e56908 (diff)
downloadzig-6f46570958af8ae27308eb4a9470e05f33aaa522.tar.gz
zig-6f46570958af8ae27308eb4a9470e05f33aaa522.zip
link.MachO: update parallel hasher to std.Io
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/Build/Cache.zig66
-rw-r--r--lib/std/Build/WebServer.zig6
-rw-r--r--lib/std/Io.zig4
-rw-r--r--lib/std/Io/File.zig34
-rw-r--r--lib/std/fs/test.zig14
5 files changed, 73 insertions, 51 deletions
diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig
index 0176d71a1c..dab1926f53 100644
--- a/lib/std/Build/Cache.zig
+++ b/lib/std/Build/Cache.zig
@@ -800,7 +800,7 @@ pub const Manifest = struct {
}
var actual_digest: BinDigest = undefined;
- hashFile(this_file, &actual_digest) catch |err| {
+ hashFile(io, this_file, &actual_digest) catch |err| {
self.diagnostic = .{ .file_read = .{
.file_index = idx,
.err = err,
@@ -908,9 +908,11 @@ pub const Manifest = struct {
}
}
- fn populateFileHashHandle(self: *Manifest, ch_file: *File, handle: Io.File) !void {
+ fn populateFileHashHandle(self: *Manifest, ch_file: *File, io_file: Io.File) !void {
const io = self.cache.io;
- const actual_stat = try handle.stat(io);
+ const gpa = self.cache.gpa;
+
+ const actual_stat = try io_file.stat(io);
ch_file.stat = .{
.size = actual_stat.size,
.mtime = actual_stat.mtime,
@@ -924,19 +926,17 @@ pub const Manifest = struct {
}
if (ch_file.max_file_size) |max_file_size| {
- if (ch_file.stat.size > max_file_size) {
- return error.FileTooBig;
- }
+ if (ch_file.stat.size > max_file_size) return error.FileTooBig;
- const contents = try self.cache.gpa.alloc(u8, @as(usize, @intCast(ch_file.stat.size)));
- errdefer self.cache.gpa.free(contents);
+ // Hash while reading from disk, to keep the contents in the cpu
+ // cache while doing hashing.
+ const contents = try gpa.alloc(u8, @intCast(ch_file.stat.size));
+ errdefer gpa.free(contents);
- // Hash while reading from disk, to keep the contents in the cpu cache while
- // doing hashing.
var hasher = hasher_init;
var off: usize = 0;
while (true) {
- const bytes_read = try handle.pread(contents[off..], off);
+ const bytes_read = try io_file.readPositional(io, &.{contents[off..]}, off);
if (bytes_read == 0) break;
hasher.update(contents[off..][0..bytes_read]);
off += bytes_read;
@@ -945,7 +945,7 @@ pub const Manifest = struct {
ch_file.contents = contents;
} else {
- try hashFile(handle, &ch_file.bin_digest);
+ try hashFile(io, io_file, &ch_file.bin_digest);
}
self.hash.hasher.update(&ch_file.bin_digest);
@@ -1169,13 +1169,11 @@ pub const Manifest = struct {
fn downgradeToSharedLock(self: *Manifest) !void {
if (!self.have_exclusive_lock) return;
+ const io = self.cache.io;
- // WASI does not currently support flock, so we bypass it here.
- // TODO: If/when flock is supported on WASI, this check should be removed.
- // See https://github.com/WebAssembly/wasi-filesystem/issues/2
- if (builtin.os.tag != .wasi or std.process.can_spawn or !builtin.single_threaded) {
+ if (std.process.can_spawn or !builtin.single_threaded) {
const manifest_file = self.manifest_file.?;
- try manifest_file.downgradeLock();
+ try manifest_file.downgradeLock(io);
}
self.have_exclusive_lock = false;
@@ -1184,16 +1182,14 @@ pub const Manifest = struct {
fn upgradeToExclusiveLock(self: *Manifest) error{CacheCheckFailed}!bool {
if (self.have_exclusive_lock) return false;
assert(self.manifest_file != null);
+ const io = self.cache.io;
- // WASI does not currently support flock, so we bypass it here.
- // TODO: If/when flock is supported on WASI, this check should be removed.
- // See https://github.com/WebAssembly/wasi-filesystem/issues/2
- if (builtin.os.tag != .wasi or std.process.can_spawn or !builtin.single_threaded) {
+ if (std.process.can_spawn or !builtin.single_threaded) {
const manifest_file = self.manifest_file.?;
// Here we intentionally have a period where the lock is released, in case there are
// other processes holding a shared lock.
- manifest_file.unlock();
- manifest_file.lock(.exclusive) catch |err| {
+ manifest_file.unlock(io);
+ manifest_file.lock(io, .exclusive) catch |err| {
self.diagnostic = .{ .manifest_lock = err };
return error.CacheCheckFailed;
};
@@ -1206,12 +1202,8 @@ pub const Manifest = struct {
/// The `Manifest` remains safe to deinit.
/// Don't forget to call `writeManifest` before this!
pub fn toOwnedLock(self: *Manifest) Lock {
- const lock: Lock = .{
- .manifest_file = self.manifest_file.?,
- };
-
- self.manifest_file = null;
- return lock;
+ defer self.manifest_file = null;
+ return .{ .manifest_file = self.manifest_file.? };
}
/// Releases the manifest file and frees any memory the Manifest was using.
@@ -1223,7 +1215,7 @@ pub const Manifest = struct {
if (self.manifest_file) |file| {
if (builtin.os.tag == .windows) {
// See Lock.release for why this is required on Windows
- file.unlock();
+ file.unlock(io);
}
file.close(io);
@@ -1308,15 +1300,15 @@ pub fn writeSmallFile(dir: Io.Dir, sub_path: []const u8, data: []const u8) !void
}
}
-fn hashFile(file: Io.File, bin_digest: *[Hasher.mac_length]u8) Io.File.PReadError!void {
- var buf: [1024]u8 = undefined;
+fn hashFile(io: Io, file: Io.File, bin_digest: *[Hasher.mac_length]u8) Io.File.ReadPositionalError!void {
+ var buffer: [2048]u8 = undefined;
var hasher = hasher_init;
- var off: u64 = 0;
+ var offset: u64 = 0;
while (true) {
- const bytes_read = try file.pread(&buf, off);
- if (bytes_read == 0) break;
- hasher.update(buf[0..bytes_read]);
- off += bytes_read;
+ const n = try file.readPositional(io, &.{&buffer}, offset);
+ if (n == 0) break;
+ hasher.update(buffer[0..n]);
+ offset += n;
}
hasher.final(bin_digest);
}
diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig
index 472e87b05a..162d17f070 100644
--- a/lib/std/Build/WebServer.zig
+++ b/lib/std/Build/WebServer.zig
@@ -218,9 +218,9 @@ pub fn finishBuild(ws: *WebServer, opts: struct {
else => {},
}
if (@bitSizeOf(usize) != 64) {
- // Current implementation depends on posix.mmap()'s second parameter, `length: usize`,
- // being compatible with `std.fs.getEndPos() u64`'s return value. This is not the case
- // on 32-bit platforms.
+ // Current implementation depends on posix.mmap()'s second
+ // parameter, `length: usize`, being compatible with file system's
+ // u64 return value. This is not the case on 32-bit platforms.
// Affects or affected by issues #5185, #22523, and #22464.
std.process.fatal("--fuzz not yet implemented on {d}-bit platforms", .{@bitSizeOf(usize)});
}
diff --git a/lib/std/Io.zig b/lib/std/Io.zig
index 17fb75fe54..9d6dcef615 100644
--- a/lib/std/Io.zig
+++ b/lib/std/Io.zig
@@ -692,9 +692,9 @@ pub const VTable = struct {
fileWriteFileStreaming: *const fn (?*anyopaque, File, header: []const u8, *Io.File.Reader, Io.Limit) File.Writer.WriteFileError!usize,
fileWriteFilePositional: *const fn (?*anyopaque, File, header: []const u8, *Io.File.Reader, Io.Limit, offset: u64) File.WriteFilePositionalError!usize,
/// Returns 0 on end of stream.
- fileReadStreaming: *const fn (?*anyopaque, File, data: [][]u8) File.Reader.Error!usize,
+ fileReadStreaming: *const fn (?*anyopaque, File, data: []const []u8) File.Reader.Error!usize,
/// Returns 0 on end of stream.
- fileReadPositional: *const fn (?*anyopaque, File, data: [][]u8, offset: u64) File.ReadPositionalError!usize,
+ fileReadPositional: *const fn (?*anyopaque, File, data: []const []u8, offset: u64) File.ReadPositionalError!usize,
fileSeekBy: *const fn (?*anyopaque, File, relative_offset: i64) File.SeekError!void,
fileSeekTo: *const fn (?*anyopaque, File, absolute_offset: u64) File.SeekError!void,
fileSync: *const fn (?*anyopaque, File) File.SyncError!void,
diff --git a/lib/std/Io/File.zig b/lib/std/Io/File.zig
index a9b4775772..5e89025478 100644
--- a/lib/std/Io/File.zig
+++ b/lib/std/Io/File.zig
@@ -466,13 +466,21 @@ pub fn setTimestampsNow(file: File, io: Io) SetTimestampsError!void {
pub const ReadPositionalError = Reader.Error || error{Unseekable};
-pub fn readPositional(file: File, io: Io, buffer: [][]u8, offset: u64) ReadPositionalError!usize {
+/// Returns 0 on end of stream.
+///
+/// See also:
+/// * `reader`
+pub fn readPositional(file: File, io: Io, buffer: []const []u8, offset: u64) ReadPositionalError!usize {
return io.vtable.fileReadPositional(io.userdata, file, buffer, offset);
}
pub const WritePositionalError = Writer.Error || error{Unseekable};
-pub fn writePositional(file: File, io: Io, buffer: [][]const u8, offset: u64) WritePositionalError!usize {
+/// Returns 0 on end of stream.
+///
+/// See also:
+/// * `writer`
+pub fn writePositional(file: File, io: Io, buffer: []const []const u8, offset: u64) WritePositionalError!usize {
return io.vtable.fileWritePositional(io.userdata, file, buffer, offset);
}
@@ -501,13 +509,35 @@ pub const WriteFilePositionalError = Writer.WriteFileError || error{Unseekable};
///
/// Positional is more threadsafe, since the global seek position is not
/// affected.
+///
+/// See also:
+/// * `readerStreaming`
pub fn reader(file: File, io: Io, buffer: []u8) Reader {
return .init(file, io, buffer);
}
+/// Equivalent to creating a positional reader and reading multiple times to fill `buffer`.
+///
+/// Returns number of bytes read into `buffer`. If less than `buffer.len`, end of file occurred.
+///
+/// See also:
+/// * `reader`
+pub fn readPositionalAll(file: File, io: Io, buffer: []u8, offset: u64) ReadPositionalError!usize {
+ var index: usize = 0;
+ while (index != buffer.len) {
+ const amt = try file.readPositional(io, &.{buffer[index..]}, offset + index);
+ if (amt == 0) break;
+ index += amt;
+ }
+ return index;
+}
+
/// Positional is more threadsafe, since the global seek position is not
/// affected, but when such syscalls are not available, preemptively
/// initializing in streaming mode skips a failed syscall.
+///
+/// See also:
+/// * `reader`
pub fn readerStreaming(file: File, io: Io, buffer: []u8) Reader {
return .initStreaming(file, io, buffer);
}
diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig
index 7b12ba4271..e044a97620 100644
--- a/lib/std/fs/test.zig
+++ b/lib/std/fs/test.zig
@@ -1455,7 +1455,7 @@ test "writev, readv" {
try writer.interface.writeVecAll(&write_vecs);
try writer.interface.flush();
- try testing.expectEqual(@as(u64, line1.len + line2.len), try src_file.getEndPos());
+ try testing.expectEqual(@as(u64, line1.len + line2.len), try src_file.length(io));
var reader = writer.moveToReader(io);
try reader.seekTo(0);
@@ -1486,7 +1486,7 @@ test "pwritev, preadv" {
try writer.seekTo(16);
try writer.interface.writeVecAll(&lines);
try writer.interface.flush();
- try testing.expectEqual(@as(u64, 16 + line1.len + line2.len), try src_file.getEndPos());
+ try testing.expectEqual(@as(u64, 16 + line1.len + line2.len), try src_file.length(io));
var reader = writer.moveToReader(io);
try reader.seekTo(16);
@@ -1511,13 +1511,13 @@ test "setEndPos" {
const f = try tmp.dir.openFile(io, file_name, .{ .mode = .read_write });
defer f.close(io);
- const initial_size = try f.getEndPos();
+ const initial_size = try f.length(io);
var buffer: [32]u8 = undefined;
var reader = f.reader(io, &.{});
{
try f.setEndPos(initial_size);
- try testing.expectEqual(initial_size, try f.getEndPos());
+ try testing.expectEqual(initial_size, try f.length(io));
try reader.seekTo(0);
try testing.expectEqual(initial_size, try reader.interface.readSliceShort(&buffer));
try testing.expectEqualStrings("ninebytes", buffer[0..@intCast(initial_size)]);
@@ -1526,7 +1526,7 @@ test "setEndPos" {
{
const larger = initial_size + 4;
try f.setEndPos(larger);
- try testing.expectEqual(larger, try f.getEndPos());
+ try testing.expectEqual(larger, try f.length(io));
try reader.seekTo(0);
try testing.expectEqual(larger, try reader.interface.readSliceShort(&buffer));
try testing.expectEqualStrings("ninebytes\x00\x00\x00\x00", buffer[0..@intCast(larger)]);
@@ -1535,14 +1535,14 @@ test "setEndPos" {
{
const smaller = initial_size - 5;
try f.setEndPos(smaller);
- try testing.expectEqual(smaller, try f.getEndPos());
+ try testing.expectEqual(smaller, try f.length(io));
try reader.seekTo(0);
try testing.expectEqual(smaller, try reader.interface.readSliceShort(&buffer));
try testing.expectEqualStrings("nine", buffer[0..@intCast(smaller)]);
}
try f.setEndPos(0);
- try testing.expectEqual(0, try f.getEndPos());
+ try testing.expectEqual(0, try f.length(io));
try reader.seekTo(0);
try testing.expectEqual(0, try reader.interface.readSliceShort(&buffer));
}