nextjson 0.1.0

A dependency-free, no_std JSON and CBOR library with a schema-driven, visitor-free design.
Documentation
use std::hint::black_box;
use std::time::{Duration, Instant};

use nextjson::{NsonDeserialize, NsonSerialize};

#[derive(Clone, Debug, PartialEq, NsonSerialize, NsonDeserialize)]
struct Record {
    id: u64,
    active: bool,
    score: f64,
    name: String,
    tags: Vec<String>,
    samples: Vec<i64>,
}

fn fixture() -> Vec<Record> {
    (0..128)
        .map(|index| Record {
            id: index,
            active: index % 3 != 0,
            score: index as f64 * 1.25 - 17.5,
            name: format!("record-{index:04}"),
            tags: vec![
                "json".into(),
                "zero-copy".into(),
                format!("group-{}", index % 8),
            ],
            samples: (0..16).map(|sample| index as i64 * 31 - sample).collect(),
        })
        .collect()
}

fn measure(mut operation: impl FnMut(), duration: Duration) -> (u64, f64) {
    let start = Instant::now();
    let mut iterations = 0_u64;
    while start.elapsed() < duration {
        operation();
        iterations += 1;
    }
    let elapsed = start.elapsed().as_secs_f64();
    (iterations, iterations as f64 / elapsed)
}

fn run(name: &str, duration: Duration, operation: impl FnMut()) {
    let (iterations, operations_per_second) = measure(operation, duration);
    println!("{name},{iterations},{operations_per_second:.2}");
}

fn main() {
    let duration_ms = std::env::var("NEXTJSON_BENCH_MS")
        .ok()
        .and_then(|value| value.parse::<u64>().ok())
        .unwrap_or(2_000);
    let duration = Duration::from_millis(duration_ms.max(100));
    let records = fixture();
    let json = nextjson::nextencode(&records).expect("fixture JSON encoding");
    let cbor = nextjson::cross_format::json_to_cbor(&json).expect("fixture JSON to CBOR");
    let json_from_cbor = nextjson::cross_format::cbor_to_json(&cbor).expect("fixture CBOR to JSON");
    let decoded: Vec<Record> =
        nextjson::nextdecode(&json_from_cbor).expect("fixture round-trip decoding");
    assert_eq!(decoded, records);

    for _ in 0..100 {
        black_box(nextjson::nextencode(black_box(&records)).unwrap());
        black_box(nextjson::nextdecode::<Vec<Record>>(black_box(&json)).unwrap());
        black_box(nextjson::cross_format::json_to_cbor(black_box(&json)).unwrap());
        black_box(nextjson::cross_format::cbor_to_json(black_box(&cbor)).unwrap());
    }

    println!("case,iterations,operations_per_second");
    run("nextjson_native_nextencode", duration, || {
        black_box(nextjson::nextencode(black_box(&records)).unwrap());
    });
    run("nextjson_native_nextdecode", duration, || {
        black_box(nextjson::nextdecode::<Vec<Record>>(black_box(&json)).unwrap());
    });
    run("nextjson_json_to_cbor", duration, || {
        black_box(nextjson::cross_format::json_to_cbor(black_box(&json)).unwrap());
    });
    run("nextjson_cbor_to_json", duration, || {
        black_box(nextjson::cross_format::cbor_to_json(black_box(&cbor)).unwrap());
    });
}