Skip to content

Commit

Permalink
perf: use custom canonicalize impl to avoid heavy syscall
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Jul 10, 2024
1 parent 8013161 commit 697cc7f
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 37 deletions.
19 changes: 2 additions & 17 deletions src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ pub struct CachedPathImpl {
path: Box<Path>,
parent: Option<CachedPath>,
meta: OnceLock<Option<FileMetadata>>,
symlink: OnceLock<Option<PathBuf>>,
canonicalized: OnceLock<Option<PathBuf>>,
node_modules: OnceLock<Option<CachedPath>>,
package_json: OnceLock<Option<Arc<PackageJson>>>,
Expand All @@ -147,7 +146,6 @@ impl CachedPathImpl {
path,
parent,
meta: OnceLock::new(),
symlink: OnceLock::new(),
canonicalized: OnceLock::new(),
node_modules: OnceLock::new(),
package_json: OnceLock::new(),
Expand Down Expand Up @@ -190,24 +188,11 @@ impl CachedPathImpl {
)
}

fn symlink<Fs: FileSystem>(&self, fs: &Fs) -> io::Result<Option<PathBuf>> {
self.symlink
.get_or_try_init(|| {
if let Ok(symlink_metadata) = fs.symlink_metadata(&self.path) {
if symlink_metadata.is_symlink {
return fs.canonicalize(self.path()).map(Some);
}
}
Ok(None)
})
.cloned()
}

pub fn realpath<Fs: FileSystem>(&self, fs: &Fs) -> io::Result<PathBuf> {
self.canonicalized
.get_or_try_init(|| {
if let Some(link) = self.symlink(fs)? {
return Ok(Some(link));
if fs.symlink_metadata(&self.path).is_ok_and(|m| m.is_symlink) {
return fs.canonicalize(&self.path).map(Some);
}
if let Some(parent) = self.parent() {
let parent_path = parent.realpath(fs)?;
Expand Down
61 changes: 41 additions & 20 deletions src/file_system.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{
fs, io,
path::{Path, PathBuf},
path::{Component, Path, PathBuf},
};

/// File System abstraction used for `ResolverGeneric`
Expand Down Expand Up @@ -91,33 +91,54 @@ impl FileSystem for FileSystemOs {
}

fn canonicalize(&self, path: &Path) -> io::Result<PathBuf> {
#[cfg(not(target_os = "wasi"))]
#[cfg(target_os = "windows")]
{
dunce::canonicalize(path)
}
#[cfg(target_os = "wasi")]
#[cfg(not(target_os = "windows"))]
{
let meta = fs::symlink_metadata(path)?;
if meta.file_type().is_symlink() {
let link = fs::read_link(path)?;
let mut path_buf = path.to_path_buf();
path_buf.pop();
for segment in link.iter() {
match segment.to_str() {
Some("..") => {
path_buf.pop();
}
Some(".") | None => {}
Some(seg) => {
// Need to trim the extra \0 introduces by rust std rust-lang/rust#123727
path_buf.push(seg.trim_end_matches('\0'));
let mut path_buf = path.to_path_buf();
let mut is_symlink = true;
loop {
if is_symlink {
let link = fs::read_link(&path_buf)?;
path_buf.pop();
for component in link.components() {
match component {
Component::ParentDir => {
path_buf.pop();
}
Component::Normal(seg) => {
#[cfg(target_family = "wasm")]
// Need to trim the extra \0 introduces by https://github.com/nodejs/uvwasi/issues/262
{
path_buf.push(seg.to_string_lossy().trim_end_matches('\0'));
}
#[cfg(not(target_family = "wasm"))]
{
path_buf.push(seg);
}
}
Component::RootDir => {
#[cfg(target_os = "windows")]
{
path_buf.push("\\");
}
#[cfg(not(target_os = "windows"))]
{
#[allow(clippy::path_buf_push_overwrite)]
path_buf.push("/");
}
}
Component::CurDir | Component::Prefix(_) => {}
}
}
is_symlink = fs::symlink_metadata(&path_buf)?.is_symlink();
} else {
break;
}
Ok(path_buf)
} else {
Ok(path.to_path_buf())
}
Ok(path_buf)
}
}
}
Expand Down

0 comments on commit 697cc7f

Please sign in to comment.