Tracks RON spec v0.4.0 and
ron-go v0.1.1. The pinned conformance corpus moves
from v0.1.0 to v0.4.0. This release is breaking in both the API and the output bytes.
Added
- JSON escapes in every string form. Bare strings,
'/"strings, repeated-delimiter
strings, comma-prefixed tokens and object keys all accept\" \\ \/ \b \f \n \r \t \uXXXX,
including surrogate pairs. An escape is a single scanner atom, soa\u0020bstays one token.
Type classification happens before decoding, sotr\u0075eis the string"true".
Unknown, truncated and non-hex escapes, unpaired surrogates and raw U+0000-U+001F in string
content are now rejected. The renderer escapes backslash and C0 controls before deciding
bare vs quoted, so"a\nb"renders as barea\nband a literal backslash is doubled. RonModeenum (Pretty,Compact,Canonical) replacing the$pretty/$canonicalflags.- Real canonical mode.
RonMode::Canonicalapplies the RFC 8785 and RFC 7493 (I-JSON) contract:
duplicate decoded member names, invalid Unicode, Unicode noncharacters and numbers whose
IEEE 754 conversion is non-finite are rejected, and numbers are re-serialized with the
ECMAScript algorithm. Ron::format()re-renders RON source in a given mode (RON -> RON), which is how canonical RON
and its hash are derived from RON rather than JSON input.setvocabulary:#set(deduplicated and sorted by each element's RFC 8785 canonical JSON
bytes) and#bits(uint32 indexes normalized to ascending, merged, inclusive ranges).#topo(TopoJSON topology) in the geo vocabulary. Validated but never expanded: shared arcs
stay indexed, negative indexes stay unreversed, quantized coordinates keep their encoding.RonObject::has()andUtf8::encodeRune().
Changed
- BREAKING:
toJson(),fromJson(),encode()andRonRenderer::__construct()take a
RonModeinstead ofbool $pretty, bool $canonical. - BREAKING:
toJson()now defaults to pretty output, matching the spec'sdefaultModeand
ron-go. PassRonMode::Compactfor the previous default. - BREAKING: pretty and compact output preserve source member order. Only
RonMode::Canonical
sorts keys. - BREAKING: canonical output no longer preserves number spelling (
1E2canonicalizes to
100,9007199254740993to9007199254740992). Pretty and compact still preserve it. - RFC 8785 canonicalization accepts a finite number that rounds during IEEE 754 conversion; only
a non-finite conversion is an error. The former "integer is not exactly representable"
rejection is gone, following the corpus. - N-quoted strings are delimiter-aware: a same-quote run shorter than the opening run and every
occurrence of the other quote byte are content. The(n - 2) % 3compatibility form is now
apostrophe-only; a double-quote run never takes it. - A comma directly after an object key starts the value token (
k ,foois the string",foo")
instead of being skipped as a separator. - Bare tokens end at Unicode whitespace, not only at ASCII delimiters.
#rxpayloads carry doubled backslashes on the wire now that every RON string decodes escapes:
JavaScript\dis written as RON\\d.
Performance
Measured best-of-N on one ~31 KB document with OPcache + JIT, against v0.4.1 on the same
machine. Pretty and compact got faster because they no longer sort object keys, and the string
renderer now answers "needs escaping?" and "needs quoting?" with one strcspn over a combined
mask instead of two scans plus an mb_check_encoding. The token scanner keeps its old 11-byte
delimiter mask for sources that contain no escape, no raw control and no Unicode-whitespace lead
byte, decided by a single pass in Scanner::setSource() — without it the 45-byte escape-aware
mask cost ~20% on token-heavy input, because strcspn rebuilds a 256-byte table per call.
| Conversion | v0.4.1 | v0.5.0 |
|---|---|---|
| JSON -> RON (compact) | ~26 MB/s | ~29 MB/s |
| JSON -> RON (pretty) | ~20 MB/s | ~26 MB/s |
| RON -> JSON (compact) | ~20 MB/s | ~19 MB/s |
| canonical hash | ~26 MB/s | ~13 MB/s |
The canonical hash is ~2x slower and stays that way by design: it used to be compact rendering
with sorted keys, and now validates the whole RFC 8785 / I-JSON contract and re-serializes every
number. Documents made of long quoted strings convert ~9% slower (~222 -> ~203 MB/s) because the
content scan is escape-aware; that path runs an order of magnitude above the token-bound one, so
it was left alone.
Removed
- BREAKING:
#lla(LngLatAlt) from the spatial vocabulary, removed upstream. Geographic
positions belong in#geo, whose positions accept an optional third altitude element.
Fixed
- Unicode noncharacters (U+FDD0-U+FDEF and every plane's U+xFFFE/U+xFFFF) are rejected in RFC 8785
canonical JSON, whether written directly or via\uXXXX.
Upgrading from 0.4.x
// before
Ron::toJson($ron); // compact, sorted keys
Ron::toJson($ron, pretty: true);
Ron::fromJson($json, pretty: false);
Ron::encode($value, pretty: false);
// after
use Mbolli\Ron\RonMode;
Ron::toJson($ron, RonMode::Compact); // compact, source order
Ron::toJson($ron); // pretty is now the default
Ron::fromJson($json, RonMode::Compact);
Ron::encode($value, RonMode::Compact);Sorted keys now mean RonMode::Canonical, which also validates the RFC 8785 / I-JSON contract
and re-serializes numbers — it is not a drop-in for the old canonical: true flag on pretty or
compact output. Ron::canonicalRon(), Ron::canonicalHash() and Ron::canonicalJson() are
unchanged in signature.
Full changelog: CHANGELOG.md ·
v0.4.1...v0.5.0