Skip to content

Commit 49053cb

Browse files
squeek502andrewrk
authored andcommitted
Add fs.path.ComponentIterator and use it in Dir.makePath
Before this commit, there were three issues with the makePath implementation: 1. The component iteration did not 'collapse' consecutive path separators; instead, it would treat `a/b//c` as `a/b//c`, `a/b/`, `a/b`, and `a`. 2. Trailing path separators led to redundant `makeDir` calls, e.g. with the path `a/b/` (if `a` doesn't exist), it would try to create `a/b/`, then try `a/b`, then try `a`, then try `a/b`, and finally try `a/b/` again. 3. The iteration did not treat the root of a path specially, so on Windows it could attempt to make a directory with a path like `X:` for something like `X:\a\b\c` if the `X:\` drive doesn't exist. This didn't lead to any problems that I could find, but there's no reason to try to make a drive letter as a directory (or any other root path). This commit fixes all three issues by introducing a ComponentIterator that is root-aware and handles both sequential path separators and trailing path separators and uses it in `Dir.makePath`. This reduces the number of `makeDir` calls for paths where (1) the root of the path doesn't exist, (2) there are consecutive path separators, or (3) there are trailing path separators As an example, here are the makeDir calls that would have been made before this commit when calling `makePath` for a relative path like `a/b//c//` (where the full path needs to be created): a/b//c// a/b//c/ a/b//c a/b/ a/b a a/b a/b/ a/b//c a/b//c/ a/b//c// And after this commit: a/b//c a/b a a/b a/b//c
1 parent 7e1af51 commit 49053cb

File tree

3 files changed

+604
-24
lines changed

3 files changed

+604
-24
lines changed

lib/std/fs.zig

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,32 +1462,22 @@ pub const Dir = struct {
14621462
/// This function is not atomic, and if it returns an error, the file system may
14631463
/// have been modified regardless.
14641464
pub fn makePath(self: Dir, sub_path: []const u8) !void {
1465-
var end_index: usize = sub_path.len;
1465+
var it = try path.componentIterator(sub_path);
1466+
var component = it.last() orelse return;
14661467
while (true) {
1467-
self.makeDir(sub_path[0..end_index]) catch |err| switch (err) {
1468+
self.makeDir(component.path) catch |err| switch (err) {
14681469
error.PathAlreadyExists => {
14691470
// TODO stat the file and return an error if it's not a directory
14701471
// this is important because otherwise a dangling symlink
14711472
// could cause an infinite loop
1472-
if (end_index == sub_path.len) return;
14731473
},
1474-
error.FileNotFound => {
1475-
// march end_index backward until next path component
1476-
while (true) {
1477-
if (end_index == 0) return err;
1478-
end_index -= 1;
1479-
if (path.isSep(sub_path[end_index])) break;
1480-
}
1474+
error.FileNotFound => |e| {
1475+
component = it.previous() orelse return e;
14811476
continue;
14821477
},
1483-
else => return err,
1478+
else => |e| return e,
14841479
};
1485-
if (end_index == sub_path.len) return;
1486-
// march end_index forward until next path component
1487-
while (true) {
1488-
end_index += 1;
1489-
if (end_index == sub_path.len or path.isSep(sub_path[end_index])) break;
1490-
}
1480+
component = it.next() orelse return;
14911481
}
14921482
}
14931483

0 commit comments

Comments
 (0)