structured-zstd 0.0.21

Pure Rust zstd implementation — managed fork of ruzstd. Dictionary decompression, no FFI.
Documentation
use criterion::{BatchSize, BenchmarkId, Criterion, criterion_group, criterion_main};
use std::hint::black_box;

use structured_zstd::decoding::{Dictionary, DictionaryHandle, FrameDecoder};
use structured_zstd::encoding::{CompressionLevel, FrameCompressor};

fn build_payload() -> Vec<u8> {
    let mut payload = Vec::with_capacity(512);
    for i in 0..512u16 {
        payload.push((i % 251) as u8);
    }
    payload
}

fn compress_with_dictionary(payload: &[u8], dict_raw: &[u8]) -> Vec<u8> {
    let dict = Dictionary::decode_dict(dict_raw).expect("dictionary should parse");
    let mut compressor = FrameCompressor::new(CompressionLevel::Default);
    compressor
        .set_dictionary(dict)
        .expect("dictionary should attach");
    compressor.set_source_size_hint(payload.len() as u64);
    compressor.set_source(payload);
    let mut compressed = Vec::new();
    compressor.set_drain(&mut compressed);
    compressor.compress();
    compressed
}

fn bench_decode_dict_handle(c: &mut Criterion) {
    let dict_raw = include_bytes!("../dict_tests/dictionary");
    let payload = build_payload();
    let compressed = compress_with_dictionary(&payload, dict_raw.as_slice());
    let output_len = payload.len();

    let handle = DictionaryHandle::decode_dict(dict_raw).expect("dictionary should parse");

    let mut output = vec![0u8; output_len];
    let mut decoder = FrameDecoder::new();
    decoder
        .decode_all_with_dict_handle(compressed.as_slice(), &mut output, &handle)
        .expect("decode should succeed");
    assert_eq!(
        output, payload,
        "prepared_handle produced unexpected output"
    );

    output.fill(0);
    let mut decoder = FrameDecoder::new();
    decoder
        .decode_all_with_dict_bytes(compressed.as_slice(), &mut output, dict_raw)
        .expect("decode should succeed");
    assert_eq!(
        output, payload,
        "raw_dict_each_call produced unexpected output"
    );

    let mut group = c.benchmark_group("decode/dict_handle");
    group.bench_with_input(
        BenchmarkId::new("prepared_handle", output_len),
        &output_len,
        |b, &len| {
            b.iter_batched(
                || (FrameDecoder::new(), vec![0u8; len]),
                |(mut decoder, mut output)| {
                    decoder
                        .decode_all_with_dict_handle(compressed.as_slice(), &mut output, &handle)
                        .expect("decode should succeed");
                    black_box(output);
                },
                BatchSize::SmallInput,
            );
        },
    );

    group.bench_with_input(
        BenchmarkId::new("raw_dict_each_call", output_len),
        &output_len,
        |b, &len| {
            b.iter_batched(
                || (FrameDecoder::new(), vec![0u8; len]),
                |(mut decoder, mut output)| {
                    decoder
                        .decode_all_with_dict_bytes(compressed.as_slice(), &mut output, dict_raw)
                        .expect("decode should succeed");
                    black_box(output);
                },
                BatchSize::SmallInput,
            );
        },
    );
    group.finish();
}

criterion_group!(benches, bench_decode_dict_handle);
criterion_main!(benches);