diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-08-29 20:19:23 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-08-30 00:48:50 -0700 |
| commit | 9a0970a12bdcae105b6f3f65c0a72d95a209bd35 (patch) | |
| tree | ab0de8f2447b5e52c2bb5c92a976a52fb4b613ee /lib/std/fs | |
| parent | 79f267f6b9e7f80a6fed3b1019f9de942841c3be (diff) | |
| download | zig-9a0970a12bdcae105b6f3f65c0a72d95a209bd35.tar.gz zig-9a0970a12bdcae105b6f3f65c0a72d95a209bd35.zip | |
rework std.Io.Writer.Allocating to support runtime-known alignment
Also, breaking API changes to:
* std.fs.Dir.readFileAlloc
* std.fs.Dir.readFileAllocOptions
Diffstat (limited to 'lib/std/fs')
| -rw-r--r-- | lib/std/fs/Dir.zig | 78 | ||||
| -rw-r--r-- | lib/std/fs/test.zig | 60 |
2 files changed, 83 insertions, 55 deletions
diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index 768efdeff5..d097573122 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -1977,41 +1977,59 @@ pub fn readFile(self: Dir, file_path: []const u8, buffer: []u8) ![]u8 { return buffer[0..end_index]; } -/// On success, caller owns returned buffer. -/// If the file is larger than `max_bytes`, returns `error.FileTooBig`. -/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). -/// On WASI, `file_path` should be encoded as valid UTF-8. -/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding. -pub fn readFileAlloc(self: Dir, allocator: mem.Allocator, file_path: []const u8, max_bytes: usize) ![]u8 { - return self.readFileAllocOptions(allocator, file_path, max_bytes, null, .of(u8), null); +pub const ReadFileAllocError = File.OpenError || File.ReadError || Allocator.Error || error{ + /// File size reached or exceeded the provided limit. + StreamTooLong, +}; + +/// Reads all the bytes from the named file. On success, caller owns returned +/// buffer. +/// +/// If the file size is already known, a better alternative is to initialize a +/// `File.Reader`. +/// +/// If the file size cannot be obtained, an error is returned. If +/// this is a realistic possibility, a better alternative is to initialize a +/// `File.Reader` which handles this seamlessly. +pub fn readFileAlloc( + dir: Dir, + /// On Windows, should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). + /// On WASI, should be encoded as valid UTF-8. + /// On other platforms, an opaque sequence of bytes with no particular encoding. + sub_path: []const u8, + /// Used to allocate the result. + gpa: Allocator, + /// If reached or exceeded, `error.StreamTooLong` is returned instead. + limit: std.Io.Limit, +) ReadFileAllocError![]u8 { + return readFileAllocOptions(dir, sub_path, gpa, limit, .of(u8), null); } -/// On success, caller owns returned buffer. -/// If the file is larger than `max_bytes`, returns `error.FileTooBig`. -/// If `size_hint` is specified the initial buffer size is calculated using -/// that value, otherwise the effective file size is used instead. -/// Allows specifying alignment and a sentinel value. -/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). -/// On WASI, `file_path` should be encoded as valid UTF-8. -/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding. +/// Reads all the bytes from the named file. On success, caller owns returned +/// buffer. +/// +/// If the file size is already known, a better alternative is to initialize a +/// `File.Reader`. pub fn readFileAllocOptions( - self: Dir, - allocator: mem.Allocator, - file_path: []const u8, - max_bytes: usize, - size_hint: ?usize, + dir: Dir, + /// On Windows, should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). + /// On WASI, should be encoded as valid UTF-8. + /// On other platforms, an opaque sequence of bytes with no particular encoding. + sub_path: []const u8, + /// Used to allocate the result. + gpa: Allocator, + /// If reached or exceeded, `error.StreamTooLong` is returned instead. + limit: std.Io.Limit, comptime alignment: std.mem.Alignment, - comptime optional_sentinel: ?u8, -) !(if (optional_sentinel) |s| [:s]align(alignment.toByteUnits()) u8 else []align(alignment.toByteUnits()) u8) { - var file = try self.openFile(file_path, .{}); + comptime sentinel: ?u8, +) ReadFileAllocError!(if (sentinel) |s| [:s]align(alignment.toByteUnits()) u8 else []align(alignment.toByteUnits()) u8) { + var file = try dir.openFile(sub_path, .{}); defer file.close(); - - // If the file size doesn't fit a usize it'll be certainly greater than - // `max_bytes` - const stat_size = size_hint orelse std.math.cast(usize, try file.getEndPos()) orelse - return error.FileTooBig; - - return file.readToEndAllocOptions(allocator, max_bytes, stat_size, alignment, optional_sentinel); + var file_reader = file.reader(&.{}); + return file_reader.interface.allocRemainingAlignedSentinel(gpa, limit, alignment, sentinel) catch |err| switch (err) { + error.ReadFailed => return file_reader.err.?, + error.OutOfMemory, error.StreamTooLong => |e| return e, + }; } pub const DeleteTreeError = error{ diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 60879a5ead..7afe322686 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -676,37 +676,47 @@ test "Dir.realpath smoke test" { }.impl); } -test "readAllAlloc" { +test "readFileAlloc" { var tmp_dir = tmpDir(.{}); defer tmp_dir.cleanup(); var file = try tmp_dir.dir.createFile("test_file", .{ .read = true }); defer file.close(); - const buf1 = try file.readToEndAlloc(testing.allocator, 1024); + const buf1 = try tmp_dir.dir.readFileAlloc("test_file", testing.allocator, .limited(1024)); defer testing.allocator.free(buf1); - try testing.expectEqual(@as(usize, 0), buf1.len); + try testing.expectEqualStrings("", buf1); const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n"; try file.writeAll(write_buf); - try file.seekTo(0); - - // max_bytes > file_size - const buf2 = try file.readToEndAlloc(testing.allocator, 1024); - defer testing.allocator.free(buf2); - try testing.expectEqual(write_buf.len, buf2.len); - try testing.expectEqualStrings(write_buf, buf2); - try file.seekTo(0); - - // max_bytes == file_size - const buf3 = try file.readToEndAlloc(testing.allocator, write_buf.len); - defer testing.allocator.free(buf3); - try testing.expectEqual(write_buf.len, buf3.len); - try testing.expectEqualStrings(write_buf, buf3); - try file.seekTo(0); + + { + // max_bytes > file_size + const buf2 = try tmp_dir.dir.readFileAlloc("test_file", testing.allocator, .limited(1024)); + defer testing.allocator.free(buf2); + try testing.expectEqualStrings(write_buf, buf2); + } + + { + // max_bytes == file_size + try testing.expectError( + error.StreamTooLong, + tmp_dir.dir.readFileAlloc("test_file", testing.allocator, .limited(write_buf.len)), + ); + } + + { + // max_bytes == file_size + 1 + const buf2 = try tmp_dir.dir.readFileAlloc("test_file", testing.allocator, .limited(write_buf.len + 1)); + defer testing.allocator.free(buf2); + try testing.expectEqualStrings(write_buf, buf2); + } // max_bytes < file_size - try testing.expectError(error.FileTooBig, file.readToEndAlloc(testing.allocator, write_buf.len - 1)); + try testing.expectError( + error.StreamTooLong, + tmp_dir.dir.readFileAlloc("test_file", testing.allocator, .limited(write_buf.len - 1)), + ); } test "Dir.statFile" { @@ -778,16 +788,16 @@ test "file operations on directories" { switch (native_os) { .dragonfly, .netbsd => { // no error when reading a directory. See https://github.com/ziglang/zig/issues/5732 - const buf = try ctx.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize)); + const buf = try ctx.dir.readFileAlloc(test_dir_name, testing.allocator, .unlimited); testing.allocator.free(buf); }, .wasi => { // WASI return EBADF, which gets mapped to NotOpenForReading. // See https://github.com/bytecodealliance/wasmtime/issues/1935 - try testing.expectError(error.NotOpenForReading, ctx.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize))); + try testing.expectError(error.NotOpenForReading, ctx.dir.readFileAlloc(test_dir_name, testing.allocator, .unlimited)); }, else => { - try testing.expectError(error.IsDir, ctx.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize))); + try testing.expectError(error.IsDir, ctx.dir.readFileAlloc(test_dir_name, testing.allocator, .unlimited)); }, } @@ -1564,7 +1574,7 @@ test "copyFile" { } fn expectFileContents(dir: Dir, file_path: []const u8, data: []const u8) !void { - const contents = try dir.readFileAlloc(testing.allocator, file_path, 1000); + const contents = try dir.readFileAlloc(file_path, testing.allocator, .limited(1000)); defer testing.allocator.free(contents); try testing.expectEqualSlices(u8, data, contents); @@ -1587,7 +1597,7 @@ test "AtomicFile" { try af.file_writer.interface.writeAll(test_content); try af.finish(); } - const content = try ctx.dir.readFileAlloc(allocator, test_out_file, 9999); + const content = try ctx.dir.readFileAlloc(test_out_file, allocator, .limited(9999)); try testing.expectEqualStrings(test_content, content); try ctx.dir.deleteFile(test_out_file); @@ -2004,7 +2014,7 @@ test "invalid UTF-8/WTF-8 paths" { } try testing.expectError(expected_err, ctx.dir.readFile(invalid_path, &[_]u8{})); - try testing.expectError(expected_err, ctx.dir.readFileAlloc(testing.allocator, invalid_path, 0)); + try testing.expectError(expected_err, ctx.dir.readFileAlloc(invalid_path, testing.allocator, .limited(0))); try testing.expectError(expected_err, ctx.dir.deleteTree(invalid_path)); try testing.expectError(expected_err, ctx.dir.deleteTreeMinStackSize(invalid_path)); |
