@amritk/nish-x86_64-linux 0.18.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 +215 -18
- 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 +4 -4
- package/runtime/runtime-wasm.c +5 -3
- package/runtime/runtime.c +69 -13
- package/runtime/shim.mjs +71 -1
- package/scripts/build.sh +223 -22
- package/std/README.md +81 -51
- 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 +932 -61
- 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 +1120 -34
- 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 +211 -31
- package/std/net/http3.ts +187 -28
- package/std/net/prefork.ts +412 -0
- package/std/net/quic-listener.ts +88 -7
- package/std/net/quic-packet.ts +243 -82
- package/std/net/quic-recovery.ts +361 -30
- package/std/net/quic-stream.ts +424 -24
- package/std/net/quic.ts +519 -167
- 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 +196 -53
- package/std/net/tls.ts +1018 -260
- package/std/net/url.ts +571 -0
- package/std/random.ts +94 -0
- package/std/threads.ts +438 -14
|
@@ -0,0 +1,1014 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nish/json-typed` — decode a JSON object into a class, and encode a class
|
|
3
|
+
* back, with code the compiler writes from the class's fields.
|
|
4
|
+
*
|
|
5
|
+
* import { JsonLimits } from "nish/json-tokenizer";
|
|
6
|
+
* import { JsonWriter } from "nish/json";
|
|
7
|
+
* import { JsonDecoder, jsonDecode, jsonEncode } from "nish/json-typed";
|
|
8
|
+
*
|
|
9
|
+
* class Point { x: i32 = 0; y: i32 = 0 }
|
|
10
|
+
*
|
|
11
|
+
* const d = new JsonDecoder(body, 0, toI32(body.length), new JsonLimits());
|
|
12
|
+
* const p = new Point();
|
|
13
|
+
* if (jsonDecode(d, p) === 0) {
|
|
14
|
+
* const w = new JsonWriter(out, 0);
|
|
15
|
+
* jsonEncode(w, p); // out[0 .. w.end) is {"x":..,"y":..}
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* **What is generated, and from what.** `jsonDecode(d, out)` and
|
|
19
|
+
* `jsonEncode(w, value)` are generic, and the bodies written below are not
|
|
20
|
+
* what runs natively: the compiler lowers every call in place, in the module
|
|
21
|
+
* that makes it, to code it writes from the layout of the class the call
|
|
22
|
+
* binds `T` to (`src/json-typed.ts`, `src/emit-json.ts`). That code is
|
|
23
|
+
* straight-line calls into the methods of `JsonDecoder` and the functions
|
|
24
|
+
* here for each field, and into `JsonWriter`'s, so all of the parsing is the
|
|
25
|
+
* tokenizer's and all of the writing is the writer's. A field may be an
|
|
26
|
+
* `i32`, `u32`, `i64`, `u64`, `f64` (or `number`), `boolean` or `string`, a
|
|
27
|
+
* numeric enum (its number), a class decoded the same way, a `JsonNumber`, an
|
|
28
|
+
* array of any of those, a `Map<string, V>` (a JSON object) whose `V` is any
|
|
29
|
+
* of these, or a string, class, array or map `| null`; anything else is
|
|
30
|
+
* refused at the call, naming the field (docs/LANGUAGE.md, "Typed JSON").
|
|
31
|
+
*
|
|
32
|
+
* **The decoding rules.** A JSON object's keys are the class's field names,
|
|
33
|
+
* byte for byte, or the keys its options give them, found by the key's length
|
|
34
|
+
* and then, among three or more names of that length, by one of its bytes:
|
|
35
|
+
*
|
|
36
|
+
* class Account {
|
|
37
|
+
* static readonly json = { rename: { userId: "user_id" }, optional: ["tags"] };
|
|
38
|
+
* ...
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* The compiler reads that literal; it is not a field. Every field is required
|
|
42
|
+
* but an optional one, whose missing key sets it to its initializer, a key
|
|
43
|
+
* may appear once, and a key that names no field is an error unless
|
|
44
|
+
* `ignoreUnknown` is set, when its value is stepped over. A number must fit its field: an integer field takes
|
|
45
|
+
* an integer written without a fraction or an exponent and in the field's
|
|
46
|
+
* range, and `f64` takes any number. `null` is accepted only by a field whose
|
|
47
|
+
* type is `| null`. Fields are written to `out` as they are read, in the
|
|
48
|
+
* input's order: a nested object is decoded into the object the field already
|
|
49
|
+
* holds, an array's elements into the elements it already has (so a reused
|
|
50
|
+
* object reuses its arrays and objects, and `Arena.used()` stays flat), and a
|
|
51
|
+
* string field keeps the string it holds when the input's is equal.
|
|
52
|
+
* A class the decoder has to make — an array element past the array's end, a
|
|
53
|
+
* `| null` field that holds `null`, or a map's value — is made with `new C()`.
|
|
54
|
+
* A map is emptied and filled in the input's order, and every map of one
|
|
55
|
+
* decoding takes at most `maxMapKeys` keys between them.
|
|
56
|
+
*
|
|
57
|
+
* **Errors are sticky codes**, as the tokenizer's and the writer's are. The
|
|
58
|
+
* first error sets `failed` to a negative code — a `JSON_DECODE_*` code here,
|
|
59
|
+
* or the tokenizer's own `JSON_TOK_ERR_*` code when the text is not JSON or
|
|
60
|
+
* crosses one of its `JsonLimits` — and `errorAt` to the byte offset of the
|
|
61
|
+
* token it was found at. Every later call does nothing, and `jsonDecode`
|
|
62
|
+
* answers `failed`. Nothing an input holds panics. After an error, `out` holds
|
|
63
|
+
* a value of its type in every field, some of them decoded and some as they
|
|
64
|
+
* were.
|
|
65
|
+
*
|
|
66
|
+
* **Encoding** writes the fields in declaration order, each through the
|
|
67
|
+
* writer method of its type, so the text is what `JSON.stringify` writes for
|
|
68
|
+
* the same object; the writer's own sticky code reports a full buffer.
|
|
69
|
+
*
|
|
70
|
+
* **Under Node** the types are erased, so there is no layout to generate the
|
|
71
|
+
* code from: `jsonDecode` and `jsonEncode` end the program with a panic
|
|
72
|
+
* naming this, rather than guessing a type from whatever value a field holds
|
|
73
|
+
* (docs/LANGUAGE.md, "Typed JSON", class C). Everything else here runs there.
|
|
74
|
+
*
|
|
75
|
+
* Every name here is `json`-prefixed, as `std/json.ts` explains, and every
|
|
76
|
+
* code is an `i32`, so the module is the same under `--number-mode f64`.
|
|
77
|
+
*/
|
|
78
|
+
import { JsonWriter } from "nish/json"
|
|
79
|
+
import {
|
|
80
|
+
JSON_TOK_ARRAY_BEGIN,
|
|
81
|
+
JSON_TOK_ARRAY_END,
|
|
82
|
+
JSON_TOK_END,
|
|
83
|
+
JSON_TOK_FALSE,
|
|
84
|
+
JSON_TOK_KEY,
|
|
85
|
+
JSON_TOK_NULL,
|
|
86
|
+
JSON_TOK_NUMBER,
|
|
87
|
+
JSON_TOK_OBJECT_BEGIN,
|
|
88
|
+
JSON_TOK_STRING,
|
|
89
|
+
JSON_TOK_TRUE,
|
|
90
|
+
JsonLimits,
|
|
91
|
+
JsonTokenizer,
|
|
92
|
+
} from "nish/json-tokenizer"
|
|
93
|
+
|
|
94
|
+
/** Nothing has failed. */
|
|
95
|
+
export const JSON_DECODE_OK: i32 = 0
|
|
96
|
+
/** A value of the wrong JSON type for its field: a string for an `i32`, an array for a class, `null` for a field that is not `| null`. */
|
|
97
|
+
export const JSON_DECODE_TYPE: i32 = -20
|
|
98
|
+
/** A number its field cannot hold: a fraction or an exponent for an integer field, or a value outside its range. */
|
|
99
|
+
export const JSON_DECODE_RANGE: i32 = -21
|
|
100
|
+
/** A key that names no field of the class, while `ignoreUnknown` is off. */
|
|
101
|
+
export const JSON_DECODE_UNKNOWN: i32 = -22
|
|
102
|
+
/** A key that names a field the object already gave. */
|
|
103
|
+
export const JSON_DECODE_DUPLICATE: i32 = -23
|
|
104
|
+
/** An object that ended without one of the class's fields; `errorAt` is its closing brace. */
|
|
105
|
+
export const JSON_DECODE_MISSING: i32 = -24
|
|
106
|
+
/**
|
|
107
|
+
* More keys, over every `Map` field one decoding fills, than `maxMapKeys`;
|
|
108
|
+
* `errorAt` is the first key past the limit. The global `Map` hashes a
|
|
109
|
+
* string with no seed, so a body can choose keys that all collide, and the
|
|
110
|
+
* limit bounds what the maps' probes cost over the whole decoding
|
|
111
|
+
* (docs/security/json.md, JSON-13).
|
|
112
|
+
*/
|
|
113
|
+
export const JSON_DECODE_MAP_LIMIT: i32 = -25
|
|
114
|
+
|
|
115
|
+
/** What a program running under Node is told, where the compiler would have written the code. */
|
|
116
|
+
const JSON_TYPED_ERASED: string =
|
|
117
|
+
"nish/json-typed: jsonDecode and jsonEncode are written by the compiler from the class's fields, and under Node the types are erased"
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* A JSON number kept as the text it was written in, so that no digit is lost:
|
|
121
|
+
* an integer wider than a `u64` and a fraction with more digits than an `f64`
|
|
122
|
+
* holds are decoded and encoded again byte for byte. A field of this class is
|
|
123
|
+
* read and written as one number, not as an object: `jsonDecode` copies the
|
|
124
|
+
* number's text into it, and `jsonEncode` writes that text back unchanged.
|
|
125
|
+
*
|
|
126
|
+
* It reads its text as `nish/json-value` reads a number (`numberText`,
|
|
127
|
+
* `isI64`, `i64`, `isU64`, `u64`, `f64`), through a tokenizer of its own, so
|
|
128
|
+
* the two answer alike; the two are separate types until the typed layer
|
|
129
|
+
* can name a `JsonValue`'s number directly.
|
|
130
|
+
*
|
|
131
|
+
* A new one holds `0`. Decoding into one reuses its bytes, so an object
|
|
132
|
+
* decoded again with numbers no longer than before allocates nothing for
|
|
133
|
+
* them.
|
|
134
|
+
*/
|
|
135
|
+
export class JsonNumber {
|
|
136
|
+
/** The text is `bytes[0, count)`, one JSON number; the bytes past it are room. */
|
|
137
|
+
bytes: u8[]
|
|
138
|
+
count: i32 = 1
|
|
139
|
+
|
|
140
|
+
constructor() {
|
|
141
|
+
this.bytes = [48]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** The number's text, exactly as it was written or set. */
|
|
145
|
+
text(): string {
|
|
146
|
+
return String.fromBytes(this.bytes, 0, this.count)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* A tokenizer over the text, read to its one token. It may open no
|
|
151
|
+
* container and take a number of any length, so it allocates no stack.
|
|
152
|
+
*/
|
|
153
|
+
reader(): JsonTokenizer {
|
|
154
|
+
const limits: JsonLimits = new JsonLimits()
|
|
155
|
+
limits.maxDepth = 0
|
|
156
|
+
limits.maxNumberBytes = this.count
|
|
157
|
+
const tok: JsonTokenizer = new JsonTokenizer(this.bytes, 0, this.count, limits)
|
|
158
|
+
tok.next()
|
|
159
|
+
return tok
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Whether the number is written without a fraction or an exponent and its value fits an `i64`. */
|
|
163
|
+
isI64(): boolean {
|
|
164
|
+
return this.reader().isInteger()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** The value as an `i64` when `isI64()` holds, and `0` otherwise. */
|
|
168
|
+
i64(): i64 {
|
|
169
|
+
return this.reader().numberI64()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Whether the number is written in digits alone, with no sign, fraction or exponent, and fits a `u64`. */
|
|
173
|
+
isU64(): boolean {
|
|
174
|
+
return this.reader().isU64()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** The value as a `u64` when `isU64()` holds, and `0` otherwise. */
|
|
178
|
+
u64(): u64 {
|
|
179
|
+
return this.reader().numberU64()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** The nearest `f64`, ties to even, as `JSON.parse` reads it. */
|
|
183
|
+
f64(): f64 {
|
|
184
|
+
return this.reader().numberF64()
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Sets the text to `text` when it is one JSON number and nothing else, and
|
|
189
|
+
* answers whether it was; any other text leaves the number as it was.
|
|
190
|
+
*/
|
|
191
|
+
setText(text: string): boolean {
|
|
192
|
+
const n: i32 = toI32(text.length)
|
|
193
|
+
const bytes: u8[] = new Array<u8>(n)
|
|
194
|
+
for (let k: i32 = 0; k < n && k < toI32(bytes.length); k += 1) {
|
|
195
|
+
let c: i32 = toI32(text.charCodeAt(k))
|
|
196
|
+
// A number is ASCII; anything wider fails the check below as `~` does.
|
|
197
|
+
if (c > 127) {
|
|
198
|
+
c = 126
|
|
199
|
+
}
|
|
200
|
+
bytes[k] = toU8(c)
|
|
201
|
+
}
|
|
202
|
+
const limits: JsonLimits = new JsonLimits()
|
|
203
|
+
limits.maxDepth = 0
|
|
204
|
+
limits.maxNumberBytes = n
|
|
205
|
+
const tok: JsonTokenizer = new JsonTokenizer(bytes, 0, n, limits)
|
|
206
|
+
const isNumber: boolean = tok.next() === JSON_TOK_NUMBER && tok.tokStart === 0 && tok.tokEnd === n
|
|
207
|
+
if (!isNumber || tok.next() !== JSON_TOK_END) {
|
|
208
|
+
return false
|
|
209
|
+
}
|
|
210
|
+
this.bytes = bytes
|
|
211
|
+
this.count = n
|
|
212
|
+
return true
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* One decoding: a tokenizer over the input and the sticky error. Make one per
|
|
218
|
+
* connection and `reset` it per request; nothing it does allocates but the
|
|
219
|
+
* strings it decodes, the arrays it grows and the objects it makes.
|
|
220
|
+
*/
|
|
221
|
+
export class JsonDecoder {
|
|
222
|
+
tok: JsonTokenizer
|
|
223
|
+
/** `JSON_DECODE_OK`, or the first `JSON_DECODE_*` or `JSON_TOK_ERR_*` code. */
|
|
224
|
+
failed: i32 = 0
|
|
225
|
+
/** The byte offset in the input of the token the error was found at; `-1` until one. */
|
|
226
|
+
errorAt: i32 = -1
|
|
227
|
+
/**
|
|
228
|
+
* The most keys every `Map` field of one decoding may take together, 4,096
|
|
229
|
+
* unless set; past it, `JSON_DECODE_MAP_LIMIT`. At `0` or below, no map may
|
|
230
|
+
* take a key, so a map's only accepted value is `{}`.
|
|
231
|
+
*/
|
|
232
|
+
maxMapKeys: i32 = 4096
|
|
233
|
+
/** The keys the maps of this decoding have taken so far; `reset` clears it. */
|
|
234
|
+
mapKeys: i32 = 0
|
|
235
|
+
/** Step over a key that names no field instead of failing with `JSON_DECODE_UNKNOWN`. */
|
|
236
|
+
ignoreUnknown: boolean = false
|
|
237
|
+
/**
|
|
238
|
+
* Whether the tokenizer's current token is a value nothing has read yet:
|
|
239
|
+
* the first token of an array element, read to see whether the array had
|
|
240
|
+
* ended, or a value `takeNull` found was not `null`.
|
|
241
|
+
*/
|
|
242
|
+
held: boolean = false
|
|
243
|
+
|
|
244
|
+
constructor(src: u8[], off: i32, len: i32, limits: JsonLimits) {
|
|
245
|
+
this.tok = new JsonTokenizer(src, off, len, limits)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Starts over on `src[off, off + len)`, keeping the limits, `ignoreUnknown`
|
|
250
|
+
* and `maxMapKeys`, and clearing any error and the count of map keys taken.
|
|
251
|
+
*/
|
|
252
|
+
reset(src: u8[], off: i32, len: i32): void {
|
|
253
|
+
this.tok.reset(src, off, len)
|
|
254
|
+
this.failed = JSON_DECODE_OK
|
|
255
|
+
this.errorAt = -1
|
|
256
|
+
this.held = false
|
|
257
|
+
this.mapKeys = 0
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Records the first error. */
|
|
261
|
+
fail(code: i32, at: i32): void {
|
|
262
|
+
if (this.failed === JSON_DECODE_OK) {
|
|
263
|
+
this.failed = code
|
|
264
|
+
this.errorAt = at
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** `JSON_DECODE_TYPE` at the current token, unless the read that found it failed already. */
|
|
269
|
+
wrongType(): void {
|
|
270
|
+
this.fail(JSON_DECODE_TYPE, this.tok.tokStart)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** The kind of the next value's first token, or a negative code; the held token first, when there is one. */
|
|
274
|
+
take(): i32 {
|
|
275
|
+
if (this.failed !== JSON_DECODE_OK) {
|
|
276
|
+
return this.failed
|
|
277
|
+
}
|
|
278
|
+
if (this.held) {
|
|
279
|
+
this.held = false
|
|
280
|
+
return this.tok.kind
|
|
281
|
+
}
|
|
282
|
+
const k: i32 = this.tok.next()
|
|
283
|
+
if (k < 0) {
|
|
284
|
+
this.fail(k, this.tok.errorAt)
|
|
285
|
+
}
|
|
286
|
+
return k
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** The next value opens an object; anything else is `JSON_DECODE_TYPE`. */
|
|
290
|
+
beginObject(): boolean {
|
|
291
|
+
const k: i32 = this.take()
|
|
292
|
+
if (k === JSON_TOK_OBJECT_BEGIN) {
|
|
293
|
+
return true
|
|
294
|
+
}
|
|
295
|
+
if (k >= 0) {
|
|
296
|
+
this.wrongType()
|
|
297
|
+
}
|
|
298
|
+
return false
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** Reads the next key of the open object, or its end; `false` at the end and after any error. */
|
|
302
|
+
nextField(): boolean {
|
|
303
|
+
if (this.failed !== JSON_DECODE_OK) {
|
|
304
|
+
return false
|
|
305
|
+
}
|
|
306
|
+
const k: i32 = this.tok.next()
|
|
307
|
+
if (k === JSON_TOK_KEY) {
|
|
308
|
+
return true
|
|
309
|
+
}
|
|
310
|
+
if (k < 0) {
|
|
311
|
+
this.fail(k, this.tok.errorAt)
|
|
312
|
+
}
|
|
313
|
+
return false
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** Whether the current key is `name`. */
|
|
317
|
+
keyIs(name: string): boolean {
|
|
318
|
+
return this.tok.keyEquals(name)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* The current key's length in bytes once decoded, or `-1` when the current
|
|
323
|
+
* token is not a key: what the code written for a class switches on before
|
|
324
|
+
* it compares a key's bytes with a field's name.
|
|
325
|
+
*/
|
|
326
|
+
keyLength(): i32 {
|
|
327
|
+
return this.tok.stringLength()
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Byte `at` of the current key when the key has no escape, so that its
|
|
332
|
+
* bytes are the input's, and `-1` otherwise: for a key with an escape, for
|
|
333
|
+
* an `at` outside it, and when the current token is not a key. A `-1`
|
|
334
|
+
* matches no byte of a field's name, and the code written for a class then
|
|
335
|
+
* compares the key with each name of its length through `keyIs`, which
|
|
336
|
+
* decodes the escapes.
|
|
337
|
+
*/
|
|
338
|
+
keyByte(at: i32): i32 {
|
|
339
|
+
const tok: JsonTokenizer = this.tok
|
|
340
|
+
const n: i32 = tok.stringLength()
|
|
341
|
+
if (n !== tok.tokEnd - tok.tokStart - 2 || at < 0 || at >= n) {
|
|
342
|
+
return -1
|
|
343
|
+
}
|
|
344
|
+
const src: u8[] = tok.src
|
|
345
|
+
const i: i32 = tok.tokStart + 1 + at
|
|
346
|
+
if (i < 0 || i >= toI32(src.length)) {
|
|
347
|
+
return -1
|
|
348
|
+
}
|
|
349
|
+
return toI32(src[i])
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Whether the maps of this decoding may take another key, the current one:
|
|
354
|
+
* whether they have taken fewer than `maxMapKeys` between them, counting
|
|
355
|
+
* it as taken when they may. A key is counted here, before its value is
|
|
356
|
+
* read, so the keys of a map inside another map count before the outer
|
|
357
|
+
* key's value is whole, and the total never passes the limit.
|
|
358
|
+
* `JSON_DECODE_MAP_LIMIT` at the key when they may not.
|
|
359
|
+
*/
|
|
360
|
+
mapRoom(): boolean {
|
|
361
|
+
if (this.mapKeys < this.maxMapKeys) {
|
|
362
|
+
this.mapKeys += 1
|
|
363
|
+
return true
|
|
364
|
+
}
|
|
365
|
+
this.fail(JSON_DECODE_MAP_LIMIT, this.tok.tokStart)
|
|
366
|
+
return false
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** The current key, decoded, as a new string: a `Map` field's key. `""` when the current token is not a key. */
|
|
370
|
+
key(): string {
|
|
371
|
+
return this.text()
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* The current key or string as a new string, made by one
|
|
376
|
+
* `String.fromBytes`: straight from the input when it has no escape, and
|
|
377
|
+
* from its unescaped bytes when it has one. `""` when the current token is
|
|
378
|
+
* neither.
|
|
379
|
+
*/
|
|
380
|
+
text(): string {
|
|
381
|
+
const tok: JsonTokenizer = this.tok
|
|
382
|
+
const n: i32 = tok.stringLength()
|
|
383
|
+
if (n < 0) {
|
|
384
|
+
return ""
|
|
385
|
+
}
|
|
386
|
+
if (n === tok.tokEnd - tok.tokStart - 2) {
|
|
387
|
+
// No escape, so the bytes between the quotes are the string's, copied
|
|
388
|
+
// once by `String.fromBytes`.
|
|
389
|
+
return String.fromBytes(tok.src, tok.tokStart + 1, tok.tokEnd - 1)
|
|
390
|
+
}
|
|
391
|
+
// The unescaped bytes are a temporary: a caller that keeps only the string
|
|
392
|
+
// it answers gives them back (the call-site reclaim, docs/LANGUAGE.md).
|
|
393
|
+
const bytes: u8[] = new Array<u8>(n)
|
|
394
|
+
const at: i32 = 0
|
|
395
|
+
tok.stringInto(bytes, at)
|
|
396
|
+
return String.fromBytes(bytes, 0, n)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** The current key names no field: stepped over with its value, or `JSON_DECODE_UNKNOWN`. */
|
|
400
|
+
unknownField(): void {
|
|
401
|
+
if (this.failed !== JSON_DECODE_OK) {
|
|
402
|
+
return
|
|
403
|
+
}
|
|
404
|
+
if (!this.ignoreUnknown) {
|
|
405
|
+
this.fail(JSON_DECODE_UNKNOWN, this.tok.tokStart)
|
|
406
|
+
return
|
|
407
|
+
}
|
|
408
|
+
const k: i32 = this.tok.skipValue()
|
|
409
|
+
if (k < 0) {
|
|
410
|
+
this.fail(k, this.tok.errorAt)
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/** The current key names a field the object already gave. */
|
|
415
|
+
duplicateField(): void {
|
|
416
|
+
this.fail(JSON_DECODE_DUPLICATE, this.tok.tokStart)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/** The object just closed without one of the fields. */
|
|
420
|
+
missingField(): void {
|
|
421
|
+
this.fail(JSON_DECODE_MISSING, this.tok.tokStart)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** After the top-level value: nothing but blanks may follow. Answers `failed`. */
|
|
425
|
+
finish(): i32 {
|
|
426
|
+
if (this.failed === JSON_DECODE_OK) {
|
|
427
|
+
const k: i32 = this.tok.next()
|
|
428
|
+
if (k < 0) {
|
|
429
|
+
this.fail(k, this.tok.errorAt)
|
|
430
|
+
} else if (k !== JSON_TOK_END) {
|
|
431
|
+
this.wrongType()
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return this.failed
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** Whether the next value is `null`, read if so; otherwise it is held for the read that follows. */
|
|
438
|
+
takeNull(): boolean {
|
|
439
|
+
const k: i32 = this.take()
|
|
440
|
+
if (k === JSON_TOK_NULL) {
|
|
441
|
+
return true
|
|
442
|
+
}
|
|
443
|
+
if (k >= 0) {
|
|
444
|
+
this.held = true
|
|
445
|
+
}
|
|
446
|
+
return false
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** The next value opens an array; anything else is `JSON_DECODE_TYPE`. */
|
|
450
|
+
beginArray(): boolean {
|
|
451
|
+
const k: i32 = this.take()
|
|
452
|
+
if (k === JSON_TOK_ARRAY_BEGIN) {
|
|
453
|
+
return true
|
|
454
|
+
}
|
|
455
|
+
if (k >= 0) {
|
|
456
|
+
this.wrongType()
|
|
457
|
+
}
|
|
458
|
+
return false
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/** Whether the open array has another element, whose first token is then held; `false` at its end and after any error. */
|
|
462
|
+
nextElement(): boolean {
|
|
463
|
+
if (this.failed !== JSON_DECODE_OK) {
|
|
464
|
+
return false
|
|
465
|
+
}
|
|
466
|
+
const k: i32 = this.tok.next()
|
|
467
|
+
if (k === JSON_TOK_ARRAY_END) {
|
|
468
|
+
return false
|
|
469
|
+
}
|
|
470
|
+
if (k < 0) {
|
|
471
|
+
this.fail(k, this.tok.errorAt)
|
|
472
|
+
return false
|
|
473
|
+
}
|
|
474
|
+
this.held = true
|
|
475
|
+
return true
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* The next value as an integer, from `lo` to `hi` when `bounded`, or `0`
|
|
480
|
+
* with an error. Every `i64` it answers is the tokenizer's or made with
|
|
481
|
+
* `toI64`, so that under Node, where an `i64` is a BigInt, what a caller
|
|
482
|
+
* computes with it never mixes a BigInt with a number.
|
|
483
|
+
*/
|
|
484
|
+
integer(lo: i64, hi: i64, bounded: boolean): i64 {
|
|
485
|
+
const k: i32 = this.take()
|
|
486
|
+
if (k !== JSON_TOK_NUMBER) {
|
|
487
|
+
if (k >= 0) {
|
|
488
|
+
this.wrongType()
|
|
489
|
+
}
|
|
490
|
+
return toI64(0)
|
|
491
|
+
}
|
|
492
|
+
if (!this.tok.isInteger()) {
|
|
493
|
+
this.fail(JSON_DECODE_RANGE, this.tok.tokStart)
|
|
494
|
+
return toI64(0)
|
|
495
|
+
}
|
|
496
|
+
const v: i64 = this.tok.numberI64()
|
|
497
|
+
if (bounded && (v < lo || v > hi)) {
|
|
498
|
+
this.fail(JSON_DECODE_RANGE, this.tok.tokStart)
|
|
499
|
+
return toI64(0)
|
|
500
|
+
}
|
|
501
|
+
return v
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
i32(): i32 {
|
|
505
|
+
const lo: i64 = -2147483648
|
|
506
|
+
const hi: i64 = 2147483647
|
|
507
|
+
return toI32(this.integer(lo, hi, true))
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/** The bounds are compared and never computed with, so under Node a number beside a BigInt is fine. */
|
|
511
|
+
u32(): u32 {
|
|
512
|
+
const lo: i64 = 0
|
|
513
|
+
const hi: i64 = 4294967295
|
|
514
|
+
// Through `f64`, which holds every `u32` exactly: under Node `toU32` takes
|
|
515
|
+
// no BigInt, and the `i64` the tokenizer answers is one there.
|
|
516
|
+
return toU32(toF64(this.integer(lo, hi, true)))
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/** Any integer `isInteger` accepts fits an `i64`, so there is no range to check. */
|
|
520
|
+
i64(): i64 {
|
|
521
|
+
return this.integer(toI64(0), toI64(0), false)
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* The next value as a `u64`: an integer written in digits alone, with no
|
|
526
|
+
* sign, fraction or exponent, up to 2^64 - 1, read through the tokenizer's
|
|
527
|
+
* `numberU64`. A negative number, `-0` included, and one past 2^64 - 1 are
|
|
528
|
+
* `JSON_DECODE_RANGE`, as a fraction is; `0` with an error.
|
|
529
|
+
*/
|
|
530
|
+
u64(): u64 {
|
|
531
|
+
const k: i32 = this.take()
|
|
532
|
+
if (k !== JSON_TOK_NUMBER) {
|
|
533
|
+
if (k >= 0) {
|
|
534
|
+
this.wrongType()
|
|
535
|
+
}
|
|
536
|
+
return toU64(0)
|
|
537
|
+
}
|
|
538
|
+
if (!this.tok.isU64()) {
|
|
539
|
+
this.fail(JSON_DECODE_RANGE, this.tok.tokStart)
|
|
540
|
+
return toU64(0)
|
|
541
|
+
}
|
|
542
|
+
return this.tok.numberU64()
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* The next value as a `JsonNumber`: its text, copied into `n`'s bytes
|
|
547
|
+
* exactly as the input writes it. Anything but a number is
|
|
548
|
+
* `JSON_DECODE_TYPE`, and `n` keeps the number it held.
|
|
549
|
+
*/
|
|
550
|
+
number(n: JsonNumber): void {
|
|
551
|
+
const k: i32 = this.take()
|
|
552
|
+
if (k !== JSON_TOK_NUMBER) {
|
|
553
|
+
if (k >= 0) {
|
|
554
|
+
this.wrongType()
|
|
555
|
+
}
|
|
556
|
+
return
|
|
557
|
+
}
|
|
558
|
+
const tok: JsonTokenizer = this.tok
|
|
559
|
+
const src: u8[] = tok.src
|
|
560
|
+
const bytes: u8[] = n.bytes
|
|
561
|
+
const start: i32 = tok.tokStart
|
|
562
|
+
const end: i32 = tok.tokEnd
|
|
563
|
+
let copied: i32 = 0
|
|
564
|
+
for (let i: i32 = start; i >= 0 && i < end && i < toI32(src.length); i += 1) {
|
|
565
|
+
// Read into a local first: a push ends what the guard proved about `i`.
|
|
566
|
+
const b: u8 = src[i]
|
|
567
|
+
if (copied < toI32(bytes.length)) {
|
|
568
|
+
bytes[copied] = b
|
|
569
|
+
} else {
|
|
570
|
+
bytes.push(b)
|
|
571
|
+
}
|
|
572
|
+
copied += 1
|
|
573
|
+
}
|
|
574
|
+
n.count = copied
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
f64(): f64 {
|
|
578
|
+
const k: i32 = this.take()
|
|
579
|
+
if (k === JSON_TOK_NUMBER) {
|
|
580
|
+
return this.tok.numberF64()
|
|
581
|
+
}
|
|
582
|
+
if (k >= 0) {
|
|
583
|
+
this.wrongType()
|
|
584
|
+
}
|
|
585
|
+
return 0
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
boolean(): boolean {
|
|
589
|
+
const k: i32 = this.take()
|
|
590
|
+
if (k === JSON_TOK_TRUE) {
|
|
591
|
+
return true
|
|
592
|
+
}
|
|
593
|
+
if (k !== JSON_TOK_FALSE && k >= 0) {
|
|
594
|
+
this.wrongType()
|
|
595
|
+
}
|
|
596
|
+
return false
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* The next value as a string: `cur` itself when the input's string is equal
|
|
601
|
+
* to it, which allocates nothing, and a new string otherwise; `cur` with an
|
|
602
|
+
* error.
|
|
603
|
+
*/
|
|
604
|
+
string(cur: string): string {
|
|
605
|
+
const k: i32 = this.take()
|
|
606
|
+
if (k !== JSON_TOK_STRING) {
|
|
607
|
+
if (k >= 0) {
|
|
608
|
+
this.wrongType()
|
|
609
|
+
}
|
|
610
|
+
return cur
|
|
611
|
+
}
|
|
612
|
+
if (this.tok.keyEquals(cur)) {
|
|
613
|
+
return cur
|
|
614
|
+
}
|
|
615
|
+
return this.text()
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Arrays of scalars and strings: each element is read into the slot the
|
|
619
|
+
// array already has, or pushed past its end, and what is left over is
|
|
620
|
+
// popped, so an array of the same length grows nothing.
|
|
621
|
+
|
|
622
|
+
i32Array(xs: i32[]): i32[] {
|
|
623
|
+
if (!this.beginArray()) {
|
|
624
|
+
return xs
|
|
625
|
+
}
|
|
626
|
+
let n: i32 = 0
|
|
627
|
+
while (this.nextElement()) {
|
|
628
|
+
const v: i32 = this.i32()
|
|
629
|
+
if (n < toI32(xs.length)) {
|
|
630
|
+
xs[n] = v
|
|
631
|
+
} else {
|
|
632
|
+
xs.push(v)
|
|
633
|
+
}
|
|
634
|
+
n += 1
|
|
635
|
+
}
|
|
636
|
+
while (toI32(xs.length) > n && xs.length > 0) {
|
|
637
|
+
xs.pop()
|
|
638
|
+
}
|
|
639
|
+
return xs
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
u32Array(xs: u32[]): u32[] {
|
|
643
|
+
if (!this.beginArray()) {
|
|
644
|
+
return xs
|
|
645
|
+
}
|
|
646
|
+
let n: i32 = 0
|
|
647
|
+
while (this.nextElement()) {
|
|
648
|
+
const v: u32 = this.u32()
|
|
649
|
+
if (n < toI32(xs.length)) {
|
|
650
|
+
xs[n] = v
|
|
651
|
+
} else {
|
|
652
|
+
xs.push(v)
|
|
653
|
+
}
|
|
654
|
+
n += 1
|
|
655
|
+
}
|
|
656
|
+
while (toI32(xs.length) > n && xs.length > 0) {
|
|
657
|
+
xs.pop()
|
|
658
|
+
}
|
|
659
|
+
return xs
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
i64Array(xs: i64[]): i64[] {
|
|
663
|
+
if (!this.beginArray()) {
|
|
664
|
+
return xs
|
|
665
|
+
}
|
|
666
|
+
let n: i32 = 0
|
|
667
|
+
while (this.nextElement()) {
|
|
668
|
+
const v: i64 = this.i64()
|
|
669
|
+
if (n < toI32(xs.length)) {
|
|
670
|
+
xs[n] = v
|
|
671
|
+
} else {
|
|
672
|
+
xs.push(v)
|
|
673
|
+
}
|
|
674
|
+
n += 1
|
|
675
|
+
}
|
|
676
|
+
while (toI32(xs.length) > n && xs.length > 0) {
|
|
677
|
+
xs.pop()
|
|
678
|
+
}
|
|
679
|
+
return xs
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
u64Array(xs: u64[]): u64[] {
|
|
683
|
+
if (!this.beginArray()) {
|
|
684
|
+
return xs
|
|
685
|
+
}
|
|
686
|
+
let n: i32 = 0
|
|
687
|
+
while (this.nextElement()) {
|
|
688
|
+
const v: u64 = this.u64()
|
|
689
|
+
if (n < toI32(xs.length)) {
|
|
690
|
+
xs[n] = v
|
|
691
|
+
} else {
|
|
692
|
+
xs.push(v)
|
|
693
|
+
}
|
|
694
|
+
n += 1
|
|
695
|
+
}
|
|
696
|
+
while (toI32(xs.length) > n && xs.length > 0) {
|
|
697
|
+
xs.pop()
|
|
698
|
+
}
|
|
699
|
+
return xs
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
f64Array(xs: f64[]): f64[] {
|
|
703
|
+
if (!this.beginArray()) {
|
|
704
|
+
return xs
|
|
705
|
+
}
|
|
706
|
+
let n: i32 = 0
|
|
707
|
+
while (this.nextElement()) {
|
|
708
|
+
const v: f64 = this.f64()
|
|
709
|
+
if (n < toI32(xs.length)) {
|
|
710
|
+
xs[n] = v
|
|
711
|
+
} else {
|
|
712
|
+
xs.push(v)
|
|
713
|
+
}
|
|
714
|
+
n += 1
|
|
715
|
+
}
|
|
716
|
+
while (toI32(xs.length) > n && xs.length > 0) {
|
|
717
|
+
xs.pop()
|
|
718
|
+
}
|
|
719
|
+
return xs
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
booleanArray(xs: boolean[]): boolean[] {
|
|
723
|
+
if (!this.beginArray()) {
|
|
724
|
+
return xs
|
|
725
|
+
}
|
|
726
|
+
let n: i32 = 0
|
|
727
|
+
while (this.nextElement()) {
|
|
728
|
+
const v: boolean = this.boolean()
|
|
729
|
+
if (n < toI32(xs.length)) {
|
|
730
|
+
xs[n] = v
|
|
731
|
+
} else {
|
|
732
|
+
xs.push(v)
|
|
733
|
+
}
|
|
734
|
+
n += 1
|
|
735
|
+
}
|
|
736
|
+
while (toI32(xs.length) > n && xs.length > 0) {
|
|
737
|
+
xs.pop()
|
|
738
|
+
}
|
|
739
|
+
return xs
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
stringArray(xs: string[]): string[] {
|
|
743
|
+
if (!this.beginArray()) {
|
|
744
|
+
return xs
|
|
745
|
+
}
|
|
746
|
+
let n: i32 = 0
|
|
747
|
+
while (this.nextElement()) {
|
|
748
|
+
let cur: string = ""
|
|
749
|
+
if (n < toI32(xs.length)) {
|
|
750
|
+
cur = xs[n]
|
|
751
|
+
}
|
|
752
|
+
// Read into a local first: the call ends what the guard proved about `n`.
|
|
753
|
+
const v: string = this.string(cur)
|
|
754
|
+
if (n < toI32(xs.length)) {
|
|
755
|
+
xs[n] = v
|
|
756
|
+
} else {
|
|
757
|
+
xs.push(v)
|
|
758
|
+
}
|
|
759
|
+
n += 1
|
|
760
|
+
}
|
|
761
|
+
while (toI32(xs.length) > n && xs.length > 0) {
|
|
762
|
+
xs.pop()
|
|
763
|
+
}
|
|
764
|
+
return xs
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
i32ArrayOrNull(xs: i32[] | null): i32[] | null {
|
|
768
|
+
if (this.takeNull()) {
|
|
769
|
+
return null
|
|
770
|
+
}
|
|
771
|
+
if (xs !== null) {
|
|
772
|
+
return this.i32Array(xs)
|
|
773
|
+
}
|
|
774
|
+
const fresh: i32[] = []
|
|
775
|
+
return this.i32Array(fresh)
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
u32ArrayOrNull(xs: u32[] | null): u32[] | null {
|
|
779
|
+
if (this.takeNull()) {
|
|
780
|
+
return null
|
|
781
|
+
}
|
|
782
|
+
if (xs !== null) {
|
|
783
|
+
return this.u32Array(xs)
|
|
784
|
+
}
|
|
785
|
+
const fresh: u32[] = []
|
|
786
|
+
return this.u32Array(fresh)
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
i64ArrayOrNull(xs: i64[] | null): i64[] | null {
|
|
790
|
+
if (this.takeNull()) {
|
|
791
|
+
return null
|
|
792
|
+
}
|
|
793
|
+
if (xs !== null) {
|
|
794
|
+
return this.i64Array(xs)
|
|
795
|
+
}
|
|
796
|
+
const fresh: i64[] = []
|
|
797
|
+
return this.i64Array(fresh)
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
u64ArrayOrNull(xs: u64[] | null): u64[] | null {
|
|
801
|
+
if (this.takeNull()) {
|
|
802
|
+
return null
|
|
803
|
+
}
|
|
804
|
+
if (xs !== null) {
|
|
805
|
+
return this.u64Array(xs)
|
|
806
|
+
}
|
|
807
|
+
const fresh: u64[] = []
|
|
808
|
+
return this.u64Array(fresh)
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
f64ArrayOrNull(xs: f64[] | null): f64[] | null {
|
|
812
|
+
if (this.takeNull()) {
|
|
813
|
+
return null
|
|
814
|
+
}
|
|
815
|
+
if (xs !== null) {
|
|
816
|
+
return this.f64Array(xs)
|
|
817
|
+
}
|
|
818
|
+
const fresh: f64[] = []
|
|
819
|
+
return this.f64Array(fresh)
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
booleanArrayOrNull(xs: boolean[] | null): boolean[] | null {
|
|
823
|
+
if (this.takeNull()) {
|
|
824
|
+
return null
|
|
825
|
+
}
|
|
826
|
+
if (xs !== null) {
|
|
827
|
+
return this.booleanArray(xs)
|
|
828
|
+
}
|
|
829
|
+
const fresh: boolean[] = []
|
|
830
|
+
return this.booleanArray(fresh)
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
stringArrayOrNull(xs: string[] | null): string[] | null {
|
|
834
|
+
if (this.takeNull()) {
|
|
835
|
+
return null
|
|
836
|
+
}
|
|
837
|
+
if (xs !== null) {
|
|
838
|
+
return this.stringArray(xs)
|
|
839
|
+
}
|
|
840
|
+
const fresh: string[] = []
|
|
841
|
+
return this.stringArray(fresh)
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Decodes the JSON object `d` holds into `out`, the whole input, and answers
|
|
847
|
+
* `d.failed`: `0`, or the first error's code. The compiler writes the code
|
|
848
|
+
* from `T`'s fields at every call; this body is what Node runs.
|
|
849
|
+
*/
|
|
850
|
+
export const jsonDecode = <T>(_d: JsonDecoder, _out: T): i32 => {
|
|
851
|
+
panic(JSON_TYPED_ERASED)
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Writes `value` to `w` as one JSON object, its fields in declaration order.
|
|
856
|
+
* The compiler writes the code from `T`'s fields at every call; this body is
|
|
857
|
+
* what Node runs.
|
|
858
|
+
*/
|
|
859
|
+
export const jsonEncode = <T>(_w: JsonWriter, _value: T): void => {
|
|
860
|
+
panic(JSON_TYPED_ERASED)
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// What an encoding calls for a field that is not one writer call. Exported
|
|
864
|
+
// because the code the compiler writes calls them from the module that
|
|
865
|
+
// encodes, which may not be this one.
|
|
866
|
+
|
|
867
|
+
export const jsonEncodeStringOrNull = (w: JsonWriter, s: string | null): void => {
|
|
868
|
+
if (s === null) {
|
|
869
|
+
w.nullValue()
|
|
870
|
+
} else {
|
|
871
|
+
w.string(s)
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* A `u64`, as its digits: at most 20 of them, written with `raw` from an
|
|
877
|
+
* array of this call's own that nothing keeps. A value past 2^63 has no
|
|
878
|
+
* `i64` to go through the writer's `i64` as, so every `u64` takes this one
|
|
879
|
+
* loop. Every `u64` here is built with `toU64`, a BigInt under Node.
|
|
880
|
+
*/
|
|
881
|
+
export const jsonEncodeU64 = (w: JsonWriter, v: u64): void => {
|
|
882
|
+
const ten: u64 = toU64(10)
|
|
883
|
+
const zero: u64 = toU64(0)
|
|
884
|
+
const digits: u8[] = new Array<u8>(20)
|
|
885
|
+
let rest: u64 = v
|
|
886
|
+
let first: i32 = toI32(digits.length)
|
|
887
|
+
for (let k: i32 = first - 1; k >= 0 && k < toI32(digits.length); k -= 1) {
|
|
888
|
+
digits[k] = toU8(48 + toI32(rest % ten))
|
|
889
|
+
rest = rest / ten
|
|
890
|
+
first = k
|
|
891
|
+
if (rest === zero) {
|
|
892
|
+
break
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
w.raw(digits, first, toI32(digits.length) - first)
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/** A `JsonNumber`, as the text it holds, byte for byte. */
|
|
899
|
+
export const jsonEncodeNumber = (w: JsonWriter, n: JsonNumber): void => {
|
|
900
|
+
const start: i32 = 0
|
|
901
|
+
w.raw(n.bytes, start, n.count)
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
export const jsonEncodeI32Array = (w: JsonWriter, xs: i32[]): void => {
|
|
905
|
+
w.beginArray()
|
|
906
|
+
for (const x of xs) {
|
|
907
|
+
w.i32(x)
|
|
908
|
+
}
|
|
909
|
+
w.endArray()
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
export const jsonEncodeU32Array = (w: JsonWriter, xs: u32[]): void => {
|
|
913
|
+
w.beginArray()
|
|
914
|
+
for (const x of xs) {
|
|
915
|
+
w.u32(x)
|
|
916
|
+
}
|
|
917
|
+
w.endArray()
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
export const jsonEncodeI64Array = (w: JsonWriter, xs: i64[]): void => {
|
|
921
|
+
w.beginArray()
|
|
922
|
+
for (const x of xs) {
|
|
923
|
+
w.i64(x)
|
|
924
|
+
}
|
|
925
|
+
w.endArray()
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
export const jsonEncodeU64Array = (w: JsonWriter, xs: u64[]): void => {
|
|
929
|
+
w.beginArray()
|
|
930
|
+
for (const x of xs) {
|
|
931
|
+
jsonEncodeU64(w, x)
|
|
932
|
+
}
|
|
933
|
+
w.endArray()
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
export const jsonEncodeF64Array = (w: JsonWriter, xs: f64[]): void => {
|
|
937
|
+
w.beginArray()
|
|
938
|
+
for (const x of xs) {
|
|
939
|
+
w.f64(x)
|
|
940
|
+
}
|
|
941
|
+
w.endArray()
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
export const jsonEncodeBooleanArray = (w: JsonWriter, xs: boolean[]): void => {
|
|
945
|
+
w.beginArray()
|
|
946
|
+
for (const x of xs) {
|
|
947
|
+
w.boolean(x)
|
|
948
|
+
}
|
|
949
|
+
w.endArray()
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
export const jsonEncodeStringArray = (w: JsonWriter, xs: string[]): void => {
|
|
953
|
+
w.beginArray()
|
|
954
|
+
for (const x of xs) {
|
|
955
|
+
w.string(x)
|
|
956
|
+
}
|
|
957
|
+
w.endArray()
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
export const jsonEncodeI32ArrayOrNull = (w: JsonWriter, xs: i32[] | null): void => {
|
|
961
|
+
if (xs === null) {
|
|
962
|
+
w.nullValue()
|
|
963
|
+
} else {
|
|
964
|
+
jsonEncodeI32Array(w, xs)
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
export const jsonEncodeU32ArrayOrNull = (w: JsonWriter, xs: u32[] | null): void => {
|
|
969
|
+
if (xs === null) {
|
|
970
|
+
w.nullValue()
|
|
971
|
+
} else {
|
|
972
|
+
jsonEncodeU32Array(w, xs)
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
export const jsonEncodeI64ArrayOrNull = (w: JsonWriter, xs: i64[] | null): void => {
|
|
977
|
+
if (xs === null) {
|
|
978
|
+
w.nullValue()
|
|
979
|
+
} else {
|
|
980
|
+
jsonEncodeI64Array(w, xs)
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
export const jsonEncodeU64ArrayOrNull = (w: JsonWriter, xs: u64[] | null): void => {
|
|
985
|
+
if (xs === null) {
|
|
986
|
+
w.nullValue()
|
|
987
|
+
} else {
|
|
988
|
+
jsonEncodeU64Array(w, xs)
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
export const jsonEncodeF64ArrayOrNull = (w: JsonWriter, xs: f64[] | null): void => {
|
|
993
|
+
if (xs === null) {
|
|
994
|
+
w.nullValue()
|
|
995
|
+
} else {
|
|
996
|
+
jsonEncodeF64Array(w, xs)
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
export const jsonEncodeBooleanArrayOrNull = (w: JsonWriter, xs: boolean[] | null): void => {
|
|
1001
|
+
if (xs === null) {
|
|
1002
|
+
w.nullValue()
|
|
1003
|
+
} else {
|
|
1004
|
+
jsonEncodeBooleanArray(w, xs)
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
export const jsonEncodeStringArrayOrNull = (w: JsonWriter, xs: string[] | null): void => {
|
|
1009
|
+
if (xs === null) {
|
|
1010
|
+
w.nullValue()
|
|
1011
|
+
} else {
|
|
1012
|
+
jsonEncodeStringArray(w, xs)
|
|
1013
|
+
}
|
|
1014
|
+
}
|