path_no_alloc 0.1.2

Provides with_paths! macro, for joining paths ergonomically and without allocation.
Documentation
use rand::distributions::{Distribution, Uniform};
use std::{path::Path};

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use path_no_alloc::with_paths;

pub fn array_from_idx<const N: usize, T>(f: impl FnMut(usize) -> T) -> [T; N] {
    let mut indices = [0; N];

    for i in 0..N {
        indices[i] = i;
    }

    indices.map(f)
}
pub fn criterion_benchmark(c: &mut Criterion) {
    let p1 = "Call me Ishmael. Some years ago - never mind how long precisely - having little or no money in my purse, and nothing particular to interest me on shore";
    let p2 = "I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen and regulating the circulation.";

    let p1_slices = array_from_idx::<129, _>(|i| &p1[..i]);
    let p2_slices = array_from_idx::<129, _>(|i| &p2[..i]);

    let mut group = c.benchmark_group("Path join");

    for i in 1..128 {
        group.bench_with_input(BenchmarkId::new("with_paths!", i), &i, |b, i| {
            b.iter(|| {
                let p1 = p1_slices[*i];
                let p2 = p2_slices[*i];

                with_paths! {
                    path = p1 / p2 => black_box(path)
                };
            })
        });

        group.bench_with_input(BenchmarkId::new("Path.join", i), &i, |b, i| {
            b.iter(|| {
                let p1 = p1_slices[*i];
                let p2 = p2_slices[*i];

                black_box(Path::new(p1).join(p2))
            })
        });
    }
}

pub fn join_random(c: &mut Criterion) {
    let mut rng = rand::thread_rng();
    const SAMPLE_SIZE: usize = 1 << 17;

    let options = b"abcdef/";
    let opt_dist = Uniform::from(0..options.len());

    let mut group = c.benchmark_group("join");

    for mean_len in (1..12).map(|i| i * 10) {
        let length_dist = Uniform::from(0..mean_len);

        let mut paths: Vec<String> = vec![];
        paths.reserve(SAMPLE_SIZE);

        for _ in 0..SAMPLE_SIZE {
            let len = length_dist.sample(&mut rng);

            let s: String = opt_dist
                .sample_iter(&mut rng)
                .take(len)
                .map(|i| options[i] as char)
                .collect();
            paths.push(s);
        }

        let mut i1 = 0;
        let mut i2 = 0;
        group.bench_function(BenchmarkId::new("with_paths!", mean_len), |b| {
            b.iter(|| {
                let p1 = &paths[i1];
                let p2 = &paths[i2];
                with_paths! {
                    path = p1 / p2 => black_box(path)
                };
                i1 = (i1 + 1) % SAMPLE_SIZE;
                i2 = (i2 + 2) % SAMPLE_SIZE;
            })
        });

        i1 = 0; i2 = 0;
        group.bench_function(BenchmarkId::new("Path.join", mean_len), |b| {
            b.iter(|| {
                let p1 = &paths[i1];
                let p2 = &paths[i2];
                black_box(Path::new(p1).join(p2));
                i1 = (i1 + 1) % SAMPLE_SIZE;
                i2 = (i2 + 2) % SAMPLE_SIZE;
            })
        });
    }
}

pub fn exists_random(c: &mut Criterion) {
    let mut rng = rand::thread_rng();
    const SAMPLE_SIZE: usize = 1 << 17;

    let options = b"abcdef/";
    let opt_dist = Uniform::from(0..options.len());

    let mut group = c.benchmark_group("exists");

    for mean_len in (1..12).map(|i| i * 10) {
        let length_dist = Uniform::from(0..mean_len);

        let mut paths: Vec<String> = vec![];
        paths.reserve(SAMPLE_SIZE);

        for _ in 0..SAMPLE_SIZE {
            let len = length_dist.sample(&mut rng);

            let s: String = opt_dist
                .sample_iter(&mut rng)
                .take(len)
                .map(|i| options[i] as char)
                .collect();
            paths.push(s);
        }

        let mut i1 = 0;
        let mut i2 = 0;
        group.bench_function(BenchmarkId::new("with_paths!", mean_len), |b| {
            b.iter(|| {
                let p1 = &paths[i1];
                let p2 = &paths[i2];
                with_paths! {
                    path = p1 / p2 => black_box(path.exists())
                };
                i1 = (i1 + 1) % SAMPLE_SIZE;
                i2 = (i2 + 2) % SAMPLE_SIZE;
            })
        });

        i1 = 0; i2 = 0;
        group.bench_function(BenchmarkId::new("Path.join", mean_len), |b| {
            b.iter(|| {
                let p1 = &paths[i1];
                let p2 = &paths[i2];
                black_box(Path::new(p1).join(p2).exists());
                i1 = (i1 + 1) % SAMPLE_SIZE;
                i2 = (i2 + 2) % SAMPLE_SIZE;
            })
        });
    }
}

criterion_group!(benches, criterion_benchmark);
criterion_group!(random, join_random, exists_random);
criterion_main!(benches, random);