blazingly-json
Focused, safe, Tokio-free JSON for MCP, JSON-RPC, API, config, snapshot, and JSONL workloads.
blazingly-json is not an incomplete reimplementation of every
serde_json feature. Its supported surface was derived from real call sites
in mcport, Weavatrix Rust, RadioChron, Blazingly, and BlazingAPI, then tested
differentially against serde_json.
The general compatible parser is only modestly faster. The large gain comes from changing protocol architecture: borrow the envelope, avoid a mutable DOM, serialize responses directly, and use an exact schema-aware recognizer where the producer emits a canonical layout.
Current status: pre-1.0 and published. The crate passes differential and property tests, Rust 1.78, strict Clippy, rustdoc, packaging, Linux CI, and Windows CI. It is the engine every crate in the Blazingly framework encodes and decodes with, on every request that carries a JSON body.
Performance summary
Local Windows measurements on an Intel Core Ultra 7 255U. Paired harnesses alternate engine order and report medians. These are workload measurements, not universal parser claims.
Canonical typed MCP call
A generated compact tools/call recognizer borrows three strings, parses a
u64 and a boolean, verifies complete consumption, and falls back to the
strict order-independent parser on any mismatch.
| Path | Median range | Speedup over typed serde_json |
|---|---|---|
CanonicalBytesScanner over ASCII canonical bytes |
45.94-50.47 ns | 7.35-7.62x |
CanonicalScanner over prevalidated &str |
47.63-51.81 ns | 7.03-7.43x |
strict order-independent JsonCursor |
261.82-284.37 ns | 1.31-1.35x |
typed serde_json derive |
342.85-384.78 ns | 1.00x |
Both canonical paths allocate zero bytes. The byte scanner avoids a complete
UTF-8 pre-pass: it matches structural ASCII directly and validates captured
ASCII strings while scanning. Across three paired runs it is slightly faster
than the already-prevalidated &str path, so byte input no longer pays the
previous 10-26% conversion penalty.
This path intentionally accepts only its exact schema, field order, compact
separators, and plain strings. The fastest byte method
plain_ascii_string rejects non-ASCII fields. Whitespace, reordered fields,
escaped or non-ASCII strings, and another schema are not treated as errors:
they take the strict JsonCursor fallback.
MCP runtime architecture
Borrowed request routing plus direct response serialization compared with
mcport's original owned-Value + clone implementation:
| Request | Borrowed path | Original mcport | Speedup |
|---|---|---|---|
| ping | 344.93 ns | 1,425.04 ns | 4.13x |
| initialize | 1,014.87 ns | 6,683.85 ns | 6.59x |
| tools/list | 2,524.99 ns | 7,987.72 ns | 3.16x |
| tools/call | 2,103.43 ns | 7,298.68 ns | 3.47x |
Allocation measurements for request dispatch:
| Request | Borrowed path | Original mcport |
|---|---|---|
| ping | 0 allocations / 0 bytes | 8 allocations / 670 bytes |
| tools/call with owned arguments | 5 allocations / 668 bytes | 27 allocations / 2,702 bytes |
| canonical typed tools/call | 0 allocations / 0 bytes | 27 allocations / 2,702 bytes |
RawValue
blazingly_json::value::RawValue implements the common
serde_json::value::RawValue contract: borrowed and boxed deserialization,
verbatim serialization, from_string, from_string_unchecked, get,
into_string, to_raw_value, constants, cloning, and direct deserialization
from &RawValue.
Three paired local runs after warm-up produced these ranges:
| Workload | Speedup over serde_json::RawValue |
|---|---|
| small MCP borrowed parse | 1.22-1.59x |
| small MCP boxed parse | 1.21-1.59x |
small MCP from_string |
1.34-1.54x |
small MCP verbatim to_vec |
1.69-2.36x |
| 1,000,000-number borrowed parse, 6.57 MiB | 1.42-1.79x |
| 1,000,000-number boxed parse | 1.19-1.55x |
| compact protocol-record array borrowed parse | 1.62-2.07x |
| large preallocated writer output | approximately 1.00x |
Large output is memory-bandwidth-bound and is reported as parity, not as a
multiple-times win. For a complete raw document, RawValue::to_vec and
RawValue::write_to bypass the generic Serde marker path. Embedded raw values
remain compatible with ordinary to_vec, to_writer, and pretty output.
Allocation measurements:
| Operation | blazingly-json | serde_json |
|---|---|---|
| small borrowed parse | 0 | 1 allocation / 8 bytes |
| small boxed parse | 1 allocation / 154 bytes | 2 allocations / 162 bytes |
from_string with reusable capacity |
0 | 1 allocation / 8 bytes |
| 1,000,000-number borrowed parse | 0 | 0 |
same payload as owned Vec<u64> in serde_json |
- | 1 allocation + 18 reallocations / 8 MiB live |
The raw parser first tries strict compact numeric-array and flat protocol-record recognizers. They must consume a complete value; whitespace, escaping, nesting, or any shape mismatch takes the fully validating general fallback.
Compatible API and large payloads
The compatible owned/typed path does not reach a multiple-times speedup:
| Workload | blazingly-json | serde_json | Difference |
|---|---|---|---|
MCP mutable Value parse |
2.195 us | 2.313 us | +5.37% |
| MCP typed parse | 1.391 us | 1.423 us | +2.33% |
| MCP typed encode | 420.75 ns | 427.20 ns | +1.53% |
| Weavatrix-like graph parse | 77.72 us | 80.00 us | +2.92% |
| Weavatrix-like graph encode | 22.13 us | 24.70 us | +11.64% |
| Cargo artifact JSONL parse | 1.650 us | 1.850 us | +12.13% |
1,000,000 u64 values |
254.9 MiB/s | 249.8 MiB/s | +2.01% |
| 1,000,000 typed records, 80.98 MiB | 150.1 MiB/s | 147.6 MiB/s | +1.70% |
1,000,000 varied MCP Value parses |
69.7 MiB/s | 68.0 MiB/s | +2.48% |
Parsing one million owned records makes exactly 2,000,001 allocations and
retains 100.27 MiB with either engine: the owned strings and vectors dominate,
not the parser. Minimal stripped Windows executables are also effectively tied:
990,208 bytes for blazingly-json and 989,184 bytes for serde_json.
The conclusion is deliberate: replacing serde_json with another compatible
owned DOM cannot honestly promise 2-3x less memory or 2-3x more speed. The
canonical and borrowed APIs exist to remove that ownership work.
Usage
[]
= "0.1"
= { = "1", = ["derive"] }
Serde-compatible typed JSON
use ;
let request: =
from_str?;
let encoded = to_string?;
# Ok::
Zero-copy envelope
RawJson validates a nested value and borrows its exact input without building
a DOM. The payload is materialized only if a selected handler needs it.
use ;
use Deserialize;
let call: = from_str?;
let arguments = call.arguments.?;
# Ok::
Drop-in-style RawValue
The type is available both at blazingly_json::value::RawValue and at the
crate root.
use ;
use ;
let input: =
from_str?;
assert_eq!;
let output = to_string?;
assert_eq!;
let owned = to_raw_value?;
assert_eq!;
# Ok::
Order-independent protocol cursor
JsonCursor visits only routing fields, skips unknown values safely, and validates
the complete document.
use JsonCursor;
let mut method = None;
let mut cursor = from_str;
cursor.object?;
cursor.end?;
assert_eq!;
# Ok::
Schema-aware canonical path
Use this only as a recognizer with a mandatory fallback. A successful recognizer must consume the complete input.
use CanonicalScanner;
let input = r#"{"method":"search","limit":20}"#;
let mut scanner = new;
let recognized = ;
if let Some = recognized else
Supported surface
Value,Map<String, Value>,Number, andjson!;from_str,from_slice,from_value;to_string, pretty/string/vector/writer variants, andto_value;- Serde structs, maps, sequences, options, enums, newtypes, and bytes;
- string, boolean, signed, unsigned, float, null, array, and object values;
- access, mutation, indexing, JSON Pointer, and
take; - validated borrowed
RawJson; - drop-in-style borrowed/boxed
RawValueandto_raw_value; - routing-oriented
JsonCursor; - exact-layout
CanonicalScannerandCanonicalBytesScanner; - strict RFC 8259 parsing with recursion limits and source locations.
Deliberate 0.1 exclusions:
- arbitrary-precision numbers;
- JSON5, comments, trailing commas, NaN, and infinities;
preserve_order;- synchronous
Read-based incremental parsing; - private
serde_jsonimplementation details other than the establishedRawValueSerde token used for serializer interoperability.
Dependencies and safety
The runtime dependencies are serde, memchr, itoa, lexical-core, and
zmij. serde_json is a development-only oracle for differential tests and
benchmarks. Tokio, Hyper, and Axum are absent.
unsafe_code is denied crate-wide. The sole exception is raw_value.rs,
which contains three documented repr(transparent) DST conversions between
str and RawValue. No parser, scanner, serializer, or other module may use
unsafe code. The optional from_string_unchecked API is itself unsafe and
states the same validity invariant as the serde_json API it replaces.
Competitive position
serde_json: broad compatibility, maturity, ecosystem, and an excellent borrowedRawValue;sonic-rs: SIMD parsing and lazy field access;simd-json: SIMD tape plus borrowed and owned DOMs, with mutable-input and unsafe-code trade-offs;serde_json_borrow: borrowed DOM and reduced string allocation;jiter: iterator/schema-oriented parsing plus Serde support.
blazingly-json does not claim to beat every engine on every document. Its
target is portable, low-MSRV protocol performance, a tiny auditable unsafe
island for RawValue, and generated schema-aware MCP/API codecs with a strict
fallback.
See:
- benchmark methodology and full results;
- consumer-derived compatibility contract;
- consumer compile/test probes;
- competitor and MCP runtime analysis;
- mcport integration spike.
Verification
cargo fmt --all -- --check
cargo test
cargo clippy --all-targets --all-features -- -D warnings
cargo +1.78 check --lib
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps
cargo package
cargo bench --bench paired_comparison
cargo bench --bench large_payload
cargo bench --bench allocation_comparison
cargo bench --bench mcp_fast_path
cargo bench --bench mcp_allocations
cargo bench --bench mcport_end_to_end
cargo bench --bench raw_value_comparison
cargo bench --bench raw_value_allocations
License
MIT