sugar_path 3.0.0

Host-native lexical path manipulation with borrowed and owned-buffer APIs
Documentation
use std::hint::black_box;
use std::path::{Path, PathBuf};

use criterion::{BatchSize, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use sugar_path::{SugarPath, SugarPathBuf};

mod support;

#[cfg(any(unix, windows))]
use support::workloads::invalid_unicode_path;
use support::workloads::{
  CANONICAL_LEADING_PARENTS, CURRENT_DIRECTORY_CASES, DIRTY_PATHS, LEADING_PARENT_SCAN_CASES,
  PathCase, ROLLDOWN_PATHS,
};

fn bench_path_cases(criterion: &mut Criterion, group_name: &str, cases: &[PathCase]) {
  let mut group = criterion.benchmark_group(group_name);
  for case in cases {
    group.throughput(Throughput::Bytes(case.path.len() as u64));
    group.bench_with_input(BenchmarkId::from_parameter(case.name), case, |bencher, case| {
      bencher.iter(|| {
        let input = Path::new(black_box(case.path));
        black_box(input.normalize())
      });
    });
  }
  group.finish();
}

fn bench_normalize(criterion: &mut Criterion) {
  bench_path_cases(criterion, "normalize/clean_rolldown", ROLLDOWN_PATHS);
  bench_path_cases(criterion, "normalize/needs_work", DIRTY_PATHS);
  bench_path_cases(criterion, "normalize/canonical_leading_parents", &[CANONICAL_LEADING_PARENTS]);
  bench_path_cases(criterion, "normalize/leading_parent_prescan", LEADING_PARENT_SCAN_CASES);
  bench_path_cases(criterion, "normalize/current_directory_spellings", CURRENT_DIRECTORY_CASES);

  let total_bytes = ROLLDOWN_PATHS.iter().map(|case| case.path.len() as u64).sum();
  let mut group = criterion.benchmark_group("normalize/rolldown_corpus");
  group.throughput(Throughput::Bytes(total_bytes));
  group.bench_function("clean", |bencher| {
    bencher.iter(|| {
      for case in black_box(ROLLDOWN_PATHS) {
        let input = Path::new(black_box(case.path));
        black_box(input.normalize());
      }
    });
  });
  group.finish();

  #[cfg(any(unix, windows))]
  {
    let invalid = invalid_unicode_path();
    let mut group = criterion.benchmark_group("normalize/invalid_encoding");
    group.throughput(Throughput::Bytes(invalid.as_os_str().len() as u64));
    group.bench_function("lexically_clean", |bencher| {
      bencher.iter(|| black_box(black_box(invalid.as_path()).normalize()));
    });
    group.finish();
  }

  for case in [&ROLLDOWN_PATHS[2], &DIRTY_PATHS[1]] {
    let mut group = criterion.benchmark_group(format!("normalize/owned_input/{}", case.name));
    group.throughput(Throughput::Bytes(case.path.len() as u64));
    group.bench_function("borrowed_receiver/pathbuf_result", |bencher| {
      bencher.iter_batched(
        || PathBuf::from(case.path),
        |input| black_box(black_box(input.as_path()).normalize().into_owned()),
        BatchSize::SmallInput,
      );
    });
    group.bench_function("owned_receiver/pathbuf_result", |bencher| {
      bencher.iter_batched(
        || PathBuf::from(case.path),
        |input| black_box(black_box(input).into_normalized()),
        BatchSize::SmallInput,
      );
    });
    group.finish();
  }
}

criterion_group!(benches, bench_normalize);
criterion_main!(benches);