1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//! One-shot encode loop: a fresh context per frame, the shape upstream's
//! `ZSTD_compress` has.
//!
//! The existing fresh-compressor loops drive the STREAMING entry point
//! (`set_source` / `compress`), which stages the input into the matcher's
//! history by design. Upstream's one-shot entry creates a context, compresses
//! straight from the caller's buffer and frees the context
//! (`zstd_compress.c:5497`), so comparing that against our streaming path
//! measures two different contracts. This drives
//! [`compress_slice_to_vec`], which is the same contract: fresh context,
//! input borrowed, output returned.
//!
//! Run: `cargo run --release -p ffi-bench --example oneshot_loop
//! -- <level> <iters> <corpus path>`
//!
//! Compare against `ffi_encode_loop_z000033` with the same arguments.
use std::env;
use structured_zstd::encoding::{CompressionLevel, FrameCompressor, compress_bound};
fn main() {
let args: Vec<String> = env::args().collect();
let level: i32 = args.get(1).and_then(|s| s.parse().ok()).unwrap_or(3);
let iters: u32 = args.get(2).and_then(|s| s.parse().ok()).unwrap_or(2000);
let path = args
.get(3)
.map(String::as_str)
.unwrap_or("zstd/decodecorpus_files/z000033");
let src =
std::fs::read(path).unwrap_or_else(|e| panic!("oneshot_loop: cannot read {path}: {e}"));
let compressor_level = CompressionLevel::from_level(level);
// The destination is allocated once at the worst-case bound and reused,
// which is what the C loop does with `ZSTD_compressBound`. Only the
// CONTEXT is per-frame, so the comparison isolates the cost of building
// one — allocating a fresh output buffer per iteration as well would
// charge this side for something the other side never pays.
let mut out: Vec<u8> = Vec::with_capacity(compress_bound(src.len()));
let mut sink: usize = 0;
for _ in 0..iters {
let mut compressor: FrameCompressor<&[u8], &mut Vec<u8>> =
FrameCompressor::new(compressor_level);
out.clear();
compressor.compress_independent_frame_into(&src[..], &mut out);
sink = sink.wrapping_add(out.len());
core::hint::black_box(&out);
}
eprintln!(
"encoded {} bytes × {iters} iters at level {level} (one-shot); out-sum={sink}",
src.len(),
);
}