|
1 | 1 | # JSON.parseBinary |
| 2 | +The JSON.parseBinary proposal introduces a new static method to the global JSON object. It allows developers to parse JSON directly from binary data (Uint8Array, ArrayBuffer) without first converting it into a JavaScript string. Unlike the traditional JSON.parse, this method returns a **result object** instead of throwing an exception, significantly improving performance and memory efficiency in high-load environments like HTTP servers. |
| 3 | +## The Core Problems |
2 | 4 |
|
| 5 | +The current workflow for parsing network data in JavaScript is: |
| 6 | +``` |
| 7 | +Network Bytes → TextDecoder.decode() (String) → JSON.parse(String) → Object |
| 8 | +``` |
3 | 9 |
|
| 10 | +This pipeline suffers from three major "hidden" costs: |
| 11 | + |
| 12 | + - The Intermediate String Penalty: Converting binary data to a string creates a massive temporary copy in memory. If a single multi-byte character (like an emoji) is present, V8 may upgrade the entire string to UTF-16, doubling its memory footprint. This increases Garbage Collection (GC) pressure and can lead to "stop-the-world" pauses. |
| 13 | + |
| 14 | + - The SyntaxError Overhead: JSON.parse is "throw-heavy." When it encounters invalid JSON, it generates a SyntaxError with a full stack trace. Benchmarks show that creating and throwing an Error is roughly 500x more expensive than returning a plain object. In network scenarios, invalid payloads are common and shouldn't require the overhead of a debugging stack trace. |
| 15 | + |
| 16 | + - Memory Pollution: Current methods often leave the original binary buffer and the intermediate string in memory longer than necessary, causing the heap to swell and slowing down the entire system. |
| 17 | + |
| 18 | +## Proposed Solution: `JSON.parseBinary` |
| 19 | + |
| 20 | +The proposal suggests a synchronous, single-pass parser with the following TypeScript signature: |
| 21 | +TypeScript |
| 22 | +```typescript |
| 23 | +JSON.parseBinary(input: ArrayBufferLike | Uint8Array): |
| 24 | + { ok: true; value: any } | |
| 25 | + { ok: false; message: string }; |
| 26 | +``` |
| 27 | + |
| 28 | +How the Pipeline Changes: |
| 29 | +## Key Advantages |
| 30 | +|Feature|JSON.parse|JSON.parseBinary| |
| 31 | +|---|---|---| |
| 32 | +|Memory|Allocates 2x-3x payload size for strings|Zero intermediate string allocation| |
| 33 | +|Error Handling|Uses try-catch (slow, blocks inlining)|Returns a result object (fast, ergonomic)| |
| 34 | +|Performance|Significant GC overhead and stack trace costs|Optimized for "untrusted" network input| |
| 35 | +|Simplicity|Requires TextDecoder and global pollution|Clean, self-contained API| |
| 36 | + |
| 37 | + - Early Abort: If the first byte is invalid, JSON.parseBinary returns immediately without wasting any time decoding the rest of the buffer. |
| 38 | + |
| 39 | + - Selective Encoding: It only creates strings for the actual keys and values it extracts, keeping the rest of the data in its compact binary form during the walk. |
| 40 | + |
| 41 | + - Developer Experience: Eliminates nested try-catch blocks, allowing for cleaner code when handling potentially malformed API responses. |
| 42 | + |
| 43 | +## Synergy with ArrayBuffer.prototype.detach() |
| 44 | + |
| 45 | +This proposal is designed to work alongside the detach() proposal. Together, they allow a server to: |
| 46 | + |
| 47 | + - Receive a binary chunk. |
| 48 | + |
| 49 | + - Parse it directly into an object. |
| 50 | + |
| 51 | + - Manually release the binary memory immediately after parsing, ensuring the memory footprint never stays higher than necessary. |
0 commit comments