Skip to content

Commit e933993

Browse files
committed
Changed implementation of remove_dir_all from recursive to an iterative version
1 parent e6b64a2 commit e933993

1 file changed

Lines changed: 37 additions & 16 deletions

File tree

library/std/src/sys/fs/common.rs

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![allow(dead_code)] // not used on all platforms
2+
use alloc_crate::collections::VecDeque;
23

34
use crate::io::{self, Error, ErrorKind};
45
use crate::path::{Path, PathBuf};
@@ -29,27 +30,47 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
2930

3031
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
3132
let filetype = fs::symlink_metadata(path)?.file_type();
32-
if filetype.is_symlink() { fs::remove_file(path) } else { remove_dir_all_recursive(path) }
33+
if filetype.is_symlink() { fs::remove_file(path) } else { remove_dir_all_iterative(path) }
3334
}
3435

35-
fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
36-
for child in fs::read_dir(path)? {
37-
let result: io::Result<()> = try {
38-
let child = child?;
39-
if child.file_type()?.is_dir() {
40-
remove_dir_all_recursive(&child.path())?;
41-
} else {
42-
fs::remove_file(&child.path())?;
36+
fn remove_dir_all_iterative(path: &Path) -> io::Result<()> {
37+
// In unix/windows/solid/hermit/motor/uefi implementation of ReadDir struct, there is a field
38+
// called `root` that contains the directory path that we are reading directory entries
39+
// from. This makes holding a PathBuf in this tuple redundant since we only need this to remove
40+
// the directory when we have exhausted the ReadDir iterator; if we can expose that field
41+
// in the ReadDir struct through a method that return &Path, we can reduce memory usage
42+
// allocated to this VecDeque.
43+
44+
let mut directories = VecDeque::new();
45+
directories.push_front((path.to_path_buf(), fs::read_dir(path)?));
46+
47+
while !directories.is_empty() {
48+
let (parent_path, read_dir) = &mut directories[0];
49+
let child = read_dir.next();
50+
if let Some(child) = child {
51+
let result: io::Result<()> = try {
52+
let child = child?;
53+
if child.file_type()?.is_dir() {
54+
directories
55+
.push_front((child.path().to_path_buf(), fs::read_dir(&child.path())?));
56+
} else {
57+
fs::remove_file(&child.path())?;
58+
}
59+
};
60+
61+
// ignore internal NotFound errors to prevent race conditions
62+
if let Err(err) = &result
63+
&& err.kind() != io::ErrorKind::NotFound
64+
{
65+
return result;
4366
}
44-
};
45-
// ignore internal NotFound errors to prevent race conditions
46-
if let Err(err) = &result
47-
&& err.kind() != io::ErrorKind::NotFound
48-
{
49-
return result;
67+
} else {
68+
ignore_notfound(fs::remove_dir(parent_path))?;
69+
directories.pop_front();
5070
}
5171
}
52-
ignore_notfound(fs::remove_dir(path))
72+
73+
Ok(())
5374
}
5475

5576
pub fn exists(path: &Path) -> io::Result<bool> {

0 commit comments

Comments
 (0)