diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-12-08 15:23:18 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-12-23 22:15:08 -0800 |
| commit | 6f46570958af8ae27308eb4a9470e05f33aaa522 (patch) | |
| tree | 19aec2fa52364c78ffa9a9d8dc14d335f664fe06 /lib/std | |
| parent | 181ac08459f8d4001c504330ee66037135e56908 (diff) | |
| download | zig-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.zig | 66 | ||||
| -rw-r--r-- | lib/std/Build/WebServer.zig | 6 | ||||
| -rw-r--r-- | lib/std/Io.zig | 4 | ||||
| -rw-r--r-- | lib/std/Io/File.zig | 34 | ||||
| -rw-r--r-- | lib/std/fs/test.zig | 14 |
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)); } |
