Skip to content

Commit b6eeab6

Browse files
authored
perf: use custom canonicalize impl to avoid usless syscall (#220)
Reduce one redundant fs::symlink_metadata system call.
1 parent e29975d commit b6eeab6

File tree

2 files changed

+37
-34
lines changed

2 files changed

+37
-34
lines changed

src/cache.rs

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ pub struct CachedPathImpl {
134134
path: Box<Path>,
135135
parent: Option<CachedPath>,
136136
meta: OnceLock<Option<FileMetadata>>,
137-
symlink: OnceLock<Option<PathBuf>>,
138137
canonicalized: OnceLock<Option<PathBuf>>,
139138
node_modules: OnceLock<Option<CachedPath>>,
140139
package_json: OnceLock<Option<Arc<PackageJson>>>,
@@ -147,7 +146,6 @@ impl CachedPathImpl {
147146
path,
148147
parent,
149148
meta: OnceLock::new(),
150-
symlink: OnceLock::new(),
151149
canonicalized: OnceLock::new(),
152150
node_modules: OnceLock::new(),
153151
package_json: OnceLock::new(),
@@ -190,24 +188,11 @@ impl CachedPathImpl {
190188
)
191189
}
192190

193-
fn symlink<Fs: FileSystem>(&self, fs: &Fs) -> io::Result<Option<PathBuf>> {
194-
self.symlink
195-
.get_or_try_init(|| {
196-
if let Ok(symlink_metadata) = fs.symlink_metadata(&self.path) {
197-
if symlink_metadata.is_symlink {
198-
return fs.canonicalize(self.path()).map(Some);
199-
}
200-
}
201-
Ok(None)
202-
})
203-
.cloned()
204-
}
205-
206191
pub fn realpath<Fs: FileSystem>(&self, fs: &Fs) -> io::Result<PathBuf> {
207192
self.canonicalized
208193
.get_or_try_init(|| {
209-
if let Some(link) = self.symlink(fs)? {
210-
return Ok(Some(link));
194+
if fs.symlink_metadata(&self.path).is_ok_and(|m| m.is_symlink) {
195+
return fs.canonicalize(&self.path).map(Some);
211196
}
212197
if let Some(parent) = self.parent() {
213198
let parent_path = parent.realpath(fs)?;

src/file_system.rs

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{
22
fs, io,
3-
path::{Path, PathBuf},
3+
path::{Component, Path, PathBuf},
44
};
55

66
/// File System abstraction used for `ResolverGeneric`
@@ -91,33 +91,51 @@ impl FileSystem for FileSystemOs {
9191
}
9292

9393
fn canonicalize(&self, path: &Path) -> io::Result<PathBuf> {
94-
#[cfg(not(target_os = "wasi"))]
94+
#[cfg(target_os = "windows")]
9595
{
9696
dunce::canonicalize(path)
9797
}
98-
#[cfg(target_os = "wasi")]
98+
#[cfg(not(target_os = "windows"))]
9999
{
100-
let meta = fs::symlink_metadata(path)?;
101-
if meta.file_type().is_symlink() {
102-
let link = fs::read_link(path)?;
103-
let mut path_buf = path.to_path_buf();
100+
let mut path_buf = path.to_path_buf();
101+
loop {
102+
let link = fs::read_link(&path_buf)?;
104103
path_buf.pop();
105-
for segment in link.iter() {
106-
match segment.to_str() {
107-
Some("..") => {
104+
for component in link.components() {
105+
match component {
106+
Component::ParentDir => {
108107
path_buf.pop();
109108
}
110-
Some(".") | None => {}
111-
Some(seg) => {
112-
// Need to trim the extra \0 introduces by rust std rust-lang/rust#123727
113-
path_buf.push(seg.trim_end_matches('\0'));
109+
Component::Normal(seg) => {
110+
#[cfg(target_family = "wasm")]
111+
// Need to trim the extra \0 introduces by https://github.com/nodejs/uvwasi/issues/262
112+
{
113+
path_buf.push(seg.to_string_lossy().trim_end_matches('\0'));
114+
}
115+
#[cfg(not(target_family = "wasm"))]
116+
{
117+
path_buf.push(seg);
118+
}
114119
}
120+
Component::RootDir => {
121+
#[cfg(target_os = "windows")]
122+
{
123+
path_buf.push("\\");
124+
}
125+
#[cfg(not(target_os = "windows"))]
126+
{
127+
#[allow(clippy::path_buf_push_overwrite)]
128+
path_buf.push("/");
129+
}
130+
}
131+
Component::CurDir | Component::Prefix(_) => {}
115132
}
116133
}
117-
Ok(path_buf)
118-
} else {
119-
Ok(path.to_path_buf())
134+
if !fs::symlink_metadata(&path_buf)?.is_symlink() {
135+
break;
136+
}
120137
}
138+
Ok(path_buf)
121139
}
122140
}
123141
}

0 commit comments

Comments
 (0)