aboutsummaryrefslogtreecommitdiff
path: root/lib/std/fs
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-08-29 20:19:23 -0700
committerAndrew Kelley <andrew@ziglang.org>2025-08-30 00:48:50 -0700
commit9a0970a12bdcae105b6f3f65c0a72d95a209bd35 (patch)
treeab0de8f2447b5e52c2bb5c92a976a52fb4b613ee /lib/std/fs
parent79f267f6b9e7f80a6fed3b1019f9de942841c3be (diff)
downloadzig-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.zig78
-rw-r--r--lib/std/fs/test.zig60
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));