//
// Syd: rock-solid application kernel
// benches/sys/unlink.rs: unlink microbenchmarks
//
// Copyright (c) 2024 Ali Polatel <alip@chesswob.org>
// Based in part upon gVisor's unlink_benchmark.cc which is:
//   Copyright 2020 The gVisor Authors.
//   SPDX-License-Identifier: Apache-2.0
//
// SPDX-License-Identifier: GPL-3.0

// This benchmark approximates gVisor's unlink micro-benchmark: for each run,
// we create a directory with N files, then unlink them all. In gVisor, it is
// done in a "batch" manner. We replicate the logic here using Brunch.

use std::{
    env,
    fs::{self, File},
    time::{Duration, SystemTime},
};

use brunch::{benches, Bench};
use nix::unistd::unlink;

/// Create a new directory in temp, fill it with `count` files, then
/// unlink all those files.
fn unlink_batch(count: usize) {
    // Create a unique directory in /tmp.
    let mut dir = env::temp_dir();
    let unique = format!(
        "syd_unlink_bench_{}_{}",
        count,
        SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_nanos()
    );
    dir.push(unique);
    fs::create_dir_all(&dir).unwrap_or_else(|_| panic!("Failed to create directory: {:?}", &dir));

    // Create `count` files in that directory.
    let mut paths = Vec::with_capacity(count);
    for i in 0..count {
        let file_path = dir.join(format!("file_{}", i));
        File::create(&file_path)
            .unwrap_or_else(|_| panic!("Failed to create file: {:?}", file_path));
        paths.push(file_path);
    }

    // Unlink all files.
    for file in paths {
        let _ = unlink(&file);
    }

    // Remove the directory itself.
    let _ = fs::remove_dir_all(&dir);
}

fn main() {
    // We replicate a range of file counts (up to 100,000 like the original).
    // Adjust as desired.
    benches!(
        inline:

        Bench::new("Unlink(1)").run(|| {
            unlink_batch(1);
        }),
        Bench::new("Unlink(100)").run(|| {
            unlink_batch(100);
        }),
        Bench::new("Unlink(1000)").run(|| {
            unlink_batch(1000);
        }),
        Bench::new("Unlink(10_000)").run(|| {
            unlink_batch(10_000);
        }),
        Bench::new("Unlink(100_000)")
            .with_timeout(Duration::from_secs(60))
            .run(|| {
                unlink_batch(100_000);
            }),
    );
}
