use bindet::{detect, detect_at_start, FileTypeMatches};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rand::Rng;
use std::fs::{read_dir, OpenOptions};
use std::io::{BufReader, BufWriter, Error, Write};
use std::path::{Path, PathBuf};
fn generate<P: AsRef<Path>>(path: P, size: usize) {
if path.as_ref().exists() {
return;
}
let mut file = OpenOptions::new()
.write(true)
.create(true)
.open(path)
.expect("failed to write random data");
let mut writer = BufWriter::new(&mut file);
let mut rng = rand::thread_rng();
let mut buffer = [0u8; 8096];
let mut total = 0;
while total < size {
let write_size = std::cmp::min(size - total, buffer.len());
let buffer = &mut buffer[..write_size];
rng.fill(buffer);
writer.write(buffer).expect("failed to write random data");
total += write_size;
}
}
fn list_files() -> impl Iterator<Item = PathBuf> {
read_dir("files/")
.expect("failed to read files directory")
.map(|entry| entry.expect("failed to read entry").path())
.filter(|p| {
p.extension().map_or(false, |e| {
e.to_str().map_or(false, |s| s != "big" && s != "asc")
})
})
}
fn test_detect<P>(path: P) -> Result<Option<FileTypeMatches>, Error>
where
P: AsRef<Path>,
{
let file = OpenOptions::new().read(true).open(path).unwrap();
let buf = BufReader::new(file);
detect(buf)
}
fn test_fast_detect<P>(path: P) -> Result<Option<FileTypeMatches>, Error>
where
P: AsRef<Path>,
{
let file = OpenOptions::new().read(true).open(path).unwrap();
let buf = BufReader::new(file);
detect_at_start(buf)
}
fn detect_rar_sfx() -> Result<Option<FileTypeMatches>, Error> {
test_detect(black_box("files/hello-world.exe"))
}
fn detect_2mib_sfx() -> Result<Option<FileTypeMatches>, Error> {
test_detect(black_box("files/2mib.exe"))
}
fn detect_fast_2mib_sfx() -> Result<Option<FileTypeMatches>, Error> {
test_fast_detect(black_box("files/2mib.exe"))
}
fn detect_fast_rar_sfx() -> Result<Option<FileTypeMatches>, Error> {
test_fast_detect(black_box("files/hello-world.exe"))
}
fn criterion_benchmark(c: &mut Criterion) {
{
let mut group = c.benchmark_group("sfx");
group.bench_function("sfx", |b| b.iter(detect_rar_sfx));
group.bench_function("start-of-sfx", |b| b.iter(detect_fast_rar_sfx));
group.bench_function("2mib-sfx", |b| b.iter(detect_2mib_sfx));
group.bench_function("start-of-2mib-sfx", |b| b.iter(detect_fast_2mib_sfx));
generate("files/1gib.data.big", 1 << 30);
generate("files/100mib.data.big", 100 << 20);
group.bench_function("1gib", |b| {
b.iter(|| test_detect(black_box("files/1gib.data.big")))
});
group.bench_function("start-of-1gib", |b| {
b.iter(|| test_fast_detect(black_box("files/1gib.data.big")))
});
group.bench_function("100mib", |b| {
b.iter(|| test_detect(black_box("files/100mib.data.big")))
});
group.bench_function("start-of-100mib", |b| {
b.iter(|| test_fast_detect(black_box("files/100mib.data.big")))
});
}
{
let mut group = c.benchmark_group("regular_detections");
group.bench_function("little_tar", |b| {
b.iter(|| test_detect(black_box("files/test.tar")))
});
group.bench_function("1mb_tar", |b| {
b.iter(|| test_detect(black_box("files/test-0.tar")))
});
group.bench_function("multi", |b| {
b.iter(|| {
let _ = black_box(test_detect(black_box("files/hello.rar")));
let _ = black_box(test_detect(black_box("files/hello.zip")));
let _ = black_box(test_detect(black_box("files/rust-logo.png")));
})
});
}
{
let mut group = c.benchmark_group("all-types");
let mut files = list_files();
for file in files {
let name = file.file_stem().expect("name").to_str().expect("name");
let ext = file
.extension()
.expect("extension")
.to_str()
.expect("extension");
group.bench_with_input(format!("{name}-{ext}"), &file, |b, file| {
b.iter(|| test_detect(file))
});
}
}
}
criterion_group! {
name = benches;
config = Criterion::default().sample_size(50);
targets = criterion_benchmark
}
criterion_main!(benches);