cobs_codec_rs 1.3.0

Consistent Overhead Byte Stuffing (COBS) and COBS/R codec: no_std, zero-dependency, for zero-free framing of serial and packet byte streams.
Documentation
//! Throughput micro-benchmarks for the COBS and COBS/R codecs.
//!
//! Measures how fast the allocating `*_to_vec` entry points process a fixed
//! 1 KiB (1024-byte) payload. The payload is deterministic pseudo-random data
//! that is mostly non-zero with roughly one byte in eight forced to `0x00`, so
//! the encoders repeatedly close and open blocks instead of coasting down a
//! single 254-byte run. Throughput is reported against the *payload* length
//! (1024 bytes) for every case, so the numbers are directly comparable as
//! "payload bytes per second".

// The crate-wide `missing_docs = "warn"` lint also lints this bench target, but
// the `benches` function is generated by `criterion_group!` and cannot carry a
// doc comment. Allow it here so the bench compiles warning-clean.
#![allow(missing_docs)]

use cobs_codec_rs::{cobs, cobsr};
use criterion::{Criterion, Throughput, black_box, criterion_group, criterion_main};

/// Payload size under test: 1 KiB.
const PAYLOAD_LEN: usize = 1024;

/// Builds the fixed 1 KiB benchmark payload.
///
/// Uses a seeded xorshift32 generator so the bytes are identical on every run
/// (stable, reproducible numbers) while still looking random to the codec.
/// About 1 byte in 8 is set to `0x00`; the rest are guaranteed non-zero, which
/// exercises the block-splitting path COBS takes on each zero.
fn representative_payload() -> Vec<u8> {
    let mut state: u32 = 0x1234_5678;
    let mut next = || {
        state ^= state << 13;
        state ^= state >> 17;
        state ^= state << 5;
        state
    };
    (0..PAYLOAD_LEN)
        .map(|_| {
            let bytes = next().to_le_bytes();
            if bytes[0] % 8 == 0 {
                0x00
            } else {
                // Decorrelate the value from the byte used for the zero
                // decision, and make sure a "non-zero" slot is truly non-zero.
                match bytes[1] {
                    0 => 0xA5,
                    b => b,
                }
            }
        })
        .collect()
}

fn throughput(c: &mut Criterion) {
    let payload = representative_payload();
    // Pre-encode once so the decode benchmark times only the decode.
    let encoded = cobs::encode_to_vec(&payload);

    let mut group = c.benchmark_group("cobs_1kib");
    // Report throughput against the 1024-byte payload for all three cases.
    group.throughput(Throughput::Bytes(1024));

    group.bench_function("cobs_encode", |b| {
        b.iter(|| cobs::encode_to_vec(black_box(&payload)));
    });

    group.bench_function("cobs_decode", |b| {
        b.iter(|| cobs::decode_to_vec(black_box(&encoded)).unwrap());
    });

    group.bench_function("cobsr_encode", |b| {
        b.iter(|| cobsr::encode_to_vec(black_box(&payload)));
    });

    group.finish();
}

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