Background
Part of the qjson fuzz harness (#63). #63 lands the shared fuzz/ crate
scaffolding, the nightly CI job, and fuzz_parse_eager (accept/reject
differential). This issue is the highest-payoff follow-up.
Phase 1 (scan + validation) already has differential coverage
(scanner_crosscheck.rs, json_test_suite.rs, rfc8259_compliance.rs).
Phase 2 — cursor walk, skip cache, lazy decode — has no property /
differential / fuzz coverage, and it is the most offset-arithmetic-heavy
code in the crate (find_value_span, scalar_byte_range, nth_object_entry,
the +1/+2/+3 index conventions, the cold/warm skip-cache paths).
Depends on #63 (needs the fuzz/ crate + nightly CI from that issue).
Goal
Put a correctness oracle on Phase 2 via value-equality differential testing
against serde_json. For every input that serde_json accepts, decode the
entire qjson document through the public cursor API and assert deep equality
with serde_json::Value.
This oracle is strictly stronger than v1's accept/reject: it catches
"accepted but decoded wrong" (off-by-one in a span, a mis-decoded escape, a
wrong number) that accept/reject can never see.
Scope (this issue = one PR)
Acceptance Criteria
Notes
- Lazy mode deliberately skips value-level validation, so accept/reject
differential does not apply here — only value comparison on
serde-accepted inputs.
- Duplicate-key value semantics must be aligned with serde (last-wins vs
first-wins) or normalized before comparison.
Affected files
fuzz/fuzz_targets/fuzz_parse_lazy.rs (new)
fuzz/ shared walker helper if factored out
Part of #63. Depends on #63.
Background
Part of the qjson fuzz harness (#63). #63 lands the shared
fuzz/cratescaffolding, the nightly CI job, and
fuzz_parse_eager(accept/rejectdifferential). This issue is the highest-payoff follow-up.
Phase 1 (scan + validation) already has differential coverage
(
scanner_crosscheck.rs,json_test_suite.rs,rfc8259_compliance.rs).Phase 2 — cursor walk, skip cache, lazy decode — has no property /
differential / fuzz coverage, and it is the most offset-arithmetic-heavy
code in the crate (
find_value_span,scalar_byte_range,nth_object_entry,the
+1/+2/+3index conventions, the cold/warm skip-cache paths).Depends on #63 (needs the
fuzz/crate + nightly CI from that issue).Goal
Put a correctness oracle on Phase 2 via value-equality differential testing
against
serde_json. For every input thatserde_jsonaccepts, decode theentire qjson document through the public cursor API and assert deep equality
with
serde_json::Value.This oracle is strictly stronger than v1's accept/reject: it catches
"accepted but decoded wrong" (off-by-one in a span, a mis-decoded escape, a
wrong number) that accept/reject can never see.
Scope (this issue = one PR)
fuzz_parse_lazyfuzz target.each leaf (string / number / bool / null) into a
serde_json::Valueusing the cursor API (
qjson_cursor_*/object_entry_at).Valueagainstserde_json::from_slicewith deep equality; numbers compared bit-exact as
f64.lookups per container so the warm (cached) path is covered, not just
the cold (populate) path.
Acceptance Criteria
cargo +nightly fuzz run fuzz_parse_lazybuilds and runs under-max_total_time=60.serde_json::Value(modulo documented duplicate-key / numbernormalization, mirroring test: fuzz harness (cargo-fuzz / libFuzzer) #63's allowlist).
re-lookups), not only the cold path.
Notes
differential does not apply here — only value comparison on
serde-accepted inputs.
first-wins) or normalized before comparison.
Affected files
fuzz/fuzz_targets/fuzz_parse_lazy.rs(new)fuzz/shared walker helper if factored outPart of #63. Depends on #63.