Skip to content

Commit 28fc566

Browse files
author
Ethan Arrowood
committed
fs: make readdir recursive algorithm iterative
1 parent 7b39e80 commit 28fc566

File tree

2 files changed

+61
-33
lines changed

2 files changed

+61
-33
lines changed

lib/fs.js

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
const {
2828
ArrayPrototypePush,
29+
ArrayPrototypeShift,
2930
BigIntPrototypeToString,
3031
MathMax,
3132
Number,
@@ -140,7 +141,6 @@ const {
140141
validateObject,
141142
validateString,
142143
} = require('internal/validators');
143-
144144
let truncateWarn = true;
145145
let fs;
146146

@@ -1404,34 +1404,64 @@ function mkdirSync(path, options) {
14041404
}
14051405
}
14061406

1407-
// TODO(Ethan-Arrowood): Make this iterative too
1408-
function readdirSyncRecursive(path, origPath, options) {
1409-
nullCheck(path, 'path', true);
1410-
const ctx = { path };
1411-
const result = binding.readdir(pathModule.toNamespacedPath(path),
1412-
options.encoding, !!options.withFileTypes, undefined, ctx);
1413-
handleErrorFromBinding(ctx);
1414-
return options.withFileTypes ?
1415-
getDirents(path, result).flatMap((dirent) => {
1416-
return [
1417-
dirent,
1418-
...(dirent.isDirectory() ?
1419-
readdirSyncRecursive(
1420-
pathModule.join(path, dirent.name),
1421-
origPath,
1422-
options,
1423-
) : []),
1424-
];
1425-
}) :
1426-
result.flatMap((ent) => {
1427-
const innerPath = pathModule.join(path, ent);
1428-
const relativePath = pathModule.relative(origPath, innerPath);
1429-
const stat = binding.internalModuleStat(innerPath);
1430-
return [
1431-
relativePath,
1432-
...(stat === 1 ? readdirSyncRecursive(innerPath, origPath, options) : []),
1433-
];
1434-
});
1407+
/**
1408+
* An iterative algorithm for reading the entire contents of the `basePath` directory.
1409+
* This function does not validate `basePath` as a directory. It is passed directly to
1410+
* `binding.readdir` after a `nullCheck`.
1411+
* @param {string} basePath
1412+
* @param {{ encoding: string, withFileTypes: boolean }} options
1413+
* @returns {Array<string> | Array<Dirent>}
1414+
*/
1415+
function readdirSyncRecursive(basePath, options) {
1416+
nullCheck(basePath, 'path', true);
1417+
1418+
const results = [];
1419+
const opsQueue = [];
1420+
1421+
function _read(_path) {
1422+
const ctx = { _path };
1423+
const result = binding.readdir(
1424+
pathModule.toNamespacedPath(_path),
1425+
options.encoding,
1426+
!!options.withFileTypes,
1427+
undefined,
1428+
ctx,
1429+
);
1430+
handleErrorFromBinding(ctx);
1431+
1432+
if (options.withFileTypes) {
1433+
const dirents = getDirents(_path, result);
1434+
for (let i = 0; i < dirents.length; i++) {
1435+
const dirent = dirents[i];
1436+
ArrayPrototypePush(results, dirent);
1437+
if (dirent.isDirectory()) {
1438+
ArrayPrototypePush(opsQueue, curryRead(pathModule.join(dirent.path, dirent.name)));
1439+
}
1440+
}
1441+
} else {
1442+
for (let i = 0; i < result.length; i++) {
1443+
const ent = result[i];
1444+
const innerPath = pathModule.join(_path, ent);
1445+
const relativePath = pathModule.relative(basePath, innerPath);
1446+
const stat = binding.internalModuleStat(innerPath);
1447+
ArrayPrototypePush(results, relativePath);
1448+
if (stat === 1) {
1449+
ArrayPrototypePush(opsQueue, curryRead(innerPath));
1450+
}
1451+
}
1452+
}
1453+
}
1454+
1455+
const curryRead = (p) => () => _read(p);
1456+
1457+
ArrayPrototypePush(opsQueue, curryRead(basePath));
1458+
1459+
while (opsQueue.length !== 0) {
1460+
const op = ArrayPrototypeShift(opsQueue);
1461+
op();
1462+
}
1463+
1464+
return results;
14351465
}
14361466

14371467
/**
@@ -1456,7 +1486,7 @@ function readdir(path, options, callback) {
14561486
}
14571487

14581488
if (options.recursive) {
1459-
callback(null, readdirSyncRecursive(path, path, options));
1489+
callback(null, readdirSyncRecursive(path, options));
14601490
return;
14611491
}
14621492

@@ -1494,7 +1524,7 @@ function readdirSync(path, options) {
14941524
}
14951525

14961526
if (options.recursive) {
1497-
return readdirSyncRecursive(path, path, options);
1527+
return readdirSyncRecursive(path, options);
14981528
}
14991529

15001530
const ctx = { path };

lib/internal/fs/dir.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,6 @@ class Dir {
160160
}
161161
}
162162

163-
// TODO(Ethan-Arrowood): Review this implementation. Make it iterative.
164-
// Can we better leverage the `kDirOperationQueue`?
165163
readSyncRecursive(dirent) {
166164
const ctx = { path: dirent.path };
167165
const handle = dirBinding.opendir(

0 commit comments

Comments
 (0)