Skip to content

Instantly share code, notes, and snippets.

@tksst
Created September 6, 2022 14:58
Show Gist options
  • Save tksst/f046710b142ef57e50371fadd2d974c7 to your computer and use it in GitHub Desktop.
Save tksst/f046710b142ef57e50371fadd2d974c7 to your computer and use it in GitHub Desktop.
recursiveReaddir.mjs
import fs from "node:fs/promises";
import path from "node:path";
function isNil(it) {
return it === null || it === undefined;
}
function isNotNil(it) {
return it !== null && it !== undefined;
}
class Timer {
constructor() {
this.t = performance.now();
}
elapsed() {
return performance.now() - this.t;
}
log() {
console.log(performance.now() - this.t);
}
}
async function* recursiveReaddir(dir) {
const dirents = await fs.readdir(dir, { withFileTypes: true });
for (const dirent of dirents) {
const joined = path.join(dir, dirent.name);
if (dirent.isDirectory()) {
yield* recursiveReaddir(joined);
} else {
yield joined;
}
}
}
async function* recursiveReaddirNative(dir) {
for await (const dirent of await fs.opendir(dir, { bufferSize: 100 })) {
const joined = path.join(dir, dirent.name);
if (dirent.isDirectory()) {
yield* recursiveReaddirNative(joined);
} else {
yield joined;
}
}
}
class RelativePathDirent {
#dirent;
#path;
constructor(dirent, path) {
this.#dirent = dirent;
this.#path = path;
}
isDirectory() {
return this.#dirent.isDirectory();
}
name() {
return this.#dirent.name();
}
relativePath() {
return path.join(this.#path, this.#dirent.name);
}
}
class RelativePathDir {
#path;
#handle;
static async create(path) {
return new RelativePathDir(path, await fs.opendir(path));
}
constructor(path, handle) {
this.#path = path;
this.#handle = handle;
}
async read() {
const x = await this.#handle.read();
if (isNil(x)) {
return undefined;
}
return new RelativePathDirent(x, this.#path);
}
async close() {
return this.#handle.close();
}
}
class Dir {
#queue;
static async create(path) {
return new Dir(RelativePathDir.create(path));
}
constructor(handle) {
this.#queue = [handle];
}
async read() {
while (true) {
if (this.#queue.length === 0) {
return undefined;
}
const handle = await this.#queue.pop();
const dirent = await handle.read();
if (isNil(dirent)) {
handle.close();
continue;
}
this.#queue.push(handle);
if (dirent.isDirectory()) {
this.#queue.push(RelativePathDir.create(dirent.relativePath()));
continue;
}
return dirent.relativePath();
}
}
async readMulti(n) {
const ar = [];
for (let i = 0; i < n; ++i) {
ar.push(this.read());
}
const result = await Promise.all(ar);
if (isNil(result[n - 1])) {
return result.filter(isNotNil);
}
return result;
}
}
async function* recursiveReaddirOwnImpl(path) {
const d = await Dir.create(path);
while (true) {
const x = await d.read();
if (isNil(x)) {
break;
}
yield x;
}
}
async function* recursiveReaddirOwnImplMulti(path) {
const d = await Dir.create(path);
while (true) {
const x = await d.readMulti(100);
if (x.length === 0) {
break;
}
yield* x;
}
}
let t;
let d;
// t = new Timer();
// for await (const x of await recursiveReaddirOwnImpl("../")) {
// console.log(x);
// }
// t.log();
t = new Timer();
for await (const x of await recursiveReaddirOwnImplMulti("../")) {
// console.log(x);
}
t.log();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment