aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-12-10 23:09:08 -0800
committerAndrew Kelley <andrew@ziglang.org>2025-12-23 22:15:09 -0800
commit94ef56ee26558daea3c7a1468f898c94735d1658 (patch)
tree86e22c258d78ca40fb09504493bc2daaff029d6e /lib
parent7bc0166b7c34ac0120f50aceb7132ffaa4aeec83 (diff)
downloadzig-94ef56ee26558daea3c7a1468f898c94735d1658.tar.gz
zig-94ef56ee26558daea3c7a1468f898c94735d1658.zip
std.Io.Dir: fix walking
* Make Io.Dir.Reader lower level, accepting a buffer. * Make Io.Dir.Iterator higher level, requesting only one Entry with every call to `next`.
Diffstat (limited to 'lib')
-rw-r--r--lib/std/Io/Dir.zig68
-rw-r--r--lib/std/Io/Threaded.zig19
2 files changed, 44 insertions, 43 deletions
diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig
index 5da2b16f82..2c6bee99a5 100644
--- a/lib/std/Io/Dir.zig
+++ b/lib/std/Io/Dir.zig
@@ -95,8 +95,13 @@ pub const Reader = struct {
dir: Dir,
state: State,
/// Stores I/O implementation specific data.
- buffer: [2048]u8 align(@alignOf(usize)),
+ buffer: []u8 align(@alignOf(usize)),
+ /// Index of next entry in `buffer`.
index: usize,
+ /// Fill position of `buffer`.
+ end: usize,
+
+ pub const min_buffer_len = 32;
pub const State = enum {
/// Indicates the next call to `read` should rewind and start over the
@@ -112,27 +117,45 @@ pub const Reader = struct {
SystemResources,
} || Io.UnexpectedError || Io.Cancelable;
- pub fn init(dir: Dir) Reader {
+ pub fn init(dir: Dir, buffer: []align(@alignOf(usize)) u8) Reader {
+ assert(buffer.len >= min_buffer_len);
return .{
.dir = dir,
.state = .reset,
.index = 0,
- .buffer = undefined,
+ .end = 0,
+ .buffer = buffer,
};
}
+ /// All `Entry.name` are invalidated with the next call to `read` or
+ /// `next`.
pub fn read(r: *Reader, io: Io, buffer: []Entry) Error!usize {
return io.vtable.dirRead(io.userdata, r, buffer);
}
+
+ /// `Entry.name` is invalidated with the next call to `read` or `next`.
+ pub fn next(r: *Reader, io: Io) Error!?Entry {
+ var buffer: [1]Entry = undefined;
+ while (true) {
+ const n = try read(r, io, &buffer);
+ if (n == 1) return buffer[0];
+ if (r.state == .finished) return null;
+ }
+ }
};
+/// This API is designed for convenience rather than performance:
+/// * It chooses a buffer size rather than allowing the user to provide one.
+/// * It is movable by only requesting one `Entry` at a time from the `Io`
+/// implementation rather than doing batch operations.
+///
+/// Still, it will do a decent job of minimizing syscall overhead. For a
+/// lower level abstraction, see `Reader`. For a higher level abstraction,
+/// see `Walker`.
pub const Iterator = struct {
reader: Reader,
- buffer: [32]Entry,
- /// Index of next entry in `buffer`.
- index: usize,
- /// Fill position of `buffer`.
- end: usize,
+ reader_buffer: [2048]u8 align(@alignOf(usize)),
pub const Error = Reader.Error;
@@ -142,27 +165,16 @@ pub const Iterator = struct {
.dir = dir,
.state = reader_state,
.index = 0,
+ .end = 0,
.buffer = undefined,
},
- .buffer = undefined,
- .index = 0,
- .end = 0,
+ .reader_buffer = undefined,
};
}
pub fn next(it: *Iterator, io: Io) Error!?Entry {
- if (it.end - it.index == 0) {
- if (it.reader.state == .finished) return null;
- it.end = try it.reader.read(io, &it.buffer);
- it.index = 0;
- if (it.end - it.index == 0) {
- assert(it.reader.state == .finished);
- return null;
- }
- }
- const index = it.index;
- it.index = index + 1;
- return it.buffer[index];
+ it.reader.buffer = &it.reader_buffer;
+ return it.reader.next(io);
}
};
@@ -178,14 +190,14 @@ pub fn iterateAssumeFirstIteration(dir: Dir) Iterator {
}
pub const SelectiveWalker = struct {
- stack: std.ArrayList(Walker.StackItem),
+ stack: std.ArrayList(StackItem),
name_buffer: std.ArrayList(u8),
allocator: Allocator,
- pub const Error = Io.Dir.Iterator.Error || Allocator.Error;
+ pub const Error = Iterator.Error || Allocator.Error;
const StackItem = struct {
- iter: Dir.Iterator,
+ iter: Iterator,
dirname_len: usize,
};
@@ -942,7 +954,7 @@ pub fn renameAbsolute(io: Io, old_path: []const u8, new_path: []const u8) Rename
return io.vtable.dirRename(io.userdata, my_cwd, old_path, my_cwd, new_path);
}
-/// Use with `Dir.symLink`, `Dir.symLinkAtomic`, and `symLinkAbsolute` to
+/// Use with `symLink`, `symLinkAtomic`, and `symLinkAbsolute` to
/// specify whether the symlink will point to a file or a directory. This value
/// is ignored on all hosts except Windows where creating symlinks to different
/// resource types, requires different flags. By default, `symLinkAbsolute` is
@@ -1182,7 +1194,7 @@ pub fn deleteTree(dir: Dir, io: Io, sub_path: []const u8) DeleteTreeError!void {
const StackItem = struct {
name: []const u8,
parent_dir: Dir,
- iter: Dir.Iterator,
+ iter: Iterator,
fn closeAll(inner_io: Io, items: []@This()) void {
for (items) |*item| item.iter.reader.dir.close(inner_io);
diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig
index 55cc21db0e..05b00b555f 100644
--- a/lib/std/Io/Threaded.zig
+++ b/lib/std/Io/Threaded.zig
@@ -3280,19 +3280,9 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir
const linux = std.os.linux;
const t: *Threaded = @ptrCast(@alignCast(userdata));
const current_thread = Thread.getCurrent(t);
- const Header = extern struct {
- fill_end: usize,
- };
- const header: *Header = @ptrCast(&dr.buffer);
- const header_end: usize = @sizeOf(Header);
- if (dr.index < header_end) {
- // Initialize header.
- dr.index = header_end;
- header.* = .{ .fill_end = header_end };
- }
var buffer_index: usize = 0;
while (buffer.len - buffer_index != 0) {
- if (header.fill_end - dr.index == 0) {
+ if (dr.end - dr.index == 0) {
// Refill the buffer, unless we've already created references to
// buffered data.
if (buffer_index != 0) break;
@@ -3303,10 +3293,9 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir
};
dr.state = .reading;
}
- const dents_buffer = dr.buffer[header_end..];
try current_thread.beginSyscall();
const n = while (true) {
- const rc = linux.getdents64(dr.dir.handle, dents_buffer.ptr, dents_buffer.len);
+ const rc = linux.getdents64(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);
switch (linux.errno(rc)) {
.SUCCESS => {
current_thread.endSyscall();
@@ -3342,8 +3331,8 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir
dr.state = .finished;
return 0;
}
- dr.index = header_end;
- header.fill_end = header_end + n;
+ dr.index = 0;
+ dr.end = n;
}
const linux_entry: *align(1) linux.dirent64 = @ptrCast(&dr.buffer[dr.index]);
const next_index = dr.index + linux_entry.reclen;