Skip to content

Commit

Permalink
perf: allow to cache std::env::current_dir
Browse files Browse the repository at this point in the history
  • Loading branch information
hyf0 committed Apr 9, 2024
1 parent add7e84 commit f0799e6
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 13 deletions.
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ name = "sugar_path"
description = "Sugar functions for manipulating paths"
keywords = ["path", "path-slash", "relative-path"]
repository = "https://github.com/hyf0/sugar_path"
version = "1.0.0"
version = "1.1.0"
edition = "2021"
license = "MIT"

[features]
# Enable `cached_current_dir` will make `SugarPath` to only call `std::env::current_dir` once for all path operations.
cached_current_dir = []

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand All @@ -17,5 +20,5 @@ criterion = "0.4.0"
debug = true

[[bench]]
name = "normalize"
harness = false
name = "absolutize"
harness = false
25 changes: 25 additions & 0 deletions benches/absolutize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use std::path::Path;

use criterion::{criterion_group, criterion_main, Criterion};
use sugar_path::SugarPath;



fn absolutize() {
// "./hello".absolutize();
"/hello".absolutize();
}

fn absolutize_with(cwd: &Path) {
// "./hello".absolutize_with(cwd);
"/hello".absolutize_with(cwd);
}

fn criterion_benchmark(c: &mut Criterion) {
let cwd = std::env::current_dir().unwrap();
c.bench_function("absolutize", |b| b.iter(absolutize));
c.bench_function("absolutize_with", |b| b.iter(|| absolutize_with(&cwd)));
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
20 changes: 12 additions & 8 deletions src/impl_sugar_path.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{borrow::Cow, ops::Deref, path::{Component, Path, PathBuf}};

use crate::{utils::{component_vec_to_path_buf, to_normalized_components}, SugarPath};
use crate::{utils::{component_vec_to_path_buf, get_current_dir, to_normalized_components}, SugarPath};

impl SugarPath for Path {
fn normalize(&self) -> PathBuf {
Expand All @@ -20,12 +20,16 @@ impl SugarPath for Path {
}

fn absolutize(&self) -> PathBuf {
self.absolutize_with(std::env::current_dir().unwrap())
self.absolutize_with(get_current_dir())
}

fn absolutize_with(&self, base: impl Into<PathBuf>) -> PathBuf {
let base: PathBuf = base.into();
let mut base = if base.is_absolute() { base } else { base.absolutize() };
// Using `Cow` is on purpose.
// - Users could choose to pass a reference or an owned value depending on their use case.
// - If we accept `PathBuf` only, it may cause unnecessary allocations on case that `self` is already absolute.
// - If we accept `&Path` only, it may cause unnecessary cloning that users already have an owned value.
fn absolutize_with<'a>(&self, base: impl Into<Cow<'a, Path>>) -> PathBuf {
let base: Cow<'a, Path> = base.into();
let mut base = if base.is_absolute() { base } else { Cow::Owned(base.absolutize()) };

if self.is_absolute() {
self.normalize()
Expand All @@ -44,11 +48,11 @@ impl SugarPath for Path {
components.insert(1, Component::RootDir);
component_vec_to_path_buf(components).normalize()
} else {
base.push(self);
base.to_mut().push(self);
base.normalize()
}
} else {
base.push(self);
base.to_mut().push(self);
base.normalize()
}
}
Expand Down Expand Up @@ -145,7 +149,7 @@ impl<T: Deref<Target = str>> SugarPath for T {
self.as_path().absolutize()
}

fn absolutize_with(&self, base: impl Into<PathBuf>) -> PathBuf {
fn absolutize_with<'a>(&self, base: impl Into<Cow<'a, Path>>) -> PathBuf {
self.as_path().absolutize_with(base)
}

Expand Down
2 changes: 1 addition & 1 deletion src/sugar_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub trait SugarPath {
/// assert_eq!("..\\world".absolutize_with("C:\\hello"), "C:\\world".as_path());

Check failure on line 65 in src/sugar_path.rs

View workflow job for this annotation

GitHub Actions / Test windows

the trait bound `Cow<'_, Path>: From<&str>` is not satisfied

Check failure on line 65 in src/sugar_path.rs

View workflow job for this annotation

GitHub Actions / Test windows

the trait bound `Cow<'_, Path>: From<&str>` is not satisfied
/// }

Check failure on line 66 in src/sugar_path.rs

View workflow job for this annotation

GitHub Actions / Test windows

the trait bound `Cow<'_, Path>: From<&str>` is not satisfied

Check failure on line 66 in src/sugar_path.rs

View workflow job for this annotation

GitHub Actions / Test windows

the trait bound `Cow<'_, Path>: From<&str>` is not satisfied
/// ```
fn absolutize_with(&self, base: impl Into<PathBuf>) -> PathBuf;
fn absolutize_with<'a>(&self, base: impl Into<Cow<'a, Path>>) -> PathBuf;

///
/// ```rust
Expand Down
12 changes: 11 additions & 1 deletion src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
use std::path::{Component, Path, PathBuf};
use std::{borrow::Cow, path::{Component, Path, PathBuf}, sync::OnceLock};

static CURRENT_DIR: OnceLock<PathBuf> = OnceLock::new();

pub fn get_current_dir() -> Cow<'static, Path> {
if cfg!(feature = "cached_current_dir") {
let s: &'static Path = CURRENT_DIR.get_or_init(|| std::env::current_dir().unwrap());
Cow::Borrowed(s)
} else {
Cow::Owned(std::env::current_dir().unwrap())
}
}

#[inline]
pub fn component_vec_to_path_buf(components: Vec<Component>) -> PathBuf {
Expand Down

0 comments on commit f0799e6

Please sign in to comment.