use base64::Engine as _;
use base64::engine::general_purpose::STANDARD as BASE64;
use bathy_interpret::{interpret, known_probe_ids};
use bathy_types::{ProbeCapture, Transport};
use criterion::{Criterion, criterion_group, criterion_main};
const CORPUS_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata/captures");
fn corpus() -> Vec<ProbeCapture> {
let mut paths: Vec<_> = std::fs::read_dir(CORPUS_DIR)
.expect("reading the capture corpus")
.filter_map(Result::ok)
.map(|e| e.path())
.filter(|p| p.extension().is_some_and(|e| e == "json"))
.collect();
paths.sort();
let mut out = Vec::new();
for path in paths {
let text = std::fs::read_to_string(&path).expect("reading a capture");
let value: serde_json::Value = serde_json::from_str(&text).expect("a capture is JSON");
let capture = value["capture"].clone();
let field = |name: &str| capture[name].as_str().map(str::to_owned);
let id = field("probe_id").expect("a capture names a probe");
let probe_id = known_probe_ids()
.find(|known| *known == id)
.unwrap_or_else(|| panic!("{}: unknown probe id {id}", path.display()));
out.push(ProbeCapture {
probe_id,
transport: match field("transport").as_deref() {
Some("udp") => Transport::Udp,
_ => Transport::Tcp,
},
port: capture["port"].as_u64().unwrap_or(0) as u16,
request: field("request").map(|r| BASE64.decode(r).expect("base64 request")),
response: BASE64
.decode(field("response").expect("a capture has a response"))
.expect("base64 response"),
elapsed_micros: capture["elapsed_micros"].as_u64().unwrap_or(0),
truncated: capture["truncated"].as_bool().unwrap_or(false),
});
}
assert!(
!out.is_empty(),
"the capture corpus is empty, so this benchmark would measure an empty loop"
);
out
}
fn bench(c: &mut Criterion) {
let corpus = corpus();
let bytes: u64 = corpus.iter().map(|c| c.response.len() as u64).sum();
let mut group = c.benchmark_group("interpret");
group.throughput(criterion::Throughput::Bytes(bytes));
group.bench_function("whole_corpus", |b| {
b.iter(|| {
let mut produced = 0usize;
for capture in &corpus {
produced += std::hint::black_box(interpret(capture)).len();
}
produced
})
});
let largest = corpus
.iter()
.max_by_key(|c| c.response.len())
.expect("non-empty");
group.bench_function("largest_single_response", |b| {
b.iter(|| std::hint::black_box(interpret(largest)).len())
});
group.finish();
}
criterion_group!(benches, bench);
criterion_main!(benches);