| ... | @@ -646,16 +646,17 @@ fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator { | ... | @@ -646,16 +646,17 @@ fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator { |
| 646 | } | 646 | } |
| 647 | | 647 | |
| 648 | pub const Walker = struct { | 648 | pub const Walker = struct { |
| 649 | stack: std.ArrayList(StackItem), | 649 | stack: std.ArrayListUnmanaged(StackItem), |
| 650 | name_buffer: std.ArrayList(u8), | 650 | name_buffer: std.ArrayListUnmanaged(u8), |
| | 651 | allocator: Allocator, |
| 651 | | 652 | |
| 652 | pub const WalkerEntry = struct { | 653 | pub const WalkerEntry = struct { |
| 653 | /// The containing directory. This can be used to operate directly on `basename` | 654 | /// The containing directory. This can be used to operate directly on `basename` |
| 654 | /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. | 655 | /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. |
| 655 | /// The directory remains open until `next` or `deinit` is called. | 656 | /// The directory remains open until `next` or `deinit` is called. |
| 656 | dir: Dir, | 657 | dir: Dir, |
| 657 | basename: []const u8, | 658 | basename: [:0]const u8, |
| 658 | path: []const u8, | 659 | path: [:0]const u8, |
| 659 | kind: Dir.Entry.Kind, | 660 | kind: Dir.Entry.Kind, |
| 660 | }; | 661 | }; |
| 661 | | 662 | |
| ... | @@ -668,6 +669,7 @@ pub const Walker = struct { | ... | @@ -668,6 +669,7 @@ pub const Walker = struct { |
| 668 | /// from this function becomes invalid. A copy must be made in order to keep | 669 | /// from this function becomes invalid. A copy must be made in order to keep |
| 669 | /// a reference to the path. | 670 | /// a reference to the path. |
| 670 | pub fn next(self: *Walker) !?WalkerEntry { | 671 | pub fn next(self: *Walker) !?WalkerEntry { |
| | 672 | const gpa = self.allocator; |
| 671 | while (self.stack.items.len != 0) { | 673 | while (self.stack.items.len != 0) { |
| 672 | // `top` and `containing` become invalid after appending to `self.stack` | 674 | // `top` and `containing` become invalid after appending to `self.stack` |
| 673 | var top = &self.stack.items[self.stack.items.len - 1]; | 675 | var top = &self.stack.items[self.stack.items.len - 1]; |
| ... | @@ -686,10 +688,12 @@ pub const Walker = struct { | ... | @@ -686,10 +688,12 @@ pub const Walker = struct { |
| 686 | }) |base| { | 688 | }) |base| { |
| 687 | self.name_buffer.shrinkRetainingCapacity(dirname_len); | 689 | self.name_buffer.shrinkRetainingCapacity(dirname_len); |
| 688 | if (self.name_buffer.items.len != 0) { | 690 | if (self.name_buffer.items.len != 0) { |
| 689 | try self.name_buffer.append(fs.path.sep); | 691 | try self.name_buffer.append(gpa, fs.path.sep); |
| 690 | dirname_len += 1; | 692 | dirname_len += 1; |
| 691 | } | 693 | } |
| 692 | try self.name_buffer.appendSlice(base.name); | 694 | try self.name_buffer.ensureUnusedCapacity(gpa, base.name.len + 1); |
| | 695 | self.name_buffer.appendSliceAssumeCapacity(base.name); |
| | 696 | self.name_buffer.appendAssumeCapacity(0); |
| 693 | if (base.kind == .directory) { | 697 | if (base.kind == .directory) { |
| 694 | var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { | 698 | var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { |
| 695 | error.NameTooLong => unreachable, // no path sep in base.name | 699 | error.NameTooLong => unreachable, // no path sep in base.name |
| ... | @@ -697,18 +701,18 @@ pub const Walker = struct { | ... | @@ -697,18 +701,18 @@ pub const Walker = struct { |
| 697 | }; | 701 | }; |
| 698 | { | 702 | { |
| 699 | errdefer new_dir.close(); | 703 | errdefer new_dir.close(); |
| 700 | try self.stack.append(StackItem{ | 704 | try self.stack.append(gpa, .{ |
| 701 | .iter = new_dir.iterateAssumeFirstIteration(), | 705 | .iter = new_dir.iterateAssumeFirstIteration(), |
| 702 | .dirname_len = self.name_buffer.items.len, | 706 | .dirname_len = self.name_buffer.items.len - 1, |
| 703 | }); | 707 | }); |
| 704 | top = &self.stack.items[self.stack.items.len - 1]; | 708 | top = &self.stack.items[self.stack.items.len - 1]; |
| 705 | containing = &self.stack.items[self.stack.items.len - 2]; | 709 | containing = &self.stack.items[self.stack.items.len - 2]; |
| 706 | } | 710 | } |
| 707 | } | 711 | } |
| 708 | return WalkerEntry{ | 712 | return .{ |
| 709 | .dir = containing.iter.dir, | 713 | .dir = containing.iter.dir, |
| 710 | .basename = self.name_buffer.items[dirname_len..], | 714 | .basename = self.name_buffer.items[dirname_len .. self.name_buffer.items.len - 1 :0], |
| 711 | .path = self.name_buffer.items, | 715 | .path = self.name_buffer.items[0 .. self.name_buffer.items.len - 1 :0], |
| 712 | .kind = base.kind, | 716 | .kind = base.kind, |
| 713 | }; | 717 | }; |
| 714 | } else { | 718 | } else { |
| ... | @@ -722,37 +726,39 @@ pub const Walker = struct { | ... | @@ -722,37 +726,39 @@ pub const Walker = struct { |
| 722 | } | 726 | } |
| 723 | | 727 | |
| 724 | pub fn deinit(self: *Walker) void { | 728 | pub fn deinit(self: *Walker) void { |
| | 729 | const gpa = self.allocator; |
| 725 | // Close any remaining directories except the initial one (which is always at index 0) | 730 | // Close any remaining directories except the initial one (which is always at index 0) |
| 726 | if (self.stack.items.len > 1) { | 731 | if (self.stack.items.len > 1) { |
| 727 | for (self.stack.items[1..]) |*item| { | 732 | for (self.stack.items[1..]) |*item| { |
| 728 | item.iter.dir.close(); | 733 | item.iter.dir.close(); |
| 729 | } | 734 | } |
| 730 | } | 735 | } |
| 731 | self.stack.deinit(); | 736 | self.stack.deinit(gpa); |
| 732 | self.name_buffer.deinit(); | 737 | self.name_buffer.deinit(gpa); |
| 733 | } | 738 | } |
| 734 | }; | 739 | }; |
| 735 | | 740 | |
| 736 | /// Recursively iterates over a directory. | 741 | /// Recursively iterates over a directory. |
| | 742 | /// |
| 737 | /// `self` must have been opened with `OpenDirOptions{.iterate = true}`. | 743 | /// `self` must have been opened with `OpenDirOptions{.iterate = true}`. |
| 738 | /// Must call `Walker.deinit` when done. | 744 | /// |
| | 745 | /// `Walker.deinit` releases allocated memory and directory handles. |
| | 746 | /// |
| 739 | /// The order of returned file system entries is undefined. | 747 | /// The order of returned file system entries is undefined. |
| | 748 | /// |
| 740 | /// `self` will not be closed after walking it. | 749 | /// `self` will not be closed after walking it. |
| 741 | pub fn walk(self: Dir, allocator: Allocator) !Walker { | 750 | pub fn walk(self: Dir, allocator: Allocator) Allocator.Error!Walker { |
| 742 | var name_buffer = std.ArrayList(u8).init(allocator); | 751 | var stack: std.ArrayListUnmanaged(Walker.StackItem) = .{}; |
| 743 | errdefer name_buffer.deinit(); | | |
| 744 | | | |
| 745 | var stack = std.ArrayList(Walker.StackItem).init(allocator); | | |
| 746 | errdefer stack.deinit(); | | |
| 747 | | 752 | |
| 748 | try stack.append(Walker.StackItem{ | 753 | try stack.append(allocator, .{ |
| 749 | .iter = self.iterate(), | 754 | .iter = self.iterate(), |
| 750 | .dirname_len = 0, | 755 | .dirname_len = 0, |
| 751 | }); | 756 | }); |
| 752 | | 757 | |
| 753 | return Walker{ | 758 | return .{ |
| 754 | .stack = stack, | 759 | .stack = stack, |
| 755 | .name_buffer = name_buffer, | 760 | .name_buffer = .{}, |
| | 761 | .allocator = allocator, |
| 756 | }; | 762 | }; |
| 757 | } | 763 | } |
| 758 | | 764 | |