@amritk/nish-x86_64-linux 0.17.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL.md +43 -25
- package/bin/nish +0 -0
- package/package.json +2 -2
- package/runtime/nish.d.ts +55 -5
- package/runtime/nish.h +231 -20
- package/runtime/nish.mjs +16 -0
- package/runtime/runtime-aes.c +490 -0
- package/runtime/runtime-cross.c +366 -0
- package/runtime/runtime-host.c +7 -6
- package/runtime/runtime-net.c +93 -34
- package/runtime/runtime-os.c +213 -15
- package/runtime/runtime-parallel.c +590 -107
- package/runtime/runtime-simd.c +203 -0
- package/runtime/runtime-wasm.c +16 -3
- package/runtime/runtime.c +89 -23
- package/runtime/shim.mjs +71 -1
- package/scripts/build.sh +225 -20
- package/std/README.md +86 -55
- package/std/bytes.ts +163 -0
- package/std/crypto/LICENSE-libsecp256k1 +19 -0
- package/std/crypto/aes.ts +238 -76
- package/std/crypto/chacha20poly1305.ts +43 -9
- package/std/crypto/ed25519.ts +840 -0
- package/std/crypto/hkdf.ts +304 -12
- package/std/crypto/hmac.ts +245 -53
- package/std/crypto/p256.ts +1376 -42
- package/std/crypto/x25519.ts +55 -37
- package/std/crypto/x509-random.ts +71 -0
- package/std/crypto/x509.ts +9 -9
- package/std/json-tokenizer.ts +1296 -0
- package/std/json-typed.ts +1014 -0
- package/std/json-value.ts +1163 -0
- package/std/json.ts +1412 -70
- package/std/net/buffer-pool.ts +151 -0
- package/std/net/hpack.ts +416 -112
- package/std/net/http-fields.ts +93 -0
- package/std/net/http1-server.ts +417 -55
- package/std/net/http1.ts +31 -6
- package/std/net/http2-tls.ts +112 -24
- package/std/net/http2.ts +333 -75
- package/std/net/http3-server.ts +231 -45
- package/std/net/http3.ts +187 -28
- package/std/net/prefork.ts +412 -0
- package/std/net/quic-conn-params.ts +86 -16
- package/std/net/quic-listener.ts +576 -219
- package/std/net/quic-packet.ts +443 -58
- package/std/net/quic-recovery.ts +361 -30
- package/std/net/quic-stream.ts +434 -28
- package/std/net/quic.ts +922 -206
- package/std/net/router.ts +676 -0
- package/std/net/tls/codec.ts +426 -27
- package/std/net/tls/record-server.ts +478 -132
- package/std/net/tls/record.ts +133 -42
- package/std/net/tls/schedule.ts +464 -55
- package/std/net/tls/ticket.ts +610 -0
- package/std/net/tls-tcp.ts +204 -57
- package/std/net/tls.ts +1132 -305
- package/std/net/url.ts +571 -0
- package/std/random.ts +94 -0
- package/std/text.ts +77 -0
- package/std/threads.ts +438 -14
|
@@ -0,0 +1,1163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nish/json-value` — any JSON document as a tree a program can read, build
|
|
3
|
+
* and write back, with every number kept as the text it was written in.
|
|
4
|
+
*
|
|
5
|
+
* `nish/json-typed` reads JSON into a class the program declares, which is the
|
|
6
|
+
* fast and safe way when the shape is known. This module is for when it is
|
|
7
|
+
* not: a configuration file, a proxy that rewrites one field and passes the
|
|
8
|
+
* rest through, a tool that prints whatever it was given.
|
|
9
|
+
*
|
|
10
|
+
* import { JsonDocument, JSON_VALUE_NUMBER } from "nish/json-value"
|
|
11
|
+
* import { JsonLimits } from "nish/json-tokenizer"
|
|
12
|
+
*
|
|
13
|
+
* const doc = new JsonDocument(new JsonLimits())
|
|
14
|
+
* const root = doc.parseText('{"id":18446744073709551615,"tags":["a","b"]}')
|
|
15
|
+
* if (root < 0) {
|
|
16
|
+
* // a JSON_TOK_ERR_* code, or JSON_VALUE_ERR_DUPLICATE; the byte is doc.errorAt
|
|
17
|
+
* }
|
|
18
|
+
* const id = doc.get(root, "id")
|
|
19
|
+
* doc.kind(id) === JSON_VALUE_NUMBER // true
|
|
20
|
+
* doc.isU64(id) // true; doc.isI64(id) is false
|
|
21
|
+
* doc.numberText(id) // "18446744073709551615", as written
|
|
22
|
+
* doc.stringify(root) // the same text back, byte for byte
|
|
23
|
+
*
|
|
24
|
+
* **A value is a handle.** `JsonValue` is an `i32` naming one value of the
|
|
25
|
+
* document that made it, and every reader and builder is a method of that
|
|
26
|
+
* document. A handle stays valid until the document parses again or is
|
|
27
|
+
* `clear`ed, which forgets every value at once and keeps the memory: the
|
|
28
|
+
* nodes, the member slots and the bytes of every string and number live in a
|
|
29
|
+
* handful of arrays the document owns and reuses. A server keeps one document
|
|
30
|
+
* and parses each request into it, and once the arrays have grown to the
|
|
31
|
+
* largest request seen, parsing allocates nothing.
|
|
32
|
+
*
|
|
33
|
+
* A handle carries the generation of the document's contents it was made in,
|
|
34
|
+
* which moves on at every `parse` and `clear`, so a handle kept from an
|
|
35
|
+
* earlier request names nothing in the next one, even when the next one has
|
|
36
|
+
* the same shape: it is `JSON_VALUE_NONE` to `kind`, and every reader answers
|
|
37
|
+
* its empty value for it rather than another document's. The generation has
|
|
38
|
+
* 511 values and moves by one at a `clear`, one at a parse and two at a parse
|
|
39
|
+
* that fails, so the check misses only a handle kept across exactly a
|
|
40
|
+
* multiple of 511 of those steps; a program must not keep handles across
|
|
41
|
+
* documents, and this catches the mistake rather than licensing it. A
|
|
42
|
+
* negative handle, or one past the last value, is `JSON_VALUE_NONE` too.
|
|
43
|
+
*
|
|
44
|
+
* **The document's size is bounded.** Each value costs about 46 bytes of the
|
|
45
|
+
* arrays the document keeps for its life, on top of the decoded text of its
|
|
46
|
+
* strings, keys and numbers, which `JsonLimits.maxTotalBytes` bounds through
|
|
47
|
+
* the input (`docs/security/json.md`, JSON-10). `maxValues` caps the first
|
|
48
|
+
* part by capping how many values one document holds:
|
|
49
|
+
* `JSON_VALUE_DEFAULT_MAX_VALUES` unless the program sets it, at most
|
|
50
|
+
* `JSON_VALUE_MAX_VALUES`. A parse that would make one more is
|
|
51
|
+
* `JSON_VALUE_ERR_VALUES` at the byte of the value that does not fit, and a
|
|
52
|
+
* builder answers the same code.
|
|
53
|
+
*
|
|
54
|
+
* **Numbers are lossless.** A number keeps its source text, and is read as
|
|
55
|
+
* whatever the caller asks for when it asks: `i64` and `u64` when the text is
|
|
56
|
+
* an integer that fits (`isI64`, `isU64`), the nearest `f64` (`f64`, correctly
|
|
57
|
+
* rounded, the tokenizer's `numberF64`), or the text itself (`numberText`).
|
|
58
|
+
* Writing it back copies the text. So `18446744073709551616`, wider than any
|
|
59
|
+
* integer type, and `0.1000000000000000055511151231257827`, more digits than an
|
|
60
|
+
* `f64` holds, come back out exactly as they went in, where JavaScript's
|
|
61
|
+
* `JSON.parse` turns both into the nearest double.
|
|
62
|
+
*
|
|
63
|
+
* **Objects keep insertion order**, the order the members were written in or
|
|
64
|
+
* added. `get` finds a member by its key with a scan, so it is for a handful of
|
|
65
|
+
* lookups per object; a program that looks up many keys of a large object
|
|
66
|
+
* walks it once with `keyAt` and `at`.
|
|
67
|
+
*
|
|
68
|
+
* **Duplicate keys are refused by default.** `{"a":1,"a":2}` is
|
|
69
|
+
* `JSON_VALUE_ERR_DUPLICATE`, with `errorAt` at the first key that repeats an
|
|
70
|
+
* earlier one, because two readers that pick different members of the same
|
|
71
|
+
* text are a check bypassed (`docs/security/json.md`, JSON-3), and refusing
|
|
72
|
+
* is what `nish/json-typed` does too. A program that has to accept what
|
|
73
|
+
* `JSON.parse` accepts sets `duplicates` to `JSON_VALUE_DUPLICATE_LAST`, and
|
|
74
|
+
* gets `JSON.parse`'s answer: one member, at the place of the first, holding
|
|
75
|
+
* the value of the last. The check sorts each object's keys, so an object of
|
|
76
|
+
* n members costs n log n comparisons whatever the keys are.
|
|
77
|
+
*
|
|
78
|
+
* **Every limit and every parse error is the tokenizer's.** `parse` runs a
|
|
79
|
+
* `JsonTokenizer` with the `JsonLimits` the document was made with, and
|
|
80
|
+
* answers its `JSON_TOK_ERR_*` code and offset unchanged. The depth limit is
|
|
81
|
+
* capped at `JSON_WRITER_MAX_DEPTH`, 64, so that every document a parse
|
|
82
|
+
* accepts can be written back; a deeper one is the tokenizer's
|
|
83
|
+
* `JSON_TOK_ERR_DEPTH`. Nothing an input holds panics, and the parse keeps its
|
|
84
|
+
* own stack of open containers rather than recursing.
|
|
85
|
+
*
|
|
86
|
+
* **Writing goes through `JsonWriter`.** `write(v, w)` writes a value into a
|
|
87
|
+
* writer the caller set up, so its buffer, its reuse and its sticky
|
|
88
|
+
* `JSON_WRITE_*` codes are the writer's own, and `stringify` is a convenience
|
|
89
|
+
* that sizes a buffer and answers a string. Strings come out as `JsonWriter`
|
|
90
|
+
* escapes them, which is `JSON.stringify`'s rule, numbers as their text, and
|
|
91
|
+
* members in order. Only a built tree can be deeper than
|
|
92
|
+
* `JSON_WRITER_MAX_DEPTH`, and writing one is the writer's
|
|
93
|
+
* `JSON_WRITE_TOO_DEEP`.
|
|
94
|
+
*
|
|
95
|
+
* **Building a tree** is a `new*` call per value, then `push` onto an array or
|
|
96
|
+
* `set` on an object, each answering `JSON_VALUE_OK` or a negative code. A
|
|
97
|
+
* value lives in at most one container, so a tree built through `push` and
|
|
98
|
+
* `set` can never contain itself: adding a value that is already in a
|
|
99
|
+
* container, or a container to itself or to anything inside it, is
|
|
100
|
+
* `JSON_VALUE_ERR_ATTACHED`, and writing always ends.
|
|
101
|
+
* Strings handed to a builder are taken as their bytes, as `JsonWriter` takes
|
|
102
|
+
* them, and a string read back is made from the stored bytes in one
|
|
103
|
+
* allocation (`String.fromBytes`). Under Node a string is UTF-16, and
|
|
104
|
+
* `parseText` and the builders take one byte per code unit, so non-ASCII text
|
|
105
|
+
* is stored differently there than natively, where a string is its UTF-8
|
|
106
|
+
* bytes; `parse` over a `u8[]` reads the same bytes both ways.
|
|
107
|
+
*
|
|
108
|
+
* **Public is not the whole API.** Nish has no private members, so the
|
|
109
|
+
* methods marked "Internal" in their comments, and the fields, are reachable
|
|
110
|
+
* by a program; the guarantees above hold only through the methods that are
|
|
111
|
+
* not marked so. `append` with indices of the program's choosing, for
|
|
112
|
+
* example, skips the check that keeps a tree finite.
|
|
113
|
+
*
|
|
114
|
+
* Every name here is `json`-prefixed, because a `std/` module is compiled into
|
|
115
|
+
* the program that imports it and the symbol namespace is flat
|
|
116
|
+
* (`std/README.md`). Every offset, count and code is an `i32`, so the module is
|
|
117
|
+
* the same under `--number-mode f64`, and it runs under the Node prelude, where
|
|
118
|
+
* `tests/run.js` holds every method to the native answers.
|
|
119
|
+
*/
|
|
120
|
+
import { JSON_WRITE_FULL, JSON_WRITE_OK, JSON_WRITER_MAX_DEPTH, JsonWriter } from "nish/json"
|
|
121
|
+
import {
|
|
122
|
+
JSON_TOK_ARRAY_BEGIN,
|
|
123
|
+
JSON_TOK_ARRAY_END,
|
|
124
|
+
JSON_TOK_END,
|
|
125
|
+
JSON_TOK_KEY,
|
|
126
|
+
JSON_TOK_NULL,
|
|
127
|
+
JSON_TOK_NUMBER,
|
|
128
|
+
JSON_TOK_OBJECT_BEGIN,
|
|
129
|
+
JSON_TOK_OBJECT_END,
|
|
130
|
+
JSON_TOK_STRING,
|
|
131
|
+
JSON_TOK_TRUE,
|
|
132
|
+
JsonLimits,
|
|
133
|
+
JsonTokenizer,
|
|
134
|
+
} from "nish/json-tokenizer"
|
|
135
|
+
|
|
136
|
+
/** A value in a `JsonDocument`: an index into it, valid until the document parses again or is cleared. */
|
|
137
|
+
export type JsonValue = i32
|
|
138
|
+
|
|
139
|
+
/** What `kind` answers for a handle that names no value. */
|
|
140
|
+
export const JSON_VALUE_NONE: i32 = 0
|
|
141
|
+
export const JSON_VALUE_NULL: i32 = 1
|
|
142
|
+
export const JSON_VALUE_BOOLEAN: i32 = 2
|
|
143
|
+
/** A number, kept as its text: `numberText`, `isI64`, `i64`, `isU64`, `u64` and `f64` read it. */
|
|
144
|
+
export const JSON_VALUE_NUMBER: i32 = 3
|
|
145
|
+
export const JSON_VALUE_STRING: i32 = 4
|
|
146
|
+
export const JSON_VALUE_ARRAY: i32 = 5
|
|
147
|
+
export const JSON_VALUE_OBJECT: i32 = 6
|
|
148
|
+
|
|
149
|
+
/** What a builder answers when it did what it was asked. */
|
|
150
|
+
export const JSON_VALUE_OK: i32 = 0
|
|
151
|
+
/** A key an object already has, refused while `duplicates` is `JSON_VALUE_DUPLICATE_REFUSE`. */
|
|
152
|
+
export const JSON_VALUE_ERR_DUPLICATE: i32 = -30
|
|
153
|
+
/** A handle that names no value, or a value of the wrong kind: `push` onto an object, `set` on an array. */
|
|
154
|
+
export const JSON_VALUE_ERR_HANDLE: i32 = -31
|
|
155
|
+
/** A value already in a container, or a container added to itself or to a value inside it. */
|
|
156
|
+
export const JSON_VALUE_ERR_ATTACHED: i32 = -32
|
|
157
|
+
/** Text that is not one JSON number, for `newNumber`, or an `f64` that is NaN or an infinity, for `newF64`. */
|
|
158
|
+
export const JSON_VALUE_ERR_NUMBER: i32 = -33
|
|
159
|
+
/** One value more than `JsonDocument.maxValues`, by a parse (with `errorAt` at its first byte) or a builder. */
|
|
160
|
+
export const JSON_VALUE_ERR_VALUES: i32 = -34
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The most values one document holds by default: a parse that would make
|
|
164
|
+
* more is `JSON_VALUE_ERR_VALUES`. Each value costs the document about 46
|
|
165
|
+
* bytes of arrays it keeps (`docs/security/json.md`, JSON-10), so this caps
|
|
166
|
+
* them near 12 MB; a program raises `maxValues` up to
|
|
167
|
+
* `JSON_VALUE_MAX_VALUES` for larger documents.
|
|
168
|
+
*/
|
|
169
|
+
export const JSON_VALUE_DEFAULT_MAX_VALUES: i32 = 262144
|
|
170
|
+
/** The most `maxValues` can be: a handle keeps a value's index in its low 22 bits. */
|
|
171
|
+
export const JSON_VALUE_MAX_VALUES: i32 = 4194304
|
|
172
|
+
|
|
173
|
+
/** The default: a repeated key is `JSON_VALUE_ERR_DUPLICATE`. */
|
|
174
|
+
export const JSON_VALUE_DUPLICATE_REFUSE: i32 = 0
|
|
175
|
+
/** `JSON.parse`'s rule: one member, at the first key's place, with the last key's value. */
|
|
176
|
+
export const JSON_VALUE_DUPLICATE_LAST: i32 = 1
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* A handle is `generation << JSON_VALUE_INDEX_BITS | index`. The generation
|
|
180
|
+
* runs from 1 to `JSON_VALUE_LAST_GENERATION` and moves on by one at every
|
|
181
|
+
* `clear` and every parse, two at a parse that fails, so a handle kept from an
|
|
182
|
+
* earlier document names nothing in this one, unless exactly a multiple of
|
|
183
|
+
* 511 of those steps have passed.
|
|
184
|
+
*/
|
|
185
|
+
const JSON_VALUE_INDEX_BITS: i32 = 22
|
|
186
|
+
const JSON_VALUE_INDEX_MASK: i32 = 4194303
|
|
187
|
+
const JSON_VALUE_LAST_GENERATION: i32 = 511
|
|
188
|
+
/** At most this many members are checked for duplicates pair by pair; a larger object is sorted. */
|
|
189
|
+
const JSON_VALUE_PAIRWISE: i32 = 8
|
|
190
|
+
/** How many member slots a built container gets when it first grows. */
|
|
191
|
+
const JSON_VALUE_FIRST_SLOTS: i32 = 4
|
|
192
|
+
/** The largest an `i32` count may be, which every limit of the number reader is set to. */
|
|
193
|
+
const JSON_VALUE_I32_MAX: i32 = 2147483647
|
|
194
|
+
const JSON_VALUE_DIGIT_0: i32 = 48
|
|
195
|
+
/** The words of a value with nothing to point at, and a `true` boolean's first word; spelled `i32` for `--number-mode f64`. */
|
|
196
|
+
const JSON_VALUE_NO_WORD: i32 = 0
|
|
197
|
+
const JSON_VALUE_TRUE_WORD: i32 = 1
|
|
198
|
+
const JSON_VALUE_MINUS: i32 = 45
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* A JSON tree, and the memory every value of it lives in. Make one, then
|
|
202
|
+
* `parse` into it or build with the `new*` methods; the module header has the
|
|
203
|
+
* rules. Its fields and its "Internal" methods are reachable, as every
|
|
204
|
+
* member in Nish is; use the others.
|
|
205
|
+
*/
|
|
206
|
+
export class JsonDocument {
|
|
207
|
+
/** Per value: its `JSON_VALUE_*` kind. */
|
|
208
|
+
kinds: u8[]
|
|
209
|
+
/** Per value: a string's or a number's first byte in `bytes`, a container's first slot, a boolean's `0` or `1`. */
|
|
210
|
+
first: i32[]
|
|
211
|
+
/** Per value: a string's or a number's byte count, or a container's member count. */
|
|
212
|
+
count: i32[]
|
|
213
|
+
/** Per value: how many slots a container has from `first`, its members and room to grow. */
|
|
214
|
+
room: i32[]
|
|
215
|
+
/** Per value: the container it is in, or `-1`. */
|
|
216
|
+
parent: i32[]
|
|
217
|
+
/** Per member slot: the member's value, and an object member's key as a window of `bytes`. */
|
|
218
|
+
slotValue: i32[]
|
|
219
|
+
slotKeyAt: i32[]
|
|
220
|
+
slotKeyLength: i32[]
|
|
221
|
+
/** The bytes of every string, key and number, decoded, one after the other. */
|
|
222
|
+
bytes: u8[]
|
|
223
|
+
/**
|
|
224
|
+
* The parse's own stacks: the open containers, where each one's members
|
|
225
|
+
* start in the pending arrays and the key it is the value of, if any; and
|
|
226
|
+
* the pending arrays, which hold every member read and not yet
|
|
227
|
+
* placed, with its key and the key's source offset. The sort arrays are the
|
|
228
|
+
* duplicate check's. All of them keep their length from one parse to the
|
|
229
|
+
* next, which is what lets a parse allocate nothing.
|
|
230
|
+
*/
|
|
231
|
+
openValue: i32[]
|
|
232
|
+
openBase: i32[]
|
|
233
|
+
openKeyAt: i32[]
|
|
234
|
+
openKeyLength: i32[]
|
|
235
|
+
openKeySource: i32[]
|
|
236
|
+
pendingValue: i32[]
|
|
237
|
+
pendingKeyAt: i32[]
|
|
238
|
+
pendingKeyLength: i32[]
|
|
239
|
+
pendingSource: i32[]
|
|
240
|
+
sortA: i32[]
|
|
241
|
+
sortB: i32[]
|
|
242
|
+
/** `parseText`'s copy of its string. */
|
|
243
|
+
textBytes: u8[]
|
|
244
|
+
tok: JsonTokenizer
|
|
245
|
+
/** A tokenizer over one stored number at a time, for `isI64`, `i64` and `f64`, limited only by `i32`. */
|
|
246
|
+
numbers: JsonTokenizer
|
|
247
|
+
|
|
248
|
+
/** `JSON_VALUE_DUPLICATE_REFUSE` or `JSON_VALUE_DUPLICATE_LAST`; read by `parse`. */
|
|
249
|
+
duplicates: i32 = 0
|
|
250
|
+
/** The last `parse`'s code: `0`, a `JSON_TOK_ERR_*` code or `JSON_VALUE_ERR_DUPLICATE`. */
|
|
251
|
+
failed: i32 = 0
|
|
252
|
+
/** The byte offset in the parsed array the error was found at, and `-1` until one. */
|
|
253
|
+
errorAt: i32 = -1
|
|
254
|
+
/** How many values are in use: the handles are `0 .. top - 1`. */
|
|
255
|
+
top: i32 = 0
|
|
256
|
+
/** How many member slots are in use, and how many bytes. */
|
|
257
|
+
slotTop: i32 = 0
|
|
258
|
+
byteTop: i32 = 0
|
|
259
|
+
/** How many members are pending. */
|
|
260
|
+
pendingTop: i32 = 0
|
|
261
|
+
/** The most values the document holds; more is `JSON_VALUE_ERR_VALUES`. At most `JSON_VALUE_MAX_VALUES`. */
|
|
262
|
+
maxValues: i32 = 0
|
|
263
|
+
/** The generation every handle of the current document carries; see `JSON_VALUE_INDEX_BITS`. */
|
|
264
|
+
generation: i32 = 1
|
|
265
|
+
/** Whether the last `unsigned` read a number that fits a `u64`. */
|
|
266
|
+
unsignedFits: boolean = false
|
|
267
|
+
|
|
268
|
+
constructor(limits: JsonLimits) {
|
|
269
|
+
this.maxValues = JSON_VALUE_DEFAULT_MAX_VALUES
|
|
270
|
+
this.kinds = []
|
|
271
|
+
this.first = []
|
|
272
|
+
this.count = []
|
|
273
|
+
this.room = []
|
|
274
|
+
this.parent = []
|
|
275
|
+
this.slotValue = []
|
|
276
|
+
this.slotKeyAt = []
|
|
277
|
+
this.slotKeyLength = []
|
|
278
|
+
this.bytes = []
|
|
279
|
+
this.openValue = []
|
|
280
|
+
this.openBase = []
|
|
281
|
+
this.openKeyAt = []
|
|
282
|
+
this.openKeyLength = []
|
|
283
|
+
this.openKeySource = []
|
|
284
|
+
this.pendingValue = []
|
|
285
|
+
this.pendingKeyAt = []
|
|
286
|
+
this.pendingKeyLength = []
|
|
287
|
+
this.pendingSource = []
|
|
288
|
+
this.sortA = []
|
|
289
|
+
this.sortB = []
|
|
290
|
+
this.textBytes = []
|
|
291
|
+
// The writer nests 64 deep, so a parsed document is held to that too:
|
|
292
|
+
// every document a parse accepts can be written back.
|
|
293
|
+
const capped = new JsonLimits()
|
|
294
|
+
capped.maxDepth = limits.maxDepth < JSON_WRITER_MAX_DEPTH ? limits.maxDepth : JSON_WRITER_MAX_DEPTH
|
|
295
|
+
capped.maxStringBytes = limits.maxStringBytes
|
|
296
|
+
capped.maxNumberBytes = limits.maxNumberBytes
|
|
297
|
+
capped.maxTotalBytes = limits.maxTotalBytes
|
|
298
|
+
this.tok = new JsonTokenizer(this.bytes, 0, 0, capped)
|
|
299
|
+
const wide = new JsonLimits()
|
|
300
|
+
wide.maxDepth = 0
|
|
301
|
+
wide.maxNumberBytes = JSON_VALUE_I32_MAX
|
|
302
|
+
wide.maxTotalBytes = JSON_VALUE_I32_MAX
|
|
303
|
+
this.numbers = new JsonTokenizer(this.bytes, 0, 0, wide)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Forgets every value, keeping the memory for the next parse or build. Every handle is invalid afterwards. */
|
|
307
|
+
clear(): void {
|
|
308
|
+
this.generation = this.generation >= JSON_VALUE_LAST_GENERATION ? 1 : this.generation + 1
|
|
309
|
+
this.top = 0
|
|
310
|
+
this.slotTop = 0
|
|
311
|
+
this.byteTop = 0
|
|
312
|
+
this.pendingTop = 0
|
|
313
|
+
this.failed = 0
|
|
314
|
+
this.errorAt = -1
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ---- Parsing ----------------------------------------------------------------
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Clears the document and parses `src[off, off + len)` into it. Answers the
|
|
321
|
+
* root value, or a negative code — the tokenizer's `JSON_TOK_ERR_*` or
|
|
322
|
+
* `JSON_VALUE_ERR_DUPLICATE` — which is also in `failed`, with the byte
|
|
323
|
+
* offset in `errorAt`. Nothing is kept from a parse that fails.
|
|
324
|
+
*/
|
|
325
|
+
parse(src: u8[], off: i32, len: i32): JsonValue {
|
|
326
|
+
this.clear()
|
|
327
|
+
const tok: JsonTokenizer = this.tok
|
|
328
|
+
tok.reset(src, off, len)
|
|
329
|
+
let depth: i32 = 0
|
|
330
|
+
let root: i32 = -1
|
|
331
|
+
let keyAt: i32 = 0
|
|
332
|
+
let keyLength: i32 = 0
|
|
333
|
+
let keySource: i32 = 0
|
|
334
|
+
let k: i32 = tok.next()
|
|
335
|
+
while (k > 0) {
|
|
336
|
+
let v: i32 = -1
|
|
337
|
+
if (k === JSON_TOK_KEY) {
|
|
338
|
+
keySource = tok.tokStart
|
|
339
|
+
keyAt = this.byteTop
|
|
340
|
+
keyLength = this.storeString(tok)
|
|
341
|
+
} else if (k === JSON_TOK_OBJECT_BEGIN || k === JSON_TOK_ARRAY_BEGIN) {
|
|
342
|
+
const c: i32 = this.newNode(
|
|
343
|
+
k === JSON_TOK_OBJECT_BEGIN ? JSON_VALUE_OBJECT : JSON_VALUE_ARRAY,
|
|
344
|
+
JSON_VALUE_NO_WORD,
|
|
345
|
+
JSON_VALUE_NO_WORD
|
|
346
|
+
)
|
|
347
|
+
if (c < 0) {
|
|
348
|
+
return this.parseFailed(c, tok.tokStart)
|
|
349
|
+
}
|
|
350
|
+
jsonValuePut(this.openValue, depth, c)
|
|
351
|
+
jsonValuePut(this.openBase, depth, this.pendingTop)
|
|
352
|
+
jsonValuePut(this.openKeyAt, depth, keyAt)
|
|
353
|
+
jsonValuePut(this.openKeyLength, depth, keyLength)
|
|
354
|
+
jsonValuePut(this.openKeySource, depth, keySource)
|
|
355
|
+
depth += 1
|
|
356
|
+
} else if (k === JSON_TOK_OBJECT_END || k === JSON_TOK_ARRAY_END) {
|
|
357
|
+
depth -= 1
|
|
358
|
+
const c: i32 = jsonValueGet(this.openValue, depth)
|
|
359
|
+
const code: i32 = this.close(c, jsonValueGet(this.openBase, depth))
|
|
360
|
+
if (code < 0) {
|
|
361
|
+
return this.parseFailed(code, this.errorAt)
|
|
362
|
+
}
|
|
363
|
+
// The container's own key, read before its members' keys overwrote the three locals.
|
|
364
|
+
keyAt = jsonValueGet(this.openKeyAt, depth)
|
|
365
|
+
keyLength = jsonValueGet(this.openKeyLength, depth)
|
|
366
|
+
keySource = jsonValueGet(this.openKeySource, depth)
|
|
367
|
+
v = c
|
|
368
|
+
} else if (k === JSON_TOK_STRING) {
|
|
369
|
+
v = this.newNode(JSON_VALUE_STRING, this.byteTop, this.storeString(tok))
|
|
370
|
+
} else if (k === JSON_TOK_NUMBER) {
|
|
371
|
+
v = this.newNode(JSON_VALUE_NUMBER, this.byteTop, this.storeSource(tok))
|
|
372
|
+
} else if (k === JSON_TOK_NULL) {
|
|
373
|
+
v = this.newNode(JSON_VALUE_NULL, JSON_VALUE_NO_WORD, JSON_VALUE_NO_WORD)
|
|
374
|
+
} else {
|
|
375
|
+
v = this.newNode(
|
|
376
|
+
JSON_VALUE_BOOLEAN,
|
|
377
|
+
k === JSON_TOK_TRUE ? JSON_VALUE_TRUE_WORD : JSON_VALUE_NO_WORD,
|
|
378
|
+
JSON_VALUE_NO_WORD
|
|
379
|
+
)
|
|
380
|
+
}
|
|
381
|
+
// `-1` is a key or an opener, which place no value yet; below it, the value was refused.
|
|
382
|
+
if (v < -1) {
|
|
383
|
+
return this.parseFailed(v, tok.tokStart)
|
|
384
|
+
}
|
|
385
|
+
if (v >= 0) {
|
|
386
|
+
if (depth === 0) {
|
|
387
|
+
root = v
|
|
388
|
+
} else {
|
|
389
|
+
const p: i32 = this.pendingTop
|
|
390
|
+
jsonValuePut(this.pendingValue, p, v)
|
|
391
|
+
jsonValuePut(this.pendingKeyAt, p, keyAt)
|
|
392
|
+
jsonValuePut(this.pendingKeyLength, p, keyLength)
|
|
393
|
+
jsonValuePut(this.pendingSource, p, keySource)
|
|
394
|
+
this.pendingTop = p + 1
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
k = tok.next()
|
|
398
|
+
}
|
|
399
|
+
if (k < 0 || root < 0) {
|
|
400
|
+
return this.parseFailed(k < 0 ? k : JSON_VALUE_ERR_HANDLE, tok.errorAt)
|
|
401
|
+
}
|
|
402
|
+
return this.handle(root)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/** `parse` over the bytes of `s`, copied into an array the document reuses; `errorAt` is a byte offset in `s`. */
|
|
406
|
+
parseText(s: string): JsonValue {
|
|
407
|
+
const n: i32 = toI32(s.length)
|
|
408
|
+
const buf: u8[] = jsonValueRoom(this.textBytes, n)
|
|
409
|
+
this.textBytes = buf
|
|
410
|
+
for (let i: i32 = 0; i < n && i < toI32(buf.length); i += 1) {
|
|
411
|
+
buf[i] = toU8(s.charCodeAt(i))
|
|
412
|
+
}
|
|
413
|
+
return this.parse(buf, JSON_VALUE_NO_WORD, n)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** Internal: Records a failed parse and forgets what it built. */
|
|
417
|
+
parseFailed(code: i32, at: i32): i32 {
|
|
418
|
+
this.clear()
|
|
419
|
+
this.failed = code
|
|
420
|
+
this.errorAt = at
|
|
421
|
+
return code
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Internal: Places the members pending from `base` into container `c` as one run of
|
|
426
|
+
* slots, checking an object's keys for duplicates first. Answers `0`, or
|
|
427
|
+
* `JSON_VALUE_ERR_DUPLICATE` with `errorAt` set.
|
|
428
|
+
*/
|
|
429
|
+
close(c: i32, base: i32): i32 {
|
|
430
|
+
let n: i32 = this.pendingTop - base
|
|
431
|
+
const object: boolean = this.kindAt(c) === JSON_VALUE_OBJECT
|
|
432
|
+
if (object && n > 1) {
|
|
433
|
+
const code: i32 = this.duplicateKeys(base, n)
|
|
434
|
+
if (code < 0) {
|
|
435
|
+
return code
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const start: i32 = this.slotTop
|
|
439
|
+
let kept: i32 = 0
|
|
440
|
+
for (let p: i32 = base; p < base + n; p += 1) {
|
|
441
|
+
const v: i32 = jsonValueGet(this.pendingValue, p)
|
|
442
|
+
if (v < 0) {
|
|
443
|
+
continue
|
|
444
|
+
}
|
|
445
|
+
const s: i32 = start + kept
|
|
446
|
+
jsonValuePut(this.slotValue, s, v)
|
|
447
|
+
jsonValuePut(this.slotKeyAt, s, jsonValueGet(this.pendingKeyAt, p))
|
|
448
|
+
jsonValuePut(this.slotKeyLength, s, jsonValueGet(this.pendingKeyLength, p))
|
|
449
|
+
jsonValuePut(this.parent, v, c)
|
|
450
|
+
kept += 1
|
|
451
|
+
}
|
|
452
|
+
n = kept
|
|
453
|
+
this.slotTop = start + n
|
|
454
|
+
jsonValuePut(this.first, c, start)
|
|
455
|
+
jsonValuePut(this.count, c, n)
|
|
456
|
+
jsonValuePut(this.room, c, n)
|
|
457
|
+
this.pendingTop = base
|
|
458
|
+
return 0
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Internal: The duplicate check over the `n` members pending from `base`. Under
|
|
463
|
+
* `JSON_VALUE_DUPLICATE_LAST`, a repeated key's later members are marked
|
|
464
|
+
* gone (their pending value set to `-1`) and the first one takes the last
|
|
465
|
+
* one's value. Small objects compare every pair; larger ones are sorted by
|
|
466
|
+
* key with ties in member order, so a key's repeats are next to each other
|
|
467
|
+
* and the first of them is its first member.
|
|
468
|
+
*/
|
|
469
|
+
duplicateKeys(base: i32, n: i32): i32 {
|
|
470
|
+
let firstRepeat: i32 = -1
|
|
471
|
+
if (n <= JSON_VALUE_PAIRWISE) {
|
|
472
|
+
for (let i: i32 = base + 1; i < base + n; i += 1) {
|
|
473
|
+
for (let j: i32 = base; j < i; j += 1) {
|
|
474
|
+
if (jsonValueGet(this.pendingValue, j) >= 0 && this.samePendingKey(i, j)) {
|
|
475
|
+
firstRepeat = this.repeat(j, i, firstRepeat)
|
|
476
|
+
break
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
const order: i32[] = this.sortPending(base, n)
|
|
482
|
+
let i: i32 = 0
|
|
483
|
+
while (i < n) {
|
|
484
|
+
const head: i32 = jsonValueGet(order, i)
|
|
485
|
+
let j: i32 = i + 1
|
|
486
|
+
while (j < n && this.samePendingKey(head, jsonValueGet(order, j))) {
|
|
487
|
+
firstRepeat = this.repeat(head, jsonValueGet(order, j), firstRepeat)
|
|
488
|
+
j += 1
|
|
489
|
+
}
|
|
490
|
+
i = j
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (firstRepeat >= 0 && this.duplicates !== JSON_VALUE_DUPLICATE_LAST) {
|
|
494
|
+
this.errorAt = firstRepeat
|
|
495
|
+
return JSON_VALUE_ERR_DUPLICATE
|
|
496
|
+
}
|
|
497
|
+
return 0
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Internal: Pending member `later` repeats the key of the earlier `head`: under
|
|
502
|
+
* `JSON_VALUE_DUPLICATE_LAST` `head` takes its value and it is marked gone.
|
|
503
|
+
* Answers the smallest source offset of a repeating key seen so far.
|
|
504
|
+
*/
|
|
505
|
+
repeat(head: i32, later: i32, firstRepeat: i32): i32 {
|
|
506
|
+
const source: i32 = jsonValueGet(this.pendingSource, later)
|
|
507
|
+
if (this.duplicates === JSON_VALUE_DUPLICATE_LAST) {
|
|
508
|
+
jsonValuePut(this.pendingValue, head, jsonValueGet(this.pendingValue, later))
|
|
509
|
+
jsonValuePut(this.pendingValue, later, -1)
|
|
510
|
+
}
|
|
511
|
+
return firstRepeat < 0 || source < firstRepeat ? source : firstRepeat
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/** Internal: Whether pending members `a` and `b` have the same key bytes. */
|
|
515
|
+
samePendingKey(a: i32, b: i32): boolean {
|
|
516
|
+
return this.comparePending(a, b) === 0
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Internal: The pending indices `base .. base + n - 1` sorted by key, ties in index
|
|
521
|
+
* order: a bottom-up merge sort between the two sort arrays, which answers
|
|
522
|
+
* whichever of them holds the result.
|
|
523
|
+
*/
|
|
524
|
+
sortPending(base: i32, n: i32): i32[] {
|
|
525
|
+
let from: i32[] = jsonValueRoomI32(this.sortA, n)
|
|
526
|
+
let to: i32[] = jsonValueRoomI32(this.sortB, n)
|
|
527
|
+
this.sortA = from
|
|
528
|
+
this.sortB = to
|
|
529
|
+
for (let i: i32 = 0; i < n && i < toI32(from.length); i += 1) {
|
|
530
|
+
from[i] = base + i
|
|
531
|
+
}
|
|
532
|
+
let width: i32 = 1
|
|
533
|
+
while (width < n) {
|
|
534
|
+
let lo: i32 = 0
|
|
535
|
+
while (lo < n) {
|
|
536
|
+
const mid: i32 = lo + width < n ? lo + width : n
|
|
537
|
+
const hi: i32 = mid + width < n ? mid + width : n
|
|
538
|
+
let a: i32 = lo
|
|
539
|
+
let b: i32 = mid
|
|
540
|
+
let out: i32 = lo
|
|
541
|
+
while (out < hi) {
|
|
542
|
+
const takeA: boolean =
|
|
543
|
+
b >= hi || (a < mid && this.comparePending(jsonValueGet(from, a), jsonValueGet(from, b)) <= 0)
|
|
544
|
+
if (takeA) {
|
|
545
|
+
jsonValuePut(to, out, jsonValueGet(from, a))
|
|
546
|
+
a += 1
|
|
547
|
+
} else {
|
|
548
|
+
jsonValuePut(to, out, jsonValueGet(from, b))
|
|
549
|
+
b += 1
|
|
550
|
+
}
|
|
551
|
+
out += 1
|
|
552
|
+
}
|
|
553
|
+
lo = hi
|
|
554
|
+
}
|
|
555
|
+
const swap: i32[] = from
|
|
556
|
+
from = to
|
|
557
|
+
to = swap
|
|
558
|
+
width = width * 2
|
|
559
|
+
}
|
|
560
|
+
return from
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/** Internal: Pending members `a` and `b` ordered by key bytes. */
|
|
564
|
+
comparePending(a: i32, b: i32): i32 {
|
|
565
|
+
return jsonValueCompareBytes(
|
|
566
|
+
this.bytes,
|
|
567
|
+
jsonValueGet(this.pendingKeyAt, a),
|
|
568
|
+
jsonValueGet(this.pendingKeyLength, a),
|
|
569
|
+
jsonValueGet(this.pendingKeyAt, b),
|
|
570
|
+
jsonValueGet(this.pendingKeyLength, b)
|
|
571
|
+
)
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/** Internal: The tokenizer's current string or key, decoded onto the end of `bytes`; answers its length. */
|
|
575
|
+
storeString(tok: JsonTokenizer): i32 {
|
|
576
|
+
const n: i32 = tok.stringLength()
|
|
577
|
+
const at: i32 = this.byteTop
|
|
578
|
+
const buf: u8[] = jsonValueRoom(this.bytes, at + n)
|
|
579
|
+
this.bytes = buf
|
|
580
|
+
tok.stringInto(buf, at)
|
|
581
|
+
this.byteTop = at + n
|
|
582
|
+
return n
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/** Internal: The tokenizer's current token as written, a number's text, onto the end of `bytes`; answers its length. */
|
|
586
|
+
storeSource(tok: JsonTokenizer): i32 {
|
|
587
|
+
const src: u8[] = tok.src
|
|
588
|
+
const start: i32 = tok.tokStart
|
|
589
|
+
const n: i32 = tok.tokEnd - start
|
|
590
|
+
const at: i32 = this.byteTop
|
|
591
|
+
const buf: u8[] = jsonValueRoom(this.bytes, at + n)
|
|
592
|
+
this.bytes = buf
|
|
593
|
+
for (let i: i32 = 0; i < n && start + i < toI32(src.length) && at + i < toI32(buf.length); i += 1) {
|
|
594
|
+
if (start + i >= 0 && at + i >= 0) {
|
|
595
|
+
buf[at + i] = src[start + i]
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
this.byteTop = at + n
|
|
599
|
+
return n
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Internal: a new value of `kind` with its two words, in no container, and
|
|
604
|
+
* its index; `JSON_VALUE_ERR_VALUES` when the document already holds
|
|
605
|
+
* `maxValues`.
|
|
606
|
+
*/
|
|
607
|
+
newNode(kind: i32, first: i32, count: i32): i32 {
|
|
608
|
+
const v: i32 = this.top
|
|
609
|
+
const cap: i32 = this.maxValues < JSON_VALUE_MAX_VALUES ? this.maxValues : JSON_VALUE_MAX_VALUES
|
|
610
|
+
if (v >= cap) {
|
|
611
|
+
return JSON_VALUE_ERR_VALUES
|
|
612
|
+
}
|
|
613
|
+
if (v < toI32(this.kinds.length)) {
|
|
614
|
+
this.kinds[v] = toU8(kind)
|
|
615
|
+
} else {
|
|
616
|
+
this.kinds.push(toU8(kind))
|
|
617
|
+
}
|
|
618
|
+
jsonValuePut(this.first, v, first)
|
|
619
|
+
jsonValuePut(this.count, v, count)
|
|
620
|
+
jsonValuePut(this.room, v, 0)
|
|
621
|
+
jsonValuePut(this.parent, v, -1)
|
|
622
|
+
this.top = v + 1
|
|
623
|
+
return v
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// ---- Handles ----------------------------------------------------------------
|
|
627
|
+
|
|
628
|
+
/** Internal: the index handle `v` names in this document, or `-1` when it names nothing here. */
|
|
629
|
+
index(v: JsonValue): i32 {
|
|
630
|
+
if (v < 0 || v >> JSON_VALUE_INDEX_BITS !== this.generation) {
|
|
631
|
+
return -1
|
|
632
|
+
}
|
|
633
|
+
const x: i32 = v & JSON_VALUE_INDEX_MASK
|
|
634
|
+
return x < this.top ? x : -1
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/** Internal: the handle of index `x` in this document's generation; a negative `x` is answered as it is. */
|
|
638
|
+
handle(x: i32): JsonValue {
|
|
639
|
+
return x < 0 ? x : (this.generation << JSON_VALUE_INDEX_BITS) | x
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// ---- Reading ----------------------------------------------------------------
|
|
643
|
+
|
|
644
|
+
/** `v`'s `JSON_VALUE_*` kind, or `JSON_VALUE_NONE` when `v` names no value of this document's current contents. */
|
|
645
|
+
kind(v: JsonValue): i32 {
|
|
646
|
+
return this.kindAt(this.index(v))
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/** Internal: the kind of index `x`, or `JSON_VALUE_NONE`. */
|
|
650
|
+
kindAt(x: i32): i32 {
|
|
651
|
+
const kinds: u8[] = this.kinds
|
|
652
|
+
if (x < 0 || x >= this.top || x >= toI32(kinds.length)) {
|
|
653
|
+
return JSON_VALUE_NONE
|
|
654
|
+
}
|
|
655
|
+
return toI32(kinds[x])
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/** A boolean's value; `false` for any other value. */
|
|
659
|
+
boolean(v: JsonValue): boolean {
|
|
660
|
+
const x: i32 = this.index(v)
|
|
661
|
+
return this.kindAt(x) === JSON_VALUE_BOOLEAN && jsonValueGet(this.first, x) === JSON_VALUE_TRUE_WORD
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/** How many elements an array has, or members an object has; `-1` for any other value. */
|
|
665
|
+
length(v: JsonValue): i32 {
|
|
666
|
+
return this.lengthAt(this.index(v))
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/** Internal: `length` of index `x`. */
|
|
670
|
+
lengthAt(x: i32): i32 {
|
|
671
|
+
const k: i32 = this.kindAt(x)
|
|
672
|
+
return k === JSON_VALUE_ARRAY || k === JSON_VALUE_OBJECT ? jsonValueGet(this.count, x) : -1
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/** Internal: the slot of member or element `i` of the container at index `x`, or `-1`. */
|
|
676
|
+
slotAt(x: i32, i: i32): i32 {
|
|
677
|
+
if (i < 0 || i >= this.lengthAt(x)) {
|
|
678
|
+
return -1
|
|
679
|
+
}
|
|
680
|
+
return jsonValueGet(this.first, x) + i
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/** Element `i` of an array, or the value of member `i` of an object, in order; `-1` when there is none. */
|
|
684
|
+
at(v: JsonValue, i: i32): JsonValue {
|
|
685
|
+
const s: i32 = this.slotAt(this.index(v), i)
|
|
686
|
+
return s < 0 ? -1 : this.handle(jsonValueGet(this.slotValue, s))
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/** The key of member `i` of an object, decoded; `""` when `v` is not an object or has no member `i`. */
|
|
690
|
+
keyAt(v: JsonValue, i: i32): string {
|
|
691
|
+
const x: i32 = this.index(v)
|
|
692
|
+
if (this.kindAt(x) !== JSON_VALUE_OBJECT) {
|
|
693
|
+
return ""
|
|
694
|
+
}
|
|
695
|
+
const s: i32 = this.slotAt(x, i)
|
|
696
|
+
if (s < 0) {
|
|
697
|
+
return ""
|
|
698
|
+
}
|
|
699
|
+
return this.textOf(jsonValueGet(this.slotKeyAt, s), jsonValueGet(this.slotKeyLength, s))
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/** The value of the member of object `v` whose key is `key`'s bytes; `-1` when there is none. */
|
|
703
|
+
get(v: JsonValue, key: string): JsonValue {
|
|
704
|
+
const x: i32 = this.index(v)
|
|
705
|
+
if (this.kindAt(x) !== JSON_VALUE_OBJECT) {
|
|
706
|
+
return -1
|
|
707
|
+
}
|
|
708
|
+
const s: i32 = this.findKey(x, key)
|
|
709
|
+
return s < 0 ? -1 : this.handle(jsonValueGet(this.slotValue, s))
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/** Internal: the slot of the member of the object at index `x` whose key is `key`'s bytes, or `-1`. */
|
|
713
|
+
findKey(x: i32, key: string): i32 {
|
|
714
|
+
const start: i32 = jsonValueGet(this.first, x)
|
|
715
|
+
const n: i32 = jsonValueGet(this.count, x)
|
|
716
|
+
for (let s: i32 = start; s < start + n; s += 1) {
|
|
717
|
+
if (this.bytesEqual(jsonValueGet(this.slotKeyAt, s), jsonValueGet(this.slotKeyLength, s), key)) {
|
|
718
|
+
return s
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return -1
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/** A string's decoded bytes as a string; `""` for any other value. */
|
|
725
|
+
string(v: JsonValue): string {
|
|
726
|
+
const x: i32 = this.index(v)
|
|
727
|
+
if (this.kindAt(x) !== JSON_VALUE_STRING) {
|
|
728
|
+
return ""
|
|
729
|
+
}
|
|
730
|
+
return this.textOf(jsonValueGet(this.first, x), jsonValueGet(this.count, x))
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/** Whether `v` is a string whose decoded bytes are `s`'s. Allocates nothing. */
|
|
734
|
+
stringEquals(v: JsonValue, s: string): boolean {
|
|
735
|
+
const x: i32 = this.index(v)
|
|
736
|
+
return (
|
|
737
|
+
this.kindAt(x) === JSON_VALUE_STRING &&
|
|
738
|
+
this.bytesEqual(jsonValueGet(this.first, x), jsonValueGet(this.count, x), s)
|
|
739
|
+
)
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/** A number's text, exactly as it was written or built; `""` for any other value. */
|
|
743
|
+
numberText(v: JsonValue): string {
|
|
744
|
+
const x: i32 = this.index(v)
|
|
745
|
+
if (this.kindAt(x) !== JSON_VALUE_NUMBER) {
|
|
746
|
+
return ""
|
|
747
|
+
}
|
|
748
|
+
return this.textOf(jsonValueGet(this.first, x), jsonValueGet(this.count, x))
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/** Whether `v` is a number written without a fraction or an exponent whose value fits an `i64`. */
|
|
752
|
+
isI64(v: JsonValue): boolean {
|
|
753
|
+
return this.readNumber(this.index(v)) && this.numbers.isInteger()
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/** A number's value as an `i64` when `isI64(v)` holds, and `0` otherwise. */
|
|
757
|
+
i64(v: JsonValue): i64 {
|
|
758
|
+
// `numberI64` is already `0` for a number that is not an `i64`.
|
|
759
|
+
return this.readNumber(this.index(v)) ? this.numbers.numberI64() : toI64(0)
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/** Whether `v` is a number written without a sign, a fraction or an exponent whose value fits a `u64`. */
|
|
763
|
+
isU64(v: JsonValue): boolean {
|
|
764
|
+
this.unsigned(this.index(v))
|
|
765
|
+
return this.unsignedFits
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/** A number's value as a `u64` when `isU64(v)` holds, and `0` otherwise. */
|
|
769
|
+
u64(v: JsonValue): u64 {
|
|
770
|
+
return this.unsigned(this.index(v))
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Internal: the digits of the number at index `x` as a `u64`, setting
|
|
775
|
+
* `unsignedFits` to whether it is a `u64` at all: no sign, no fraction or
|
|
776
|
+
* exponent, and at most 2^64 - 1. Every `u64` comes from `toU64`, which is
|
|
777
|
+
* a BigInt under Node, and nothing here wraps, so it means the same there.
|
|
778
|
+
*/
|
|
779
|
+
unsigned(x: i32): u64 {
|
|
780
|
+
this.unsignedFits = false
|
|
781
|
+
if (!this.readNumber(x) || !this.numbers.integral) {
|
|
782
|
+
return toU64(0)
|
|
783
|
+
}
|
|
784
|
+
const bytes: u8[] = this.bytes
|
|
785
|
+
const start: i32 = jsonValueGet(this.first, x)
|
|
786
|
+
const end: i32 = start + jsonValueGet(this.count, x)
|
|
787
|
+
if (start < 0 || start >= toI32(bytes.length) || toI32(bytes[start]) === JSON_VALUE_MINUS) {
|
|
788
|
+
return toU64(0)
|
|
789
|
+
}
|
|
790
|
+
const ten: u64 = toU64(10)
|
|
791
|
+
// 2^64 - 1 has no literal: 2^16 - 1 widened twice.
|
|
792
|
+
const quarter: u64 = toU64(65535)
|
|
793
|
+
const half: u64 = (quarter << toU64(16)) | quarter
|
|
794
|
+
const max: u64 = (half << toU64(32)) | half
|
|
795
|
+
let acc: u64 = toU64(0)
|
|
796
|
+
for (let i: i32 = start; i < end && i < toI32(bytes.length); i += 1) {
|
|
797
|
+
const digit: u64 = toU64(toI32(bytes[i]) - JSON_VALUE_DIGIT_0)
|
|
798
|
+
if (acc > (max - digit) / ten) {
|
|
799
|
+
return toU64(0)
|
|
800
|
+
}
|
|
801
|
+
acc = acc * ten + digit
|
|
802
|
+
}
|
|
803
|
+
this.unsignedFits = true
|
|
804
|
+
return acc
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/** A number's nearest `f64`, ties to even, as `JSON.parse` reads it; `0` for any other value. */
|
|
808
|
+
f64(v: JsonValue): f64 {
|
|
809
|
+
if (!this.readNumber(this.index(v))) {
|
|
810
|
+
return 0
|
|
811
|
+
}
|
|
812
|
+
return this.numbers.numberF64()
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/** Internal: points the number tokenizer at index `x`'s text and reads its one token; `false` when it is not a number. */
|
|
816
|
+
readNumber(x: i32): boolean {
|
|
817
|
+
if (this.kindAt(x) !== JSON_VALUE_NUMBER) {
|
|
818
|
+
return false
|
|
819
|
+
}
|
|
820
|
+
const numbers: JsonTokenizer = this.numbers
|
|
821
|
+
numbers.reset(this.bytes, jsonValueGet(this.first, x), jsonValueGet(this.count, x))
|
|
822
|
+
return numbers.next() === JSON_TOK_NUMBER
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
/** Internal: `bytes[at, at + n)` as a string, in one allocation. */
|
|
826
|
+
textOf(at: i32, n: i32): string {
|
|
827
|
+
const bytes: u8[] = this.bytes
|
|
828
|
+
if (at < 0 || n <= 0 || at > toI32(bytes.length) - n) {
|
|
829
|
+
return ""
|
|
830
|
+
}
|
|
831
|
+
return String.fromBytes(bytes, at, at + n)
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/** Internal: whether `bytes[at, at + n)` are exactly `s`'s bytes. */
|
|
835
|
+
bytesEqual(at: i32, n: i32, s: string): boolean {
|
|
836
|
+
const bytes: u8[] = this.bytes
|
|
837
|
+
if (toI32(s.length) !== n || at < 0 || at > toI32(bytes.length) - n) {
|
|
838
|
+
return false
|
|
839
|
+
}
|
|
840
|
+
for (let i: i32 = 0; i < n && i < toI32(s.length) && at + i < toI32(bytes.length); i += 1) {
|
|
841
|
+
if (toI32(bytes[at + i]) !== toI32(s.charCodeAt(i))) {
|
|
842
|
+
return false
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return true
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// ---- Building ---------------------------------------------------------------
|
|
849
|
+
// Each `new*` answers a handle, or `JSON_VALUE_ERR_VALUES` when the document
|
|
850
|
+
// already holds `maxValues`.
|
|
851
|
+
|
|
852
|
+
newNull(): JsonValue {
|
|
853
|
+
return this.handle(this.newNode(JSON_VALUE_NULL, JSON_VALUE_NO_WORD, JSON_VALUE_NO_WORD))
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
newBoolean(b: boolean): JsonValue {
|
|
857
|
+
return this.handle(
|
|
858
|
+
this.newNode(JSON_VALUE_BOOLEAN, b ? JSON_VALUE_TRUE_WORD : JSON_VALUE_NO_WORD, JSON_VALUE_NO_WORD)
|
|
859
|
+
)
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/** A string holding `s`'s bytes. */
|
|
863
|
+
newString(s: string): JsonValue {
|
|
864
|
+
return this.newStored(JSON_VALUE_STRING, s)
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
/** A number holding `x`'s digits. */
|
|
868
|
+
newI64(x: i64): JsonValue {
|
|
869
|
+
return this.newStored(JSON_VALUE_NUMBER, `${x}`)
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/** A number holding `x`'s digits. */
|
|
873
|
+
newU64(x: u64): JsonValue {
|
|
874
|
+
return this.newStored(JSON_VALUE_NUMBER, `${x}`)
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* A number holding `x` as `String(x)` spells it, the shortest digits that
|
|
879
|
+
* read back as `x`, with `-0` as `0`: what `JsonWriter.f64` writes.
|
|
880
|
+
* `JSON_VALUE_ERR_NUMBER` for NaN and both infinities, which JSON cannot
|
|
881
|
+
* spell.
|
|
882
|
+
*/
|
|
883
|
+
newF64(x: f64): JsonValue {
|
|
884
|
+
// `x - x` is `0` for every finite `x`, and NaN for NaN and both infinities.
|
|
885
|
+
if (x - x !== toF64(0)) {
|
|
886
|
+
return JSON_VALUE_ERR_NUMBER
|
|
887
|
+
}
|
|
888
|
+
return this.newStored(JSON_VALUE_NUMBER, `${x}`)
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* A number holding `text` exactly, which must be one JSON number and
|
|
893
|
+
* nothing else (RFC 8259 §6); `JSON_VALUE_ERR_NUMBER` otherwise. This is how
|
|
894
|
+
* a program writes a number no integer or float type holds.
|
|
895
|
+
*/
|
|
896
|
+
newNumber(text: string): JsonValue {
|
|
897
|
+
const at: i32 = this.byteTop
|
|
898
|
+
const n: i32 = this.storeText(text)
|
|
899
|
+
const numbers: JsonTokenizer = this.numbers
|
|
900
|
+
numbers.reset(this.bytes, at, n)
|
|
901
|
+
// The tokenizer allows blanks around a value, and a number here is the text alone.
|
|
902
|
+
const isNumber: boolean =
|
|
903
|
+
numbers.next() === JSON_TOK_NUMBER && numbers.tokStart === at && numbers.tokEnd === at + n
|
|
904
|
+
if (!isNumber || numbers.next() !== JSON_TOK_END) {
|
|
905
|
+
this.byteTop = at
|
|
906
|
+
return JSON_VALUE_ERR_NUMBER
|
|
907
|
+
}
|
|
908
|
+
return this.handle(this.newNode(JSON_VALUE_NUMBER, at, n))
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
newArray(): JsonValue {
|
|
912
|
+
return this.handle(this.newNode(JSON_VALUE_ARRAY, this.slotTop, JSON_VALUE_NO_WORD))
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
newObject(): JsonValue {
|
|
916
|
+
return this.handle(this.newNode(JSON_VALUE_OBJECT, this.slotTop, JSON_VALUE_NO_WORD))
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/** Appends `v` to array `a`. `JSON_VALUE_OK`, or `JSON_VALUE_ERR_HANDLE` or `JSON_VALUE_ERR_ATTACHED`. */
|
|
920
|
+
push(a: JsonValue, v: JsonValue): i32 {
|
|
921
|
+
const ax: i32 = this.index(a)
|
|
922
|
+
const vx: i32 = this.index(v)
|
|
923
|
+
if (this.kindAt(ax) !== JSON_VALUE_ARRAY) {
|
|
924
|
+
return JSON_VALUE_ERR_HANDLE
|
|
925
|
+
}
|
|
926
|
+
const code: i32 = this.adoptable(ax, vx)
|
|
927
|
+
if (code < 0) {
|
|
928
|
+
return code
|
|
929
|
+
}
|
|
930
|
+
this.append(ax, vx, JSON_VALUE_NO_WORD, JSON_VALUE_NO_WORD)
|
|
931
|
+
return JSON_VALUE_OK
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* Sets member `key` of object `o` to `v`: in place, keeping its position,
|
|
936
|
+
* when `o` has the key, and appended last when it does not, as assigning a
|
|
937
|
+
* property of a JavaScript object does. The value it replaces leaves the
|
|
938
|
+
* object and is in no container afterwards. `JSON_VALUE_OK`, or
|
|
939
|
+
* `JSON_VALUE_ERR_HANDLE` or `JSON_VALUE_ERR_ATTACHED`.
|
|
940
|
+
*/
|
|
941
|
+
set(o: JsonValue, key: string, v: JsonValue): i32 {
|
|
942
|
+
const ox: i32 = this.index(o)
|
|
943
|
+
const vx: i32 = this.index(v)
|
|
944
|
+
if (this.kindAt(ox) !== JSON_VALUE_OBJECT) {
|
|
945
|
+
return JSON_VALUE_ERR_HANDLE
|
|
946
|
+
}
|
|
947
|
+
const code: i32 = this.adoptable(ox, vx)
|
|
948
|
+
if (code < 0) {
|
|
949
|
+
return code
|
|
950
|
+
}
|
|
951
|
+
const s: i32 = this.findKey(ox, key)
|
|
952
|
+
if (s >= 0) {
|
|
953
|
+
jsonValuePut(this.parent, jsonValueGet(this.slotValue, s), -1)
|
|
954
|
+
jsonValuePut(this.slotValue, s, vx)
|
|
955
|
+
jsonValuePut(this.parent, vx, ox)
|
|
956
|
+
return JSON_VALUE_OK
|
|
957
|
+
}
|
|
958
|
+
this.append(ox, vx, this.byteTop, this.storeText(key))
|
|
959
|
+
return JSON_VALUE_OK
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Internal: whether the value at index `v` may go into the container at
|
|
964
|
+
* index `c`: it is a value, it is in no container, and it is not `c` or a
|
|
965
|
+
* container `c` is inside. Through `push` and `set`, that is what keeps
|
|
966
|
+
* every tree finite.
|
|
967
|
+
*/
|
|
968
|
+
adoptable(c: i32, v: i32): i32 {
|
|
969
|
+
if (this.kindAt(v) === JSON_VALUE_NONE) {
|
|
970
|
+
return JSON_VALUE_ERR_HANDLE
|
|
971
|
+
}
|
|
972
|
+
if (jsonValueGet(this.parent, v) >= 0) {
|
|
973
|
+
return JSON_VALUE_ERR_ATTACHED
|
|
974
|
+
}
|
|
975
|
+
let p: i32 = c
|
|
976
|
+
while (p >= 0) {
|
|
977
|
+
if (p === v) {
|
|
978
|
+
return JSON_VALUE_ERR_ATTACHED
|
|
979
|
+
}
|
|
980
|
+
p = jsonValueGet(this.parent, p)
|
|
981
|
+
}
|
|
982
|
+
return JSON_VALUE_OK
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* Internal: adds index `v`, with the key `bytes[keyAt, keyAt + keyLength)`,
|
|
987
|
+
* after the last member of index `c`, unchecked: `push` and `set` check
|
|
988
|
+
* first. A container whose slots are full moves them to the end of the slot
|
|
989
|
+
* arrays with twice the room, unless they are at the end already, where they
|
|
990
|
+
* grow in place; the slots it leaves are not reused until `clear`.
|
|
991
|
+
*/
|
|
992
|
+
append(c: i32, v: i32, keyAt: i32, keyLength: i32): void {
|
|
993
|
+
let start: i32 = jsonValueGet(this.first, c)
|
|
994
|
+
const n: i32 = jsonValueGet(this.count, c)
|
|
995
|
+
const capacity: i32 = jsonValueGet(this.room, c)
|
|
996
|
+
if (n === capacity) {
|
|
997
|
+
const grown: i32 = capacity < JSON_VALUE_FIRST_SLOTS ? JSON_VALUE_FIRST_SLOTS : capacity * 2
|
|
998
|
+
if (start + capacity !== this.slotTop) {
|
|
999
|
+
const moved: i32 = this.slotTop
|
|
1000
|
+
for (let i: i32 = 0; i < n; i += 1) {
|
|
1001
|
+
jsonValuePut(this.slotValue, moved + i, jsonValueGet(this.slotValue, start + i))
|
|
1002
|
+
jsonValuePut(this.slotKeyAt, moved + i, jsonValueGet(this.slotKeyAt, start + i))
|
|
1003
|
+
jsonValuePut(this.slotKeyLength, moved + i, jsonValueGet(this.slotKeyLength, start + i))
|
|
1004
|
+
}
|
|
1005
|
+
start = moved
|
|
1006
|
+
jsonValuePut(this.first, c, start)
|
|
1007
|
+
}
|
|
1008
|
+
this.slotTop = start + grown
|
|
1009
|
+
this.slotValue = jsonValueRoomI32(this.slotValue, this.slotTop)
|
|
1010
|
+
this.slotKeyAt = jsonValueRoomI32(this.slotKeyAt, this.slotTop)
|
|
1011
|
+
this.slotKeyLength = jsonValueRoomI32(this.slotKeyLength, this.slotTop)
|
|
1012
|
+
jsonValuePut(this.room, c, grown)
|
|
1013
|
+
}
|
|
1014
|
+
jsonValuePut(this.slotValue, start + n, v)
|
|
1015
|
+
jsonValuePut(this.slotKeyAt, start + n, keyAt)
|
|
1016
|
+
jsonValuePut(this.slotKeyLength, start + n, keyLength)
|
|
1017
|
+
jsonValuePut(this.count, c, n + 1)
|
|
1018
|
+
jsonValuePut(this.parent, v, c)
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
/** Internal: a new value of `kind` holding `s`'s bytes, as a handle. */
|
|
1022
|
+
newStored(kind: i32, s: string): JsonValue {
|
|
1023
|
+
return this.handle(this.newNode(kind, this.byteTop, this.storeText(s)))
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/** Internal: `s`'s bytes onto the end of `bytes`; answers how many. */
|
|
1027
|
+
storeText(s: string): i32 {
|
|
1028
|
+
const n: i32 = toI32(s.length)
|
|
1029
|
+
const at: i32 = this.byteTop
|
|
1030
|
+
const buf: u8[] = jsonValueRoom(this.bytes, at + n)
|
|
1031
|
+
this.bytes = buf
|
|
1032
|
+
for (let i: i32 = 0; i < n && at + i < toI32(buf.length); i += 1) {
|
|
1033
|
+
if (at + i >= 0) {
|
|
1034
|
+
buf[at + i] = toU8(s.charCodeAt(i))
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
this.byteTop = at + n
|
|
1038
|
+
return n
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// ---- Writing ----------------------------------------------------------------
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
* Writes `v` into `w` as one value, members in order and numbers as their
|
|
1045
|
+
* text; the outcome is the writer's, in `w.failed`. A value that names
|
|
1046
|
+
* nothing writes nothing and fails nothing. A parsed tree is never deeper
|
|
1047
|
+
* than the writer nests; a built one deeper than `JSON_WRITER_MAX_DEPTH`
|
|
1048
|
+
* ends at `JSON_WRITE_TOO_DEEP`, where the recursion stops.
|
|
1049
|
+
*/
|
|
1050
|
+
write(v: JsonValue, w: JsonWriter): void {
|
|
1051
|
+
this.writeAt(this.index(v), w)
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/** Internal: `write` of index `x`. */
|
|
1055
|
+
writeAt(x: i32, w: JsonWriter): void {
|
|
1056
|
+
const k: i32 = this.kindAt(x)
|
|
1057
|
+
if (k === JSON_VALUE_NONE || w.failed !== JSON_WRITE_OK) {
|
|
1058
|
+
return
|
|
1059
|
+
}
|
|
1060
|
+
if (k === JSON_VALUE_NULL) {
|
|
1061
|
+
w.nullValue()
|
|
1062
|
+
} else if (k === JSON_VALUE_BOOLEAN) {
|
|
1063
|
+
w.boolean(jsonValueGet(this.first, x) === JSON_VALUE_TRUE_WORD)
|
|
1064
|
+
} else if (k === JSON_VALUE_NUMBER) {
|
|
1065
|
+
w.raw(this.bytes, jsonValueGet(this.first, x), jsonValueGet(this.count, x))
|
|
1066
|
+
} else if (k === JSON_VALUE_STRING) {
|
|
1067
|
+
w.stringBytes(this.bytes, jsonValueGet(this.first, x), jsonValueGet(this.count, x))
|
|
1068
|
+
} else {
|
|
1069
|
+
// A container: its members are the slots from `first`, read directly.
|
|
1070
|
+
const object: boolean = k === JSON_VALUE_OBJECT
|
|
1071
|
+
const start: i32 = jsonValueGet(this.first, x)
|
|
1072
|
+
const end: i32 = start + jsonValueGet(this.count, x)
|
|
1073
|
+
if (object) {
|
|
1074
|
+
w.beginObject()
|
|
1075
|
+
} else {
|
|
1076
|
+
w.beginArray()
|
|
1077
|
+
}
|
|
1078
|
+
for (let s: i32 = start; s < end && w.failed === JSON_WRITE_OK; s += 1) {
|
|
1079
|
+
if (object) {
|
|
1080
|
+
w.keyBytes(this.bytes, jsonValueGet(this.slotKeyAt, s), jsonValueGet(this.slotKeyLength, s))
|
|
1081
|
+
}
|
|
1082
|
+
this.writeAt(jsonValueGet(this.slotValue, s), w)
|
|
1083
|
+
}
|
|
1084
|
+
if (object) {
|
|
1085
|
+
w.endObject()
|
|
1086
|
+
} else {
|
|
1087
|
+
w.endArray()
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* `v` as minified JSON text: written into a buffer that doubles until it
|
|
1094
|
+
* fits, then made into one string. `""` when `v` names nothing or the
|
|
1095
|
+
* writer refuses it, which only a built tree deeper than
|
|
1096
|
+
* `JSON_WRITER_MAX_DEPTH` makes it do; `write` gives the code.
|
|
1097
|
+
*/
|
|
1098
|
+
stringify(v: JsonValue): string {
|
|
1099
|
+
// Every byte stored and a few of punctuation per value: one pass fits most
|
|
1100
|
+
// documents, and a text with many escapes doubles from there.
|
|
1101
|
+
let size: i32 = this.byteTop + 8 * this.top + 64
|
|
1102
|
+
while (true) {
|
|
1103
|
+
const buf: u8[] = new Array<u8>(size)
|
|
1104
|
+
const w: JsonWriter = new JsonWriter(buf, 0)
|
|
1105
|
+
this.write(v, w)
|
|
1106
|
+
if (w.failed !== JSON_WRITE_FULL || size > JSON_VALUE_I32_MAX / 2) {
|
|
1107
|
+
return w.complete() && w.end <= toI32(buf.length) ? String.fromBytes(buf, 0, w.end) : ""
|
|
1108
|
+
}
|
|
1109
|
+
size = size * 2
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
/** `a[i]`, or `-1` when `i` is outside `a`. */
|
|
1115
|
+
const jsonValueGet = (a: i32[], i: i32): i32 => (i >= 0 && i < toI32(a.length) ? a[i] : -1)
|
|
1116
|
+
|
|
1117
|
+
/** `a[i] = x`, growing `a` by one when `i` is its length; an `i` further out writes nothing. */
|
|
1118
|
+
const jsonValuePut = (a: i32[], i: i32, x: i32): void => {
|
|
1119
|
+
if (i >= 0 && i < toI32(a.length)) {
|
|
1120
|
+
a[i] = x
|
|
1121
|
+
} else if (i === toI32(a.length)) {
|
|
1122
|
+
a.push(x)
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* `a` when it holds at least `n` elements, and otherwise a copy of it at
|
|
1128
|
+
* least twice as long, for the caller to keep in `a`'s place. Doubling is
|
|
1129
|
+
* what keeps a run of appends linear, and the old array is left in the arena.
|
|
1130
|
+
*/
|
|
1131
|
+
const jsonValueRoom = (a: u8[], n: i32): u8[] => {
|
|
1132
|
+
const have: i32 = toI32(a.length)
|
|
1133
|
+
if (n <= have) {
|
|
1134
|
+
return a
|
|
1135
|
+
}
|
|
1136
|
+
const grown: u8[] = new Array<u8>(n > have * 2 ? n : have * 2)
|
|
1137
|
+
grown.set(a, 0)
|
|
1138
|
+
return grown
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/** `jsonValueRoom` for an `i32[]`. */
|
|
1142
|
+
const jsonValueRoomI32 = (a: i32[], n: i32): i32[] => {
|
|
1143
|
+
const have: i32 = toI32(a.length)
|
|
1144
|
+
if (n <= have) {
|
|
1145
|
+
return a
|
|
1146
|
+
}
|
|
1147
|
+
const grown: i32[] = new Array<i32>(n > have * 2 ? n : have * 2)
|
|
1148
|
+
grown.set(a, 0)
|
|
1149
|
+
return grown
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/** `bytes[a, a + an)` and `bytes[b, b + bn)` compared byte by byte, a shorter prefix first: negative, `0` or positive. */
|
|
1153
|
+
const jsonValueCompareBytes = (bytes: u8[], a: i32, an: i32, b: i32, bn: i32): i32 => {
|
|
1154
|
+
const n: i32 = an < bn ? an : bn
|
|
1155
|
+
for (let i: i32 = 0; i < n; i += 1) {
|
|
1156
|
+
const x: i32 = a + i >= 0 && a + i < toI32(bytes.length) ? toI32(bytes[a + i]) : 0
|
|
1157
|
+
const y: i32 = b + i >= 0 && b + i < toI32(bytes.length) ? toI32(bytes[b + i]) : 0
|
|
1158
|
+
if (x !== y) {
|
|
1159
|
+
return x - y
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
return an - bn
|
|
1163
|
+
}
|