Skip to content

fix(data/json): decode-on-demand string accessor#354

Merged
octalide merged 2 commits into
devfrom
fix/340
Jul 3, 2026
Merged

fix(data/json): decode-on-demand string accessor#354
octalide merged 2 commits into
devfrom
fix/340

Conversation

@octalide

@octalide octalide commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Closes #340.

Problem

std.data.json stored two incompatible meanings in one field: parse yields the
raw on-wire bytes between the quotes (escapes intact, zero-copy), while emit
treats str_val as logical bytes and re-escapes. A parse -> emit pipeline
double-escaped every wire escape.

Decision

Per the issue (2026-07-03, delegated): decode-on-demand accessor. Zero-copy
raw parse stays; a decoding accessor gives correct string semantics at the
consumption point without making every parse pay decode cost.

Change

  • value_string_decode(v, buf, len) -> Result[usize, str]: resolves the raw wire
    escapes of a string value into logical bytes in a caller buffer — the inverse
    of the emit escaper. Handles \" \\ \/ \b \f \n \r \t and \uXXXX (a high+low
    surrogate pair combines into one astral code point, encoded to UTF-8 via the
    validated std.text.utf8 encoder). Errors on malformed escapes (unknown
    escape, \u without four hex digits, ill-formed surrogate pair). Uses the same
    counting-BufWriter size-then-fill protocol as emit.
  • Documents the raw-vs-decoded contract on str_val, value_string, the new
    accessor, and the file header.

Emit semantics are unchanged — this PR adds the decode path only.

Tests

Escape matrix (short escapes, \uXXXX BMP, surrogate-pair astral, verbatim
UTF-8 pass-through), malformed-escape errors, buffer-sizing protocol, and the
reframed round-trip decode(parse(emit(make_string(L)))) == L.

octalide added 2 commits July 3, 2026 01:19
Resolve the parse/emit string-representation split (mach-std#340) with a
decode-on-demand accessor: the parser keeps storing raw wire bytes (zero-copy),
and value_string_decode unescapes them into logical bytes at the point of
consumption -- the inverse of the emit escaper. Handles " \ \/ \b\f\n\r\t and
\uXXXX including surrogate-pair astral code points decoded to UTF-8, erroring on
malformed escapes. Documents the raw-vs-decoded contract on the storage field
and both accessors.
Escape matrix (short escapes, \uXXXX BMP incl. uppercase hex, surrogate-pair
astral -> UTF-8, verbatim UTF-8 pass-through), malformed-escape rejection
(bad escape, truncated/non-hex \u, unpaired/lone/bad surrogate, trailing
backslash), the emit size-then-fill buffer protocol, and the reframed
round-trip decode(parse(emit(make_string(L)))) == L.
@octalide octalide marked this pull request as ready for review July 3, 2026 05:23
@octalide octalide merged commit 451c92e into dev Jul 3, 2026
3 checks passed
@octalide octalide deleted the fix/340 branch July 3, 2026 05:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

data/json: parse and emit disagree on string representation (raw wire bytes vs logical bytes)

1 participant