summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2024-07-17 05:35:51 -0700
committerGitHub <noreply@github.com>2024-07-17 18:05:51 +0530
commit078def0ff8501bb07f3f286515acd8c6a2181037 (patch)
tree6811ec33761ede11e5213393e7ea3732fd54f292
parent568dd132fb0a47f9afb11bffec341c7481dda75c (diff)
perf(ext/node): optimize fs.exists[Sync] (#24613)
Use `access` on *nix and `GetFileAttributesW` on Windows. [Benchmark](https://paste.divy.work/p/-gq8Ark.js): ``` $ deno run -A bench.mjs # main (568dd) existsSync: 8980.636629ms $ target/release/deno run -A bench.mjs # this PR existsSync: 6448.7604519999995ms $ bun bench.mjs existsSync: 6562.88671ms $ node bench.mjs existsSync: 7740.064653ms ``` Ref https://github.com/denoland/deno/pull/24434#discussion_r1679777912
-rw-r--r--ext/fs/interface.rs3
-rw-r--r--ext/fs/std_fs.rs35
-rw-r--r--ext/node/lib.rs1
-rw-r--r--ext/node/ops/fs.rs23
-rw-r--r--ext/node/polyfills/_fs/_fs_exists.ts4
5 files changed, 63 insertions, 3 deletions
diff --git a/ext/fs/interface.rs b/ext/fs/interface.rs
index 2fdfa2186..09e16aff1 100644
--- a/ext/fs/interface.rs
+++ b/ext/fs/interface.rs
@@ -315,6 +315,9 @@ pub trait FileSystem: std::fmt::Debug + MaybeSend + MaybeSync {
fn exists_sync(&self, path: &Path) -> bool {
self.stat_sync(path).is_ok()
}
+ async fn exists_async(&self, path: PathBuf) -> FsResult<bool> {
+ Ok(self.stat_async(path).await.is_ok())
+ }
fn read_text_file_lossy_sync(
&self,
diff --git a/ext/fs/std_fs.rs b/ext/fs/std_fs.rs
index 7903700c3..7fc33a8ad 100644
--- a/ext/fs/std_fs.rs
+++ b/ext/fs/std_fs.rs
@@ -173,6 +173,15 @@ impl FileSystem for RealFs {
spawn_blocking(move || lstat(&path)).await?.map(Into::into)
}
+ fn exists_sync(&self, path: &Path) -> bool {
+ exists(path)
+ }
+ async fn exists_async(&self, path: PathBuf) -> FsResult<bool> {
+ spawn_blocking(move || exists(&path))
+ .await
+ .map_err(Into::into)
+ }
+
fn realpath_sync(&self, path: &Path) -> FsResult<PathBuf> {
realpath(path)
}
@@ -837,6 +846,32 @@ fn stat_extra(
}
}
+fn exists(path: &Path) -> bool {
+ #[cfg(unix)]
+ {
+ use nix::unistd::access;
+ use nix::unistd::AccessFlags;
+ access(path, AccessFlags::F_OK).is_ok()
+ }
+
+ #[cfg(windows)]
+ {
+ use std::os::windows::ffi::OsStrExt;
+ use winapi::um::fileapi::GetFileAttributesW;
+ use winapi::um::fileapi::INVALID_FILE_ATTRIBUTES;
+
+ let path = path
+ .as_os_str()
+ .encode_wide()
+ .chain(std::iter::once(0))
+ .collect::<Vec<_>>();
+ // Safety: `path` is a null-terminated string
+ let attrs = unsafe { GetFileAttributesW(path.as_ptr()) };
+
+ attrs != INVALID_FILE_ATTRIBUTES
+ }
+}
+
fn realpath(path: &Path) -> FsResult<PathBuf> {
Ok(deno_core::strip_unc_prefix(path.canonicalize()?))
}
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 13f9abc60..5be0fffa1 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -313,6 +313,7 @@ deno_core::extension!(deno_node,
ops::crypto::x509::op_node_x509_get_serial_number,
ops::crypto::x509::op_node_x509_key_usage,
ops::fs::op_node_fs_exists_sync<P>,
+ ops::fs::op_node_fs_exists<P>,
ops::fs::op_node_cp_sync<P>,
ops::fs::op_node_cp<P>,
ops::fs::op_node_lchown_sync<P>,
diff --git a/ext/node/ops/fs.rs b/ext/node/ops/fs.rs
index 47b66ee1d..687903325 100644
--- a/ext/node/ops/fs.rs
+++ b/ext/node/ops/fs.rs
@@ -26,7 +26,28 @@ where
.borrow_mut::<P>()
.check_read_with_api_name(&path, Some("node:fs.existsSync()"))?;
let fs = state.borrow::<FileSystemRc>();
- Ok(fs.lstat_sync(&path).is_ok())
+ Ok(fs.exists_sync(&path))
+}
+
+#[op2(async)]
+pub async fn op_node_fs_exists<P>(
+ state: Rc<RefCell<OpState>>,
+ #[string] path: String,
+) -> Result<bool, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ let path = PathBuf::from(path);
+
+ let fs = {
+ let mut state = state.borrow_mut();
+ state
+ .borrow_mut::<P>()
+ .check_read_with_api_name(&path, Some("node:fs.exists()"))?;
+ state.borrow::<FileSystemRc>().clone()
+ };
+
+ Ok(fs.exists_async(path).await?)
}
#[op2(fast)]
diff --git a/ext/node/polyfills/_fs/_fs_exists.ts b/ext/node/polyfills/_fs/_fs_exists.ts
index 57df1f07c..b5bbe235a 100644
--- a/ext/node/polyfills/_fs/_fs_exists.ts
+++ b/ext/node/polyfills/_fs/_fs_exists.ts
@@ -3,7 +3,7 @@
// TODO(petamoriken): enable prefer-primordials for node polyfills
// deno-lint-ignore-file prefer-primordials
-import { op_node_fs_exists_sync } from "ext:core/ops";
+import { op_node_fs_exists, op_node_fs_exists_sync } from "ext:core/ops";
import { pathFromURL } from "ext:deno_web/00_infra.js";
@@ -16,7 +16,7 @@ type ExistsCallback = (exists: boolean) => void;
*/
export function exists(path: string | URL, callback: ExistsCallback) {
path = path instanceof URL ? pathFromURL(path) : path;
- Deno.lstat(path).then(() => callback(true), () => callback(false));
+ op_node_fs_exists(path).then(callback);
}
// The callback of fs.exists doesn't have standard callback signature.