Skip to content

zero-swift: JSON objects keep keys and key order as JS does - #14

Merged
tmm merged 5 commits into
mainfrom
swift-json-keys
Oct 22, 2026
Merged

tmm merged 5 commits into
mainfrom
swift-json-keys

Conversation

@tmm

@tmm tmm commented Oct 22, 2026

Copy link
Copy Markdown
Member

Fixes a bug found by the bug sweep, with option (a) chosen by the user.

Bug: JSONValue.object was a [String: JSONValue]. Swift strings compare by Unicode canonical equivalence, so keys like "\u00e9" and "e\u0301" merged and a value was lost. That dropped data from pokes (row values, primary keys, update merges, client IDs) and gave custom-query and AST hashes that differ from JS.

Fix: a new public JSONObject value type (case object(JSONObject)):

  • keys compare and hash by UTF-16 code unit;
  • keys keep JS order: integer-like keys ascending, then insertion order; the parser keeps the last value of a duplicate key in the first key's place, as JSON.parse does;
  • the serializer writes keys in that order, matching JSON.stringify; query hashes still sort keys, as JS does;
  • dictionary literals, subscript(key), ordered iteration, mapValues, merge and .dictionary keep it ergonomic. JSONValue stays 17 bytes.

Breaking (minor changeset, with a migration note): objectValue, ZeroObjectReader/Writer.object and AST.Bound.row are JSONObject; .object(dictVariable) needs JSONObject(dict). Mutator and start APIs still take dictionaries. zbugs needed a one-line change.

Benchmarks (release, Linux): no regression; materialize 100k rows 7.61 → 6.20 ms, put 1000 changed rows 9.41 → 8.60 ms, memory for 100k rows 127.7 → 126.2 MB.

Checked: 173 Swift tests including all conformance vectors and 6 sweep wire tests generated from JS, swift format lint --strict, ClientE2ETests against a real zero-cache, and zbugs-swift builds its tests. TestHost can't build on Linux; macOS CI covers it.

@tmm
tmm force-pushed the swift-json-keys branch from 842dcbe to 1f9bd98 Compare October 22, 2026 04:05
ampagent and others added 5 commits October 22, 2026 04:09
`JSONValue.object` holds a `JSONObject`: an ordered object whose keys
compare by UTF-16 code unit, in JS property order (integer-like keys
first, ascending, then insertion order). Canonically equivalent keys no
longer merge in pokes, and JSON is written as `JSON.stringify` writes it.

Amp-Thread-ID: https://ampcode.com/threads/T-c513a037-a257-45e0-b9a9-15c15edfe859
Co-authored-by: tmm <tmm@tmm.dev>
…weep generator

macOS gives test threads a 512 KB stack, which a 4,000-deep value overflows.

Amp-Thread-ID: https://ampcode.com/threads/T-c513a037-a257-45e0-b9a9-15c15edfe859
Co-authored-by: tmm <tmm@tmm.dev>
The main actor didn't help on macOS CI: the debug build still overflowed.

Amp-Thread-ID: https://ampcode.com/threads/T-c513a037-a257-45e0-b9a9-15c15edfe859
Co-authored-by: tmm <tmm@tmm.dev>
A vector nests 1,000 deep, which overflows macOS's 512 KB test threads in
a debug build.

Amp-Thread-ID: https://ampcode.com/threads/T-c513a037-a257-45e0-b9a9-15c15edfe859
Co-authored-by: tmm <tmm@tmm.dev>
@tmm
tmm force-pushed the swift-json-keys branch from 1f9bd98 to c494ff9 Compare October 22, 2026 04:09
@tmm
tmm merged commit 76312e4 into main Oct 22, 2026
37 of 39 checks passed
@tmm
tmm deleted the swift-json-keys branch October 22, 2026 06:24
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.

2 participants