| ... | ... | @@ -95,8 +95,13 @@ pub const Reader = struct { |
| 95 | 95 | dir: Dir, |
| 96 | 96 | state: State, |
| 97 | 97 | /// Stores I/O implementation specific data. |
| 98 | | buffer: [2048]u8 align(@alignOf(usize)), |
| 98 | buffer: []u8 align(@alignOf(usize)), |
| 99 | /// Index of next entry in `buffer`. |
| 99 | 100 | index: usize, |
| 101 | /// Fill position of `buffer`. |
| 102 | end: usize, |
| 103 | |
| 104 | pub const min_buffer_len = 32; |
| 100 | 105 | |
| 101 | 106 | pub const State = enum { |
| 102 | 107 | /// Indicates the next call to `read` should rewind and start over the |
| ... | ... | @@ -112,27 +117,45 @@ pub const Reader = struct { |
| 112 | 117 | SystemResources, |
| 113 | 118 | } || Io.UnexpectedError || Io.Cancelable; |
| 114 | 119 | |
| 115 | | pub fn init(dir: Dir) Reader { |
| 120 | pub fn init(dir: Dir, buffer: []align(@alignOf(usize)) u8) Reader { |
| 121 | assert(buffer.len >= min_buffer_len); |
| 116 | 122 | return .{ |
| 117 | 123 | .dir = dir, |
| 118 | 124 | .state = .reset, |
| 119 | 125 | .index = 0, |
| 120 | | .buffer = undefined, |
| 126 | .end = 0, |
| 127 | .buffer = buffer, |
| 121 | 128 | }; |
| 122 | 129 | } |
| 123 | 130 | |
| 131 | /// All `Entry.name` are invalidated with the next call to `read` or |
| 132 | /// `next`. |
| 124 | 133 | pub fn read(r: *Reader, io: Io, buffer: []Entry) Error!usize { |
| 125 | 134 | return io.vtable.dirRead(io.userdata, r, buffer); |
| 126 | 135 | } |
| 136 | |
| 137 | /// `Entry.name` is invalidated with the next call to `read` or `next`. |
| 138 | pub fn next(r: *Reader, io: Io) Error!?Entry { |
| 139 | var buffer: [1]Entry = undefined; |
| 140 | while (true) { |
| 141 | const n = try read(r, io, &buffer); |
| 142 | if (n == 1) return buffer[0]; |
| 143 | if (r.state == .finished) return null; |
| 144 | } |
| 145 | } |
| 127 | 146 | }; |
| 128 | 147 | |
| 148 | /// This API is designed for convenience rather than performance: |
| 149 | /// * It chooses a buffer size rather than allowing the user to provide one. |
| 150 | /// * It is movable by only requesting one `Entry` at a time from the `Io` |
| 151 | /// implementation rather than doing batch operations. |
| 152 | /// |
| 153 | /// Still, it will do a decent job of minimizing syscall overhead. For a |
| 154 | /// lower level abstraction, see `Reader`. For a higher level abstraction, |
| 155 | /// see `Walker`. |
| 129 | 156 | pub const Iterator = struct { |
| 130 | 157 | reader: Reader, |
| 131 | | buffer: [32]Entry, |
| 132 | | /// Index of next entry in `buffer`. |
| 133 | | index: usize, |
| 134 | | /// Fill position of `buffer`. |
| 135 | | end: usize, |
| 158 | reader_buffer: [2048]u8 align(@alignOf(usize)), |
| 136 | 159 | |
| 137 | 160 | pub const Error = Reader.Error; |
| 138 | 161 | |
| ... | ... | @@ -142,27 +165,16 @@ pub const Iterator = struct { |
| 142 | 165 | .dir = dir, |
| 143 | 166 | .state = reader_state, |
| 144 | 167 | .index = 0, |
| 168 | .end = 0, |
| 145 | 169 | .buffer = undefined, |
| 146 | 170 | }, |
| 147 | | .buffer = undefined, |
| 148 | | .index = 0, |
| 149 | | .end = 0, |
| 171 | .reader_buffer = undefined, |
| 150 | 172 | }; |
| 151 | 173 | } |
| 152 | 174 | |
| 153 | 175 | pub fn next(it: *Iterator, io: Io) Error!?Entry { |
| 154 | | if (it.end - it.index == 0) { |
| 155 | | if (it.reader.state == .finished) return null; |
| 156 | | it.end = try it.reader.read(io, &it.buffer); |
| 157 | | it.index = 0; |
| 158 | | if (it.end - it.index == 0) { |
| 159 | | assert(it.reader.state == .finished); |
| 160 | | return null; |
| 161 | | } |
| 162 | | } |
| 163 | | const index = it.index; |
| 164 | | it.index = index + 1; |
| 165 | | return it.buffer[index]; |
| 176 | it.reader.buffer = &it.reader_buffer; |
| 177 | return it.reader.next(io); |
| 166 | 178 | } |
| 167 | 179 | }; |
| 168 | 180 | |
| ... | ... | @@ -178,14 +190,14 @@ pub fn iterateAssumeFirstIteration(dir: Dir) Iterator { |
| 178 | 190 | } |
| 179 | 191 | |
| 180 | 192 | pub const SelectiveWalker = struct { |
| 181 | | stack: std.ArrayList(Walker.StackItem), |
| 193 | stack: std.ArrayList(StackItem), |
| 182 | 194 | name_buffer: std.ArrayList(u8), |
| 183 | 195 | allocator: Allocator, |
| 184 | 196 | |
| 185 | | pub const Error = Io.Dir.Iterator.Error || Allocator.Error; |
| 197 | pub const Error = Iterator.Error || Allocator.Error; |
| 186 | 198 | |
| 187 | 199 | const StackItem = struct { |
| 188 | | iter: Dir.Iterator, |
| 200 | iter: Iterator, |
| 189 | 201 | dirname_len: usize, |
| 190 | 202 | }; |
| 191 | 203 | |
| ... | ... | @@ -942,7 +954,7 @@ pub fn renameAbsolute(io: Io, old_path: []const u8, new_path: []const u8) Rename |
| 942 | 954 | return io.vtable.dirRename(io.userdata, my_cwd, old_path, my_cwd, new_path); |
| 943 | 955 | } |
| 944 | 956 | |
| 945 | | /// Use with `Dir.symLink`, `Dir.symLinkAtomic`, and `symLinkAbsolute` to |
| 957 | /// Use with `symLink`, `symLinkAtomic`, and `symLinkAbsolute` to |
| 946 | 958 | /// specify whether the symlink will point to a file or a directory. This value |
| 947 | 959 | /// is ignored on all hosts except Windows where creating symlinks to different |
| 948 | 960 | /// 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 { |
| 1182 | 1194 | const StackItem = struct { |
| 1183 | 1195 | name: []const u8, |
| 1184 | 1196 | parent_dir: Dir, |
| 1185 | | iter: Dir.Iterator, |
| 1197 | iter: Iterator, |
| 1186 | 1198 | |
| 1187 | 1199 | fn closeAll(inner_io: Io, items: []@This()) void { |
| 1188 | 1200 | for (items) |*item| item.iter.reader.dir.close(inner_io); |