@amritk/nish-x86_64-linux 0.16.0 → 0.18.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 -34
- package/bin/nish +0 -0
- package/package.json +1 -1
- package/runtime/nish.d.ts +117 -8
- package/runtime/nish.h +50 -13
- package/runtime/nish.mjs +71 -7
- package/runtime/runtime-host.c +43 -24
- package/runtime/runtime-net.c +57 -10
- package/runtime/runtime-os.c +62 -20
- package/runtime/runtime-simd.c +203 -0
- package/runtime/runtime-wasm.c +12 -0
- package/runtime/runtime.c +66 -18
- package/runtime/shim.mjs +114 -5
- package/scripts/build.sh +14 -12
- package/std/README.md +168 -36
- package/std/crypto/aes.ts +51 -8
- package/std/crypto/base64url.ts +25 -12
- package/std/crypto/chacha20poly1305.ts +43 -29
- package/std/crypto/ct.ts +6 -1
- package/std/crypto/hkdf.ts +364 -3
- package/std/crypto/hmac.ts +307 -7
- package/std/crypto/p256.ts +618 -34
- package/std/crypto/sha1.ts +147 -0
- package/std/crypto/sha256.ts +3 -2
- package/std/crypto/x25519.ts +88 -36
- package/std/crypto/x509.ts +130 -47
- package/std/json.ts +297 -41
- package/std/net/hpack.ts +1094 -0
- package/std/net/http-fields.ts +604 -0
- package/std/net/http1-server.ts +1389 -0
- package/std/net/http1.ts +1492 -0
- package/std/net/http2-frame.ts +745 -0
- package/std/net/http2-tls.ts +216 -0
- package/std/net/http2.ts +1433 -0
- package/std/net/http3-frame.ts +457 -0
- package/std/net/http3-server.ts +911 -0
- package/std/net/http3.ts +2145 -0
- package/std/net/qpack.ts +1163 -0
- package/std/net/quic-conn-ack.ts +206 -0
- package/std/net/quic-conn-cid.ts +425 -0
- package/std/net/quic-conn-params.ts +462 -0
- package/std/net/quic-datagram.ts +130 -0
- package/std/net/quic-frame.ts +1206 -0
- package/std/net/quic-listener.ts +929 -0
- package/std/net/quic-packet.ts +1698 -0
- package/std/net/quic-recovery.ts +849 -0
- package/std/net/quic-stream.ts +1609 -0
- package/std/net/quic.ts +3578 -0
- package/std/net/tls/codec.ts +1089 -0
- package/std/net/tls/record-server.ts +1173 -0
- package/std/net/tls/record.ts +621 -0
- package/std/net/tls/schedule.ts +429 -0
- package/std/net/tls-tcp.ts +414 -0
- package/std/net/tls.ts +1437 -0
- package/std/net/websocket.ts +823 -0
- package/std/net/webtransport.ts +1334 -0
- package/std/secret.ts +62 -0
- package/std/text.ts +77 -0
package/std/json.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `std/json` — the value of
|
|
2
|
+
* `std/json` — the value of a field, or of several, of one flat JSON object.
|
|
3
3
|
*
|
|
4
4
|
* It exists because the compiler's own machine-readable surface is JSON: under
|
|
5
5
|
* `--json` every diagnostic is one flat object on a line of stdout, whose `code`
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
* whether the whole line was well-formed is asking a question this does not
|
|
27
27
|
* answer.
|
|
28
28
|
*
|
|
29
|
-
* There
|
|
30
|
-
* the program that imports it and the symbol namespace is flat
|
|
29
|
+
* There are two exported functions, and few on purpose. A `std/` module is
|
|
30
|
+
* compiled into the program that imports it and the symbol namespace is flat
|
|
31
31
|
* (`std/README.md`), so every name here is a name its importer cannot use: the
|
|
32
|
-
* helpers are `json`-prefixed and the
|
|
32
|
+
* helpers are `json`-prefixed and the readers are the only exports. Splitting a
|
|
33
33
|
* stream into lines and picking the ones that start with `{` is two calls the
|
|
34
34
|
* caller already has — `splitLines` from `std/text` and `startsWith` — and
|
|
35
35
|
* duplicating them here would cost more names than it saves.
|
|
@@ -46,6 +46,16 @@
|
|
|
46
46
|
* }
|
|
47
47
|
* }
|
|
48
48
|
*
|
|
49
|
+
* `jsonField` scans from the start of the line on every call, so a caller that
|
|
50
|
+
* wants three fields of each line reads each line three times. `jsonFields`
|
|
51
|
+
* reads it once: `jsonFields(line, ["code", "line", "message"])` answers an
|
|
52
|
+
* array whose slot `k` is exactly what `jsonField(line, names[k])` answers —
|
|
53
|
+
* absent, first-of-a-duplicate and malformed input included — because the two
|
|
54
|
+
* share the step that reads one member (`jsonReadMember`) and stop at the same
|
|
55
|
+
* one. Its array goes back to the caller with the values in it, so a loop that
|
|
56
|
+
* calls it keeps its per-pass arena release, as one calling `jsonField` does;
|
|
57
|
+
* `jsonFields`' own comment says when that holds.
|
|
58
|
+
*
|
|
49
59
|
* Every offset here is a **byte** offset and every width is spelled, with each
|
|
50
60
|
* length and byte a builtin answers read through `toI32` — `.length` and
|
|
51
61
|
* `charCodeAt` answer `number`, which is `f64` under `--number-mode f64`, so the
|
|
@@ -53,14 +63,17 @@
|
|
|
53
63
|
* (`docs/wp26-stdlib.md` §4).
|
|
54
64
|
*/
|
|
55
65
|
|
|
66
|
+
const JSON_BACKSPACE: i32 = 8
|
|
56
67
|
const JSON_TAB: i32 = 9
|
|
57
68
|
const JSON_NEWLINE: i32 = 10
|
|
69
|
+
const JSON_FORM_FEED: i32 = 12
|
|
58
70
|
const JSON_CARRIAGE_RETURN: i32 = 13
|
|
59
71
|
const JSON_SPACE: i32 = 32
|
|
60
72
|
const JSON_QUOTE: i32 = 34
|
|
61
73
|
const JSON_COMMA: i32 = 44
|
|
62
74
|
const JSON_COLON: i32 = 58
|
|
63
75
|
const JSON_BACKSLASH: i32 = 92
|
|
76
|
+
const JSON_LETTER_U: i32 = 117
|
|
64
77
|
const JSON_OPEN_BRACKET: i32 = 91
|
|
65
78
|
const JSON_CLOSE_BRACKET: i32 = 93
|
|
66
79
|
const JSON_OPEN_BRACE: i32 = 123
|
|
@@ -261,6 +274,37 @@ const jsonUtf8 = (code: i32): string => {
|
|
|
261
274
|
return `${lead}${middle}${String.fromCharCode(JSON_UTF8_CONTINUATION + (code & JSON_UTF8_LOW_SIX))}`
|
|
262
275
|
}
|
|
263
276
|
|
|
277
|
+
/** The number of bytes `jsonUtf8(code)` answers. */
|
|
278
|
+
const jsonUtf8Width = (code: i32): i32 => {
|
|
279
|
+
if (code < JSON_UTF8_TWO_BYTE_FLOOR) {
|
|
280
|
+
return 1
|
|
281
|
+
}
|
|
282
|
+
if (code < JSON_UTF8_THREE_BYTE_FLOOR) {
|
|
283
|
+
return 2
|
|
284
|
+
}
|
|
285
|
+
return 3
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Byte `k` of the `width` bytes `jsonUtf8(code)` answers: its arithmetic, one
|
|
290
|
+
* byte at a time, for a caller that compares the bytes rather than keeps them.
|
|
291
|
+
*/
|
|
292
|
+
const jsonUtf8Byte = (code: i32, width: i32, k: i32): i32 => {
|
|
293
|
+
if (width === 1) {
|
|
294
|
+
return code
|
|
295
|
+
}
|
|
296
|
+
if (k === 0) {
|
|
297
|
+
if (width === 2) {
|
|
298
|
+
return JSON_UTF8_TWO_BYTE_LEAD + (code >> 6)
|
|
299
|
+
}
|
|
300
|
+
return JSON_UTF8_THREE_BYTE_LEAD + (code >> 12)
|
|
301
|
+
}
|
|
302
|
+
if (k === width - 1) {
|
|
303
|
+
return JSON_UTF8_CONTINUATION + (code & JSON_UTF8_LOW_SIX)
|
|
304
|
+
}
|
|
305
|
+
return JSON_UTF8_CONTINUATION + ((code >> 6) & JSON_UTF8_LOW_SIX)
|
|
306
|
+
}
|
|
307
|
+
|
|
264
308
|
/**
|
|
265
309
|
* The bytes of `text[at..end)` with the JSON escapes decoded — the body of a
|
|
266
310
|
* string literal, without its quotes.
|
|
@@ -343,6 +387,190 @@ const jsonUnescape = (text: string, at: i32, end: i32): string => {
|
|
|
343
387
|
return parts.join("")
|
|
344
388
|
}
|
|
345
389
|
|
|
390
|
+
/** Whether byte `j` of `name` is `code`; an index past either end is not. */
|
|
391
|
+
const jsonNameHas = (name: string, j: i32, code: i32): boolean =>
|
|
392
|
+
j >= 0 && j < toI32(name.length) && toI32(name.charCodeAt(j)) === code
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Whether `jsonUnescape(text, at, end)` would answer `name`, decided without
|
|
396
|
+
* building it.
|
|
397
|
+
*
|
|
398
|
+
* `jsonField` and `jsonFields` ask this of every key they walk past, and almost
|
|
399
|
+
* every answer is no, so the question is answered the way it is asked: the literal and `name`
|
|
400
|
+
* are walked together, each escape decoded where it stands, and the first byte
|
|
401
|
+
* that differs ends the walk. Every escape reads as `jsonUnescape` reads it —
|
|
402
|
+
* the short forms, a `\u` as the bytes `jsonUtf8` would build, a broken `\u`
|
|
403
|
+
* as the two bytes `\u`, a backslash with no letter after it as itself, and
|
|
404
|
+
* any other escape as the byte it escapes — so a key matches here exactly when
|
|
405
|
+
* its decoded text would.
|
|
406
|
+
*/
|
|
407
|
+
const jsonKeyEquals = (text: string, at: i32, end: i32, name: string): boolean => {
|
|
408
|
+
// `jsonUnescape`'s guard, for the same reason: every caller passes the
|
|
409
|
+
// inside of a key `jsonReadMember` found with `jsonEndOfString`.
|
|
410
|
+
if (at < 0 || end > toI32(text.length)) {
|
|
411
|
+
return false
|
|
412
|
+
}
|
|
413
|
+
let i: i32 = at
|
|
414
|
+
let j: i32 = 0
|
|
415
|
+
while (i < end) {
|
|
416
|
+
const code: i32 = toI32(text.charCodeAt(i))
|
|
417
|
+
if (code !== JSON_BACKSLASH) {
|
|
418
|
+
if (!jsonNameHas(name, j, code)) {
|
|
419
|
+
return false
|
|
420
|
+
}
|
|
421
|
+
i += 1
|
|
422
|
+
j += 1
|
|
423
|
+
continue
|
|
424
|
+
}
|
|
425
|
+
const letter: i32 = i + 1 < end ? toI32(text.charCodeAt(i + 1)) : 0
|
|
426
|
+
if (letter === JSON_LETTER_U) {
|
|
427
|
+
const point: i32 = jsonHex4(text, i + 2, end)
|
|
428
|
+
if (point < 0) {
|
|
429
|
+
if (!jsonNameHas(name, j, JSON_BACKSLASH) || !jsonNameHas(name, j + 1, JSON_LETTER_U)) {
|
|
430
|
+
return false
|
|
431
|
+
}
|
|
432
|
+
i += 2
|
|
433
|
+
j += 2
|
|
434
|
+
continue
|
|
435
|
+
}
|
|
436
|
+
const width: i32 = jsonUtf8Width(point)
|
|
437
|
+
let k: i32 = 0
|
|
438
|
+
while (k < width) {
|
|
439
|
+
if (!jsonNameHas(name, j + k, jsonUtf8Byte(point, width, k))) {
|
|
440
|
+
return false
|
|
441
|
+
}
|
|
442
|
+
k += 1
|
|
443
|
+
}
|
|
444
|
+
i += 6
|
|
445
|
+
j += width
|
|
446
|
+
continue
|
|
447
|
+
}
|
|
448
|
+
// Every other escape decodes to one byte.
|
|
449
|
+
let decoded: i32 = letter
|
|
450
|
+
if (letter === 110) {
|
|
451
|
+
decoded = JSON_NEWLINE
|
|
452
|
+
} else if (letter === 116) {
|
|
453
|
+
decoded = JSON_TAB
|
|
454
|
+
} else if (letter === 114) {
|
|
455
|
+
decoded = JSON_CARRIAGE_RETURN
|
|
456
|
+
} else if (letter === 98) {
|
|
457
|
+
decoded = JSON_BACKSPACE
|
|
458
|
+
} else if (letter === 102) {
|
|
459
|
+
decoded = JSON_FORM_FEED
|
|
460
|
+
} else if (letter === 0) {
|
|
461
|
+
decoded = JSON_BACKSLASH
|
|
462
|
+
}
|
|
463
|
+
if (!jsonNameHas(name, j, decoded)) {
|
|
464
|
+
return false
|
|
465
|
+
}
|
|
466
|
+
// A backslash with no letter after it is one byte of the literal and every
|
|
467
|
+
// other escape two. The steps are constants rather than a variable because
|
|
468
|
+
// a cursor moved by a variable is one the bounds proof stops following.
|
|
469
|
+
if (letter === 0) {
|
|
470
|
+
i += 1
|
|
471
|
+
} else {
|
|
472
|
+
i += 2
|
|
473
|
+
}
|
|
474
|
+
j += 1
|
|
475
|
+
}
|
|
476
|
+
return j === toI32(name.length)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Where one member of an object lies, as `jsonReadMember` found it: the inside
|
|
481
|
+
* of its key literal, its value, and the opening quote of the key after it.
|
|
482
|
+
*
|
|
483
|
+
* It is a class so that one step can answer the four offsets together, and
|
|
484
|
+
* each reader allocates exactly one, which never leaves the reader: that is
|
|
485
|
+
* what keeps it a stack slot rather than an arena allocation.
|
|
486
|
+
*/
|
|
487
|
+
class JsonMember {
|
|
488
|
+
keyAt: i32 = 0
|
|
489
|
+
keyEnd: i32 = 0
|
|
490
|
+
valueAt: i32 = 0
|
|
491
|
+
valueEnd: i32 = 0
|
|
492
|
+
/** The opening quote of the next key, or `-1` when no comma follows the value. */
|
|
493
|
+
next: i32 = -1
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* The opening quote of the first key of `object`, or `-1` when `object` does
|
|
498
|
+
* not start, past any blanks, with a `{`.
|
|
499
|
+
*/
|
|
500
|
+
const jsonFirstKey = (object: string): i32 => {
|
|
501
|
+
const length: i32 = toI32(object.length)
|
|
502
|
+
const i: i32 = jsonSkipBlank(object, 0)
|
|
503
|
+
if (i >= length || toI32(object.charCodeAt(i)) !== JSON_OPEN_BRACE) {
|
|
504
|
+
return -1
|
|
505
|
+
}
|
|
506
|
+
return jsonSkipBlank(object, i + 1)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Reads the member whose key's opening quote is at `at` into `member`, and
|
|
511
|
+
* answers whether there was one.
|
|
512
|
+
*
|
|
513
|
+
* This is the per-key step `jsonField` and `jsonFields` share, so they cannot
|
|
514
|
+
* disagree about where a member ends: `false` for anything that is not a key,
|
|
515
|
+
* a colon and a whole value in that order, which is the point at which both
|
|
516
|
+
* stop. A value need not be followed by a comma — a truncated object still
|
|
517
|
+
* answers the fields before the cut — so a missing one only sets `next` to
|
|
518
|
+
* `-1`, and the read that follows answers `false`.
|
|
519
|
+
*/
|
|
520
|
+
const jsonReadMember = (object: string, at: i32, member: JsonMember): boolean => {
|
|
521
|
+
const length: i32 = toI32(object.length)
|
|
522
|
+
// `jsonSkipBlank` never answers a negative index for a non-negative one, but
|
|
523
|
+
// the bounds proof does not look inside a callee, so every cursor it answers
|
|
524
|
+
// is tested for `i < 0` beside `i >= length`: the pair is what lets each read
|
|
525
|
+
// below go without a check.
|
|
526
|
+
if (at < 0 || at >= length || toI32(object.charCodeAt(at)) !== JSON_QUOTE) {
|
|
527
|
+
return false
|
|
528
|
+
}
|
|
529
|
+
const keyEnd: i32 = jsonEndOfString(object, at)
|
|
530
|
+
if (keyEnd < 0) {
|
|
531
|
+
return false
|
|
532
|
+
}
|
|
533
|
+
const colon: i32 = jsonSkipBlank(object, keyEnd)
|
|
534
|
+
if (colon < 0 || colon >= length || toI32(object.charCodeAt(colon)) !== JSON_COLON) {
|
|
535
|
+
return false
|
|
536
|
+
}
|
|
537
|
+
const valueAt: i32 = jsonSkipBlank(object, colon + 1)
|
|
538
|
+
const valueEnd: i32 = jsonEndOfValue(object, valueAt)
|
|
539
|
+
if (valueEnd < 0) {
|
|
540
|
+
return false
|
|
541
|
+
}
|
|
542
|
+
member.keyAt = at + 1
|
|
543
|
+
member.keyEnd = keyEnd - 1
|
|
544
|
+
member.valueAt = valueAt
|
|
545
|
+
member.valueEnd = valueEnd
|
|
546
|
+
const comma: i32 = jsonSkipBlank(object, valueEnd)
|
|
547
|
+
if (comma < 0 || comma >= length || toI32(object.charCodeAt(comma)) !== JSON_COMMA) {
|
|
548
|
+
member.next = -1
|
|
549
|
+
} else {
|
|
550
|
+
member.next = jsonSkipBlank(object, comma + 1)
|
|
551
|
+
}
|
|
552
|
+
return true
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* The text a member's value answers: a string unquoted and unescaped, anything
|
|
557
|
+
* else as the bytes it was written with.
|
|
558
|
+
*/
|
|
559
|
+
const jsonValueText = (object: string, member: JsonMember): string => {
|
|
560
|
+
const valueAt: i32 = member.valueAt
|
|
561
|
+
const valueEnd: i32 = member.valueEnd
|
|
562
|
+
// `jsonReadMember` only fills in a value `jsonEndOfValue` ended, which starts
|
|
563
|
+
// inside the object and never ends past it, so this changes no answer: it is
|
|
564
|
+
// the range the value's first byte and its `substring` are proved in.
|
|
565
|
+
if (valueAt < 0 || valueAt >= valueEnd || valueEnd > toI32(object.length)) {
|
|
566
|
+
return ""
|
|
567
|
+
}
|
|
568
|
+
if (toI32(object.charCodeAt(valueAt)) === JSON_QUOTE) {
|
|
569
|
+
return jsonUnescape(object, valueAt + 1, valueEnd - 1)
|
|
570
|
+
}
|
|
571
|
+
return object.substring(valueAt, valueEnd)
|
|
572
|
+
}
|
|
573
|
+
|
|
346
574
|
/**
|
|
347
575
|
* The value of `name` in `object`, or `null` when the object does not have that
|
|
348
576
|
* field.
|
|
@@ -358,45 +586,73 @@ const jsonUnescape = (text: string, at: i32, end: i32): string => {
|
|
|
358
586
|
* and it is the shape the compiler's `--json` line has.
|
|
359
587
|
*/
|
|
360
588
|
export const jsonField = (object: string, name: string): string | null => {
|
|
361
|
-
const
|
|
362
|
-
let
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
// is tested for `i < 0` beside `i >= length`: the pair is what lets each read
|
|
370
|
-
// below go without a check.
|
|
371
|
-
while (i >= 0 && i < length && toI32(object.charCodeAt(i)) === JSON_QUOTE) {
|
|
372
|
-
const keyEnd: i32 = jsonEndOfString(object, i)
|
|
373
|
-
if (keyEnd < 0) {
|
|
374
|
-
return null
|
|
375
|
-
}
|
|
376
|
-
const key: string = jsonUnescape(object, i + 1, keyEnd - 1)
|
|
377
|
-
i = jsonSkipBlank(object, keyEnd)
|
|
378
|
-
if (i < 0 || i >= length || toI32(object.charCodeAt(i)) !== JSON_COLON) {
|
|
379
|
-
return null
|
|
589
|
+
const member: JsonMember = new JsonMember()
|
|
590
|
+
let at: i32 = jsonFirstKey(object)
|
|
591
|
+
while (jsonReadMember(object, at, member)) {
|
|
592
|
+
// The key is compared where it stands rather than unescaped into a string
|
|
593
|
+
// first: every key before the one asked for is a no, and a no should not
|
|
594
|
+
// cost an allocation.
|
|
595
|
+
if (jsonKeyEquals(object, member.keyAt, member.keyEnd, name)) {
|
|
596
|
+
return jsonValueText(object, member)
|
|
380
597
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
598
|
+
at = member.next
|
|
599
|
+
}
|
|
600
|
+
return null
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* The value of every one of `names` in `object`, in the order of `names`: slot
|
|
605
|
+
* `k` is exactly what `jsonField(object, names[k])` answers.
|
|
606
|
+
*
|
|
607
|
+
* It is for a caller that wants several fields of one line — a diagnostic's
|
|
608
|
+
* `code`, `line` and `message` — and would otherwise scan the line once per
|
|
609
|
+
* field. This scans it once, left to right, and stops as soon as every name has
|
|
610
|
+
* answered: a speed path and nothing more, since a slot is filled only once
|
|
611
|
+
* and reading on could change no answer. Each slot keeps the **first** field of its name, as `jsonField`
|
|
612
|
+
* does; a name asked for twice answers in both slots; and a malformed object
|
|
613
|
+
* answers in each slot what `jsonField` answers for that name, because both
|
|
614
|
+
* stop at the same member.
|
|
615
|
+
*
|
|
616
|
+
* The answer is a fresh array and the only allocation besides the values. Each
|
|
617
|
+
* value is stored into it by a plain `values[k] = text` statement, and the
|
|
618
|
+
* array is only stored into, tested and returned, so the values travel with
|
|
619
|
+
* the array: the call lets nothing else out (LANGUAGE.md, "Memory model", a
|
|
620
|
+
* value stored into an array that is returned with it). A loop calling this
|
|
621
|
+
* keeps its per-pass release on a line older than the pass, as `jsonField`'s
|
|
622
|
+
* does, and a `using a = arena()` block may hold the call. On a line built in
|
|
623
|
+
* the pass neither reader keeps the release (LANGUAGE.md, "Memory model").
|
|
624
|
+
* Reading `values` back, passing it to a function, or storing it anywhere
|
|
625
|
+
* before the return would undo this; `tests/link/std_json` pins the flat
|
|
626
|
+
* arena and the `using` block.
|
|
627
|
+
*/
|
|
628
|
+
export const jsonFields = (object: string, names: string[]): (string | null)[] => {
|
|
629
|
+
const count: i32 = toI32(names.length)
|
|
630
|
+
const values: (string | null)[] = new Array<string | null>(count)
|
|
631
|
+
const member: JsonMember = new JsonMember()
|
|
632
|
+
let missing: i32 = count
|
|
633
|
+
let at: i32 = jsonFirstKey(object)
|
|
634
|
+
while (missing > 0 && jsonReadMember(object, at, member)) {
|
|
635
|
+
// A value is built once per member however many names it answers, and
|
|
636
|
+
// only when one of them does.
|
|
637
|
+
let text: string | null = null
|
|
638
|
+
let k: i32 = 0
|
|
639
|
+
// Bounded by both lengths, which are the same, because the bounds proof
|
|
640
|
+
// follows each array by its own length and not by `count`.
|
|
641
|
+
while (k < toI32(values.length) && k < toI32(names.length)) {
|
|
642
|
+
if (values[k] === null && jsonKeyEquals(object, member.keyAt, member.keyEnd, names[k])) {
|
|
643
|
+
if (text === null) {
|
|
644
|
+
text = jsonValueText(object, member)
|
|
645
|
+
}
|
|
646
|
+
// Always true: the loop's own test. A call stands between it and the
|
|
647
|
+
// store, and a call is where the bounds proof forgets a length.
|
|
648
|
+
if (k < toI32(values.length)) {
|
|
649
|
+
values[k] = text
|
|
650
|
+
}
|
|
651
|
+
missing -= 1
|
|
392
652
|
}
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
i = jsonSkipBlank(object, valueEnd)
|
|
396
|
-
if (i < 0 || i >= length || toI32(object.charCodeAt(i)) !== JSON_COMMA) {
|
|
397
|
-
return null
|
|
653
|
+
k += 1
|
|
398
654
|
}
|
|
399
|
-
|
|
655
|
+
at = member.next
|
|
400
656
|
}
|
|
401
|
-
return
|
|
657
|
+
return values
|
|
402
658
|
}
|