@amritk/nish-x86_64-linux 0.27.2 → 0.29.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.
Files changed (54) hide show
  1. package/INSTALL.md +16 -8
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +118 -26
  4. package/package.json +1 -1
  5. package/runtime/core/host.d.ts +2 -1
  6. package/runtime/core/host.mjs +4 -0
  7. package/runtime/core.d.ts +47 -0
  8. package/runtime/core.mjs +67 -0
  9. package/runtime/nish.d.ts +176 -0
  10. package/runtime/nish.h +120 -6
  11. package/runtime/nish.mjs +24 -4
  12. package/runtime/runtime-checksum.c +270 -0
  13. package/runtime/runtime-net.c +386 -5
  14. package/runtime/runtime-os.c +59 -8
  15. package/runtime/runtime-parallel.c +81 -5
  16. package/runtime/runtime-sandbox.c +479 -0
  17. package/runtime/runtime-sha256.c +239 -0
  18. package/runtime/runtime-shm.c +541 -0
  19. package/runtime/runtime.c +827 -791
  20. package/scripts/build.sh +79 -28
  21. package/std/README.md +25 -13
  22. package/std/collections.ts +5 -5
  23. package/std/compress/checksum.ts +88 -75
  24. package/std/compress/deflate.ts +224 -0
  25. package/std/compress/gzip.ts +52 -0
  26. package/std/crypto/bcrypt.ts +816 -280
  27. package/std/crypto/hmac.ts +305 -2
  28. package/std/crypto/jwt.ts +1661 -0
  29. package/std/json-equal.ts +335 -0
  30. package/std/json-format.ts +1070 -0
  31. package/std/json-number.ts +428 -0
  32. package/std/json-pointer.ts +245 -0
  33. package/std/json-typed.ts +34 -0
  34. package/std/json-value.ts +510 -17
  35. package/std/json.ts +35 -6
  36. package/std/net/handler.ts +1348 -0
  37. package/std/net/http-client.ts +97 -16
  38. package/std/net/http-compress.ts +301 -16
  39. package/std/net/http1-client.ts +104 -19
  40. package/std/net/http1-server.ts +127 -24
  41. package/std/net/http1.ts +24 -4
  42. package/std/net/outbound.ts +409 -0
  43. package/std/net/postgres.ts +405 -0
  44. package/std/net/prefork.ts +344 -20
  45. package/std/net/websocket.ts +295 -14
  46. package/std/regex.ts +613 -52
  47. package/std/secret.ts +1 -1
  48. package/std/sync/ring.ts +587 -0
  49. package/std/sync/shm.ts +284 -0
  50. package/std/term-io.ts +35 -8
  51. package/std/term-rate.ts +13 -1
  52. package/std/threads.ts +35 -3
  53. package/std/yaml-json.ts +841 -0
  54. /package/runtime/{LICENSE-ryu → LICENSE-zmij} +0 -0
package/std/json-value.ts CHANGED
@@ -42,7 +42,8 @@
42
42
  * negative handle, or one past the last value, is `JSON_VALUE_NONE` too.
43
43
  *
44
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
45
+ * arrays the document keeps for its life, 66 after a `parseRanged`, on top of
46
+ * the decoded text of its
46
47
  * strings, keys and numbers, which `JsonLimits.maxTotalBytes` bounds through
47
48
  * the input (`docs/security/json.md`, JSON-10). `maxValues` caps the first
48
49
  * part by capping how many values one document holds:
@@ -61,6 +62,31 @@
61
62
  * `f64` holds, come back out exactly as they went in, where JavaScript's
62
63
  * `JSON.parse` turns both into the nearest double.
63
64
  *
65
+ * **Source ranges are opt-in.** `parseRanged` and `parseTextRanged` parse as
66
+ * `parse` and `parseText` do, and every value and every member key also keeps
67
+ * the range of bytes it was read from: `start(v)` and `end(v)` bound a value,
68
+ * `keyStart(o, i)` and `keyEnd(o, i)` the key of member `i`, its quotes
69
+ * included. They are offsets into the bytes `parseRanged` was given, or
70
+ * `parseTextRanged`'s string, so a line index over that text
71
+ * (`YamlLineIndex` is one) turns them into a line and a column. Under Node,
72
+ * where `parseTextRanged` takes one byte per UTF-16 code unit (below), its
73
+ * offsets count code units, so past the first non-ASCII character they differ
74
+ * from a native build's, which count UTF-8 bytes. Ranges cost about 20 bytes
75
+ * per value more: two offsets for the value, two for its key and about one on
76
+ * the parse's stacks, which grow with the nesting and the members pending.
77
+ * They are entry points of their own rather than a flag `parse` tests, so
78
+ * that `parse` keeps the loop it had before ranges existed. A plain `parse`
79
+ * writes nothing for them: ranges carry the generation they were written in,
80
+ * which `clear` moves anyway, so after a `parse` every reader answers `-1`,
81
+ * and their arrays are made on first use, so a document that never keeps
82
+ * ranges allocates none. A
83
+ * loader that builds a document from another text `clear`s, then gives each
84
+ * value it makes its range with `setRange` and each key with `setKeyRange`;
85
+ * until then a built value or key answers `-1`. `set` over an existing key
86
+ * keeps the member's key range and takes the new value's own. Under
87
+ * `JSON_VALUE_DUPLICATE_LAST` a repeated key's member keeps its first key's
88
+ * range and holds the last value, with that value's.
89
+ *
64
90
  * **Objects keep insertion order**, the order the members were written in or
65
91
  * added. `get` finds a member by its key with a scan, so it is for a handful of
66
92
  * lookups per object; a program that looks up many keys of a large object
@@ -159,12 +185,14 @@ export const JSON_VALUE_ERR_ATTACHED: i32 = -32
159
185
  export const JSON_VALUE_ERR_NUMBER: i32 = -33
160
186
  /** One value more than `JsonDocument.maxValues`, by a parse (with `errorAt` at its first byte) or a builder. */
161
187
  export const JSON_VALUE_ERR_VALUES: i32 = -34
188
+ /** `setRange` or `setKeyRange` with a range that is not `0 <= start <= end`. */
189
+ export const JSON_VALUE_ERR_RANGE: i32 = -35
162
190
 
163
191
  /**
164
192
  * The most values one document holds by default: a parse that would make
165
193
  * more is `JSON_VALUE_ERR_VALUES`. Each value costs the document about 46
166
- * bytes of arrays it keeps (`docs/security/json.md`, JSON-10), so this caps
167
- * them near 12 MB; a program raises `maxValues` up to
194
+ * bytes of arrays it keeps, about 66 after a `parseRanged` (`docs/security/json.md`,
195
+ * JSON-10), so this caps them near 12 MB, or 17 MB; a program raises `maxValues` up to
168
196
  * `JSON_VALUE_MAX_VALUES` for larger documents.
169
197
  */
170
198
  export const JSON_VALUE_DEFAULT_MAX_VALUES: i32 = 262144
@@ -198,6 +226,30 @@ const JSON_VALUE_NO_WORD: i32 = 0
198
226
  const JSON_VALUE_TRUE_WORD: i32 = 1
199
227
  const JSON_VALUE_MINUS: i32 = 45
200
228
 
229
+ /** Internal: a `JsonDocument`'s source ranges, made on first use; see `JsonDocument.ranges`. */
230
+ class JsonValueRanges {
231
+ /**
232
+ * Per value, its first byte in the parsed bytes and one past its last; per
233
+ * member slot, its key's, quotes included. `-1` where nothing was recorded.
234
+ */
235
+ valueStart: i32[]
236
+ valueEnd: i32[]
237
+ slotKeyStart: i32[]
238
+ slotKeyEnd: i32[]
239
+ /** For `parseRanged`: where each open container's key and each pending member's key end; `openKeySource` and `pendingSource` hold where they start. */
240
+ openKeyEnd: i32[]
241
+ pendingKeyEnd: i32[]
242
+
243
+ constructor() {
244
+ this.valueStart = []
245
+ this.valueEnd = []
246
+ this.slotKeyStart = []
247
+ this.slotKeyEnd = []
248
+ this.openKeyEnd = []
249
+ this.pendingKeyEnd = []
250
+ }
251
+ }
252
+
201
253
  /**
202
254
  * A JSON tree, and the memory every value of it lives in. Make one, then
203
255
  * `parse` into it or build with the `new*` methods; the module header has the
@@ -221,6 +273,20 @@ export class JsonDocument {
221
273
  slotKeyLength: i32[]
222
274
  /** The bytes of every string, key and number, decoded, one after the other. */
223
275
  bytes: u8[]
276
+ /**
277
+ * The source ranges, `null` until the first `parseRanged` or setter, so
278
+ * that a document that never keeps ranges allocates nothing for them.
279
+ * They are current only while `rangeGeneration` is `generation`, and then
280
+ * only for the first `rangeTop` values and `slotRangeTop` slots: past them
281
+ * is an earlier document's, and reads as `-1`, so that a value or member a
282
+ * builder adds writes nothing here. Validity rides on the generation, which
283
+ * `clear` moves anyway, so that a plain `parse` writes nothing for ranges.
284
+ */
285
+ ranges: JsonValueRanges | null = null
286
+ rangeTop: i32 = 0
287
+ slotRangeTop: i32 = 0
288
+ /** The generation the ranges were written in; `0`, never a generation, when none were. */
289
+ rangeGeneration: i32 = 0
224
290
  /**
225
291
  * The parse's own stacks: the open containers, where each one's members
226
292
  * start in the pending arrays and the key it is the value of, if any; and
@@ -306,7 +372,14 @@ export class JsonDocument {
306
372
 
307
373
  /** Forgets every value, keeping the memory for the next parse or build. Every handle is invalid afterwards. */
308
374
  clear(): void {
309
- this.generation = this.generation >= JSON_VALUE_LAST_GENERATION ? 1 : this.generation + 1
375
+ if (this.generation >= JSON_VALUE_LAST_GENERATION) {
376
+ // Every return to a generation already used passes here, so ranges
377
+ // stamped with one are forgotten before it comes round again.
378
+ this.generation = 1
379
+ this.rangeGeneration = 0
380
+ } else {
381
+ this.generation += 1
382
+ }
310
383
  this.top = 0
311
384
  this.slotTop = 0
312
385
  this.byteTop = 0
@@ -321,7 +394,10 @@ export class JsonDocument {
321
394
  * Clears the document and parses `src[off, off + len)` into it. Answers the
322
395
  * root value, or a negative code — the tokenizer's `JSON_TOK_ERR_*` or
323
396
  * `JSON_VALUE_ERR_DUPLICATE` — which is also in `failed`, with the byte
324
- * offset in `errorAt`. Nothing is kept from a parse that fails.
397
+ * offset in `errorAt`. Nothing is kept from a parse that fails. Keeps no
398
+ * source ranges; `parseRanged` does.
399
+ *
400
+ * Keep in step with `parseRanged`, the same loop plus the ranges.
325
401
  */
326
402
  parse(src: u8[], off: i32, len: i32): JsonValue {
327
403
  this.clear()
@@ -403,7 +479,115 @@ export class JsonDocument {
403
479
  return this.handle(root)
404
480
  }
405
481
 
406
- /** `parse` over the bytes of `s`, copied into an array the document reuses; `errorAt` is a byte offset in `s`. */
482
+ /**
483
+ * `parse`, and every value and member key keeps the range of bytes it was
484
+ * read from, for `start`, `end`, `keyStart` and `keyEnd`. The same answer,
485
+ * code and `errorAt` as `parse` on the same bytes.
486
+ *
487
+ * A loop of its own rather than a flag `parse` tests, so that `parse` runs
488
+ * the loop it ran before ranges existed: a dispatch inside `parse` let this
489
+ * loop inline there and cost the plain loop more instructions. A program
490
+ * that links both can still see the plain loop's code laid out differently
491
+ * (`docs/BENCHMARKS.md`). Keep in step with `parse`.
492
+ */
493
+ parseRanged(src: u8[], off: i32, len: i32): JsonValue {
494
+ this.clear()
495
+ const r: JsonValueRanges = this.rangeArrays()
496
+ const tok: JsonTokenizer = this.tok
497
+ tok.reset(src, off, len)
498
+ let depth: i32 = 0
499
+ let root: i32 = -1
500
+ let keyAt: i32 = 0
501
+ let keyLength: i32 = 0
502
+ let keySource: i32 = 0
503
+ let keyEnd: i32 = 0
504
+ let k: i32 = tok.next()
505
+ while (k > 0) {
506
+ let v: i32 = -1
507
+ if (k === JSON_TOK_KEY) {
508
+ keySource = tok.tokStart
509
+ keyEnd = tok.tokEnd
510
+ keyAt = this.byteTop
511
+ keyLength = this.storeString(tok)
512
+ } else if (k === JSON_TOK_OBJECT_BEGIN || k === JSON_TOK_ARRAY_BEGIN) {
513
+ const c: i32 = this.newNode(
514
+ k === JSON_TOK_OBJECT_BEGIN ? JSON_VALUE_OBJECT : JSON_VALUE_ARRAY,
515
+ JSON_VALUE_NO_WORD,
516
+ JSON_VALUE_NO_WORD
517
+ )
518
+ if (c < 0) {
519
+ return this.parseFailed(c, tok.tokStart)
520
+ }
521
+ jsonValuePut(this.openValue, depth, c)
522
+ jsonValuePut(this.openBase, depth, this.pendingTop)
523
+ jsonValuePut(this.openKeyAt, depth, keyAt)
524
+ jsonValuePut(this.openKeyLength, depth, keyLength)
525
+ jsonValuePut(this.openKeySource, depth, keySource)
526
+ // The end is a placeholder until the closer, so that both arrays grow in step with the values.
527
+ jsonValuePut(r.valueStart, c, tok.tokStart)
528
+ jsonValuePut(r.valueEnd, c, -1)
529
+ jsonValuePut(r.openKeyEnd, depth, keyEnd)
530
+ depth += 1
531
+ } else if (k === JSON_TOK_OBJECT_END || k === JSON_TOK_ARRAY_END) {
532
+ depth -= 1
533
+ const c: i32 = jsonValueGet(this.openValue, depth)
534
+ const code: i32 = this.closeRanged(c, jsonValueGet(this.openBase, depth))
535
+ if (code < 0) {
536
+ return this.parseFailed(code, this.errorAt)
537
+ }
538
+ // The container's own key, read before its members' keys overwrote the three locals.
539
+ keyAt = jsonValueGet(this.openKeyAt, depth)
540
+ keyLength = jsonValueGet(this.openKeyLength, depth)
541
+ keySource = jsonValueGet(this.openKeySource, depth)
542
+ jsonValuePut(r.valueEnd, c, tok.tokEnd)
543
+ keyEnd = jsonValueGet(r.openKeyEnd, depth)
544
+ v = c
545
+ } else if (k === JSON_TOK_STRING) {
546
+ v = this.newNode(JSON_VALUE_STRING, this.byteTop, this.storeString(tok))
547
+ } else if (k === JSON_TOK_NUMBER) {
548
+ v = this.newNode(JSON_VALUE_NUMBER, this.byteTop, this.storeSourceRanged(tok))
549
+ } else if (k === JSON_TOK_NULL) {
550
+ v = this.newNode(JSON_VALUE_NULL, JSON_VALUE_NO_WORD, JSON_VALUE_NO_WORD)
551
+ } else {
552
+ v = this.newNode(
553
+ JSON_VALUE_BOOLEAN,
554
+ k === JSON_TOK_TRUE ? JSON_VALUE_TRUE_WORD : JSON_VALUE_NO_WORD,
555
+ JSON_VALUE_NO_WORD
556
+ )
557
+ }
558
+ // `-1` is a key or an opener, which place no value yet; below it, the value was refused.
559
+ if (v < -1) {
560
+ return this.parseFailed(v, tok.tokStart)
561
+ }
562
+ if (v >= 0) {
563
+ if (k !== JSON_TOK_OBJECT_END && k !== JSON_TOK_ARRAY_END) {
564
+ jsonValuePut(r.valueStart, v, tok.tokStart)
565
+ jsonValuePut(r.valueEnd, v, tok.tokEnd)
566
+ }
567
+ if (depth === 0) {
568
+ root = v
569
+ } else {
570
+ const p: i32 = this.pendingTop
571
+ jsonValuePut(this.pendingValue, p, v)
572
+ jsonValuePut(this.pendingKeyAt, p, keyAt)
573
+ jsonValuePut(this.pendingKeyLength, p, keyLength)
574
+ jsonValuePut(this.pendingSource, p, keySource)
575
+ jsonValuePut(r.pendingKeyEnd, p, keyEnd)
576
+ this.pendingTop = p + 1
577
+ }
578
+ }
579
+ k = tok.next()
580
+ }
581
+ if (k < 0 || root < 0) {
582
+ return this.parseFailed(k < 0 ? k : JSON_VALUE_ERR_HANDLE, tok.errorAt)
583
+ }
584
+ this.rangeTop = this.top
585
+ this.slotRangeTop = this.slotTop
586
+ this.rangeGeneration = this.generation
587
+ return this.handle(root)
588
+ }
589
+
590
+ /** `parse` over the bytes of `s`, copied into an array the document reuses; `errorAt` is a byte offset in `s`. Keep in step with `parseTextRanged`. */
407
591
  parseText(s: string): JsonValue {
408
592
  const n: i32 = toI32(s.length)
409
593
  const buf: u8[] = jsonValueRoom(this.textBytes, n)
@@ -414,6 +598,17 @@ export class JsonDocument {
414
598
  return this.parse(buf, JSON_VALUE_NO_WORD, n)
415
599
  }
416
600
 
601
+ /** `parseRanged` over the bytes of `s`, as `parseText` is `parse`'s; its offsets are byte offsets in `s`. Keep in step with `parseText`. */
602
+ parseTextRanged(s: string): JsonValue {
603
+ const n: i32 = toI32(s.length)
604
+ const buf: u8[] = jsonValueRoom(this.textBytes, n)
605
+ this.textBytes = buf
606
+ for (let i: i32 = 0; i < n && i < toI32(buf.length); i += 1) {
607
+ buf[i] = toU8(s.charCodeAt(i))
608
+ }
609
+ return this.parseRanged(buf, JSON_VALUE_NO_WORD, n)
610
+ }
611
+
417
612
  /** Internal: Records a failed parse and forgets what it built. */
418
613
  parseFailed(code: i32, at: i32): i32 {
419
614
  this.clear()
@@ -425,7 +620,8 @@ export class JsonDocument {
425
620
  /**
426
621
  * Internal: Places the members pending from `base` into container `c` as one run of
427
622
  * slots, checking an object's keys for duplicates first. Answers `0`, or
428
- * `JSON_VALUE_ERR_DUPLICATE` with `errorAt` set.
623
+ * `JSON_VALUE_ERR_DUPLICATE` with `errorAt` set. Keep in step with
624
+ * `closeRanged`.
429
625
  */
430
626
  close(c: i32, base: i32): i32 {
431
627
  let n: i32 = this.pendingTop - base
@@ -459,13 +655,50 @@ export class JsonDocument {
459
655
  return 0
460
656
  }
461
657
 
658
+ /** Internal: `close` for `parseRanged`, which also gives each member its key's range. Keep in step with `close`. */
659
+ closeRanged(c: i32, base: i32): i32 {
660
+ let n: i32 = this.pendingTop - base
661
+ const object: boolean = this.kindAt(c) === JSON_VALUE_OBJECT
662
+ if (object && n > 1) {
663
+ const code: i32 = this.duplicateKeysRanged(base, n)
664
+ if (code < 0) {
665
+ return code
666
+ }
667
+ }
668
+ const r: JsonValueRanges = this.rangeArrays()
669
+ const start: i32 = this.slotTop
670
+ let kept: i32 = 0
671
+ for (let p: i32 = base; p < base + n; p += 1) {
672
+ const v: i32 = jsonValueGet(this.pendingValue, p)
673
+ if (v < 0) {
674
+ continue
675
+ }
676
+ const s: i32 = start + kept
677
+ jsonValuePut(this.slotValue, s, v)
678
+ jsonValuePut(this.slotKeyAt, s, jsonValueGet(this.pendingKeyAt, p))
679
+ jsonValuePut(this.slotKeyLength, s, jsonValueGet(this.pendingKeyLength, p))
680
+ jsonValuePut(r.slotKeyStart, s, jsonValueGet(this.pendingSource, p))
681
+ jsonValuePut(r.slotKeyEnd, s, jsonValueGet(r.pendingKeyEnd, p))
682
+ jsonValuePut(this.parent, v, c)
683
+ kept += 1
684
+ }
685
+ n = kept
686
+ this.slotTop = start + n
687
+ jsonValuePut(this.first, c, start)
688
+ jsonValuePut(this.count, c, n)
689
+ jsonValuePut(this.room, c, n)
690
+ this.pendingTop = base
691
+ return 0
692
+ }
693
+
462
694
  /**
463
695
  * Internal: The duplicate check over the `n` members pending from `base`. Under
464
696
  * `JSON_VALUE_DUPLICATE_LAST`, a repeated key's later members are marked
465
697
  * gone (their pending value set to `-1`) and the first one takes the last
466
698
  * one's value. Small objects compare every pair; larger ones are sorted by
467
699
  * key with ties in member order, so a key's repeats are next to each other
468
- * and the first of them is its first member.
700
+ * and the first of them is its first member. Keep in step with
701
+ * `duplicateKeysRanged`.
469
702
  */
470
703
  duplicateKeys(base: i32, n: i32): i32 {
471
704
  let firstRepeat: i32 = -1
@@ -498,6 +731,42 @@ export class JsonDocument {
498
731
  return 0
499
732
  }
500
733
 
734
+ /**
735
+ * Internal: `duplicateKeys` for `closeRanged`, the same check. A copy, so that
736
+ * `duplicateKeys` keeps the one caller that lets it inline into `parse`.
737
+ * Keep in step with `duplicateKeys`.
738
+ */
739
+ duplicateKeysRanged(base: i32, n: i32): i32 {
740
+ let firstRepeat: i32 = -1
741
+ if (n <= JSON_VALUE_PAIRWISE) {
742
+ for (let i: i32 = base + 1; i < base + n; i += 1) {
743
+ for (let j: i32 = base; j < i; j += 1) {
744
+ if (jsonValueGet(this.pendingValue, j) >= 0 && this.samePendingKey(i, j)) {
745
+ firstRepeat = this.repeat(j, i, firstRepeat)
746
+ break
747
+ }
748
+ }
749
+ }
750
+ } else {
751
+ const order: i32[] = this.sortPendingRanged(base, n)
752
+ let i: i32 = 0
753
+ while (i < n) {
754
+ const head: i32 = jsonValueGet(order, i)
755
+ let j: i32 = i + 1
756
+ while (j < n && this.samePendingKey(head, jsonValueGet(order, j))) {
757
+ firstRepeat = this.repeat(head, jsonValueGet(order, j), firstRepeat)
758
+ j += 1
759
+ }
760
+ i = j
761
+ }
762
+ }
763
+ if (firstRepeat >= 0 && this.duplicates !== JSON_VALUE_DUPLICATE_LAST) {
764
+ this.errorAt = firstRepeat
765
+ return JSON_VALUE_ERR_DUPLICATE
766
+ }
767
+ return 0
768
+ }
769
+
501
770
  /**
502
771
  * Internal: Pending member `later` repeats the key of the earlier `head`: under
503
772
  * `JSON_VALUE_DUPLICATE_LAST` `head` takes its value and it is marked gone.
@@ -520,7 +789,8 @@ export class JsonDocument {
520
789
  /**
521
790
  * Internal: The pending indices `base .. base + n - 1` sorted by key, ties in index
522
791
  * order: a bottom-up merge sort between the two sort arrays, which answers
523
- * whichever of them holds the result.
792
+ * whichever of them holds the result. Keep in step with
793
+ * `sortPendingRanged`.
524
794
  */
525
795
  sortPending(base: i32, n: i32): i32[] {
526
796
  let from: i32[] = jsonValueRoomI32(this.sortA, n)
@@ -561,6 +831,50 @@ export class JsonDocument {
561
831
  return from
562
832
  }
563
833
 
834
+ /**
835
+ * Internal: `sortPending` for `duplicateKeysRanged`, the same sort. A copy, so
836
+ * that `sortPending` keeps the one caller that lets it inline into `parse`.
837
+ * Keep in step with `sortPending`.
838
+ */
839
+ sortPendingRanged(base: i32, n: i32): i32[] {
840
+ let from: i32[] = jsonValueRoomI32(this.sortA, n)
841
+ let to: i32[] = jsonValueRoomI32(this.sortB, n)
842
+ this.sortA = from
843
+ this.sortB = to
844
+ for (let i: i32 = 0; i < n && i < toI32(from.length); i += 1) {
845
+ from[i] = base + i
846
+ }
847
+ let width: i32 = 1
848
+ while (width < n) {
849
+ let lo: i32 = 0
850
+ while (lo < n) {
851
+ const mid: i32 = lo + width < n ? lo + width : n
852
+ const hi: i32 = mid + width < n ? mid + width : n
853
+ let a: i32 = lo
854
+ let b: i32 = mid
855
+ let out: i32 = lo
856
+ while (out < hi) {
857
+ const takeA: boolean =
858
+ b >= hi || (a < mid && this.comparePending(jsonValueGet(from, a), jsonValueGet(from, b)) <= 0)
859
+ if (takeA) {
860
+ jsonValuePut(to, out, jsonValueGet(from, a))
861
+ a += 1
862
+ } else {
863
+ jsonValuePut(to, out, jsonValueGet(from, b))
864
+ b += 1
865
+ }
866
+ out += 1
867
+ }
868
+ lo = hi
869
+ }
870
+ const swap: i32[] = from
871
+ from = to
872
+ to = swap
873
+ width = width * 2
874
+ }
875
+ return from
876
+ }
877
+
564
878
  /** Internal: Pending members `a` and `b` ordered by key bytes. */
565
879
  comparePending(a: i32, b: i32): i32 {
566
880
  return jsonValueCompareBytes(
@@ -583,7 +897,7 @@ export class JsonDocument {
583
897
  return n
584
898
  }
585
899
 
586
- /** Internal: The tokenizer's current token as written, a number's text, onto the end of `bytes`; answers its length. */
900
+ /** Internal: The tokenizer's current token as written, a number's text, onto the end of `bytes`; answers its length. Keep in step with `storeSourceRanged`. */
587
901
  storeSource(tok: JsonTokenizer): i32 {
588
902
  const src: u8[] = tok.src
589
903
  const start: i32 = tok.tokStart
@@ -600,6 +914,27 @@ export class JsonDocument {
600
914
  return n
601
915
  }
602
916
 
917
+ /**
918
+ * Internal: `storeSource` for `parseRanged`, the same copy. A copy, so that
919
+ * `storeSource` keeps the one caller that lets it inline into `parse`.
920
+ * Keep in step with `storeSource`.
921
+ */
922
+ storeSourceRanged(tok: JsonTokenizer): i32 {
923
+ const src: u8[] = tok.src
924
+ const start: i32 = tok.tokStart
925
+ const n: i32 = tok.tokEnd - start
926
+ const at: i32 = this.byteTop
927
+ const buf: u8[] = jsonValueRoom(this.bytes, at + n)
928
+ this.bytes = buf
929
+ for (let i: i32 = 0; i < n && start + i < toI32(src.length) && at + i < toI32(buf.length); i += 1) {
930
+ if (start + i >= 0 && at + i >= 0) {
931
+ buf[at + i] = src[start + i]
932
+ }
933
+ }
934
+ this.byteTop = at + n
935
+ return n
936
+ }
937
+
603
938
  /**
604
939
  * Internal: a new value of `kind` with its two words, in no container, and
605
940
  * its index; `JSON_VALUE_ERR_VALUES` when the document already holds
@@ -864,6 +1199,126 @@ export class JsonDocument {
864
1199
  return true
865
1200
  }
866
1201
 
1202
+ // ---- Ranges -----------------------------------------------------------------
1203
+ // Each reader answers `-1` for a handle that names nothing, for a member
1204
+ // that is not there, for a value or key a builder made and no setter gave a
1205
+ // range, and for every value and key after a `parse` or `parseText`.
1206
+
1207
+ /** The offset of `v`'s first byte in the bytes the document parsed. */
1208
+ start(v: JsonValue): i32 {
1209
+ const x: i32 = this.index(v)
1210
+ return this.hasValueRange(x) ? jsonValueGet(this.rangeArrays().valueStart, x) : -1
1211
+ }
1212
+
1213
+ /** One past the offset of `v`'s last byte: `v` is `src[start(v), end(v))`. */
1214
+ end(v: JsonValue): i32 {
1215
+ const x: i32 = this.index(v)
1216
+ return this.hasValueRange(x) ? jsonValueGet(this.rangeArrays().valueEnd, x) : -1
1217
+ }
1218
+
1219
+ /** The offset of the first byte of the key of member `i` of object `o`, its opening quote. */
1220
+ keyStart(o: JsonValue, i: i32): i32 {
1221
+ const s: i32 = this.keySlot(this.index(o), i)
1222
+ return this.hasKeyRange(s) ? jsonValueGet(this.rangeArrays().slotKeyStart, s) : -1
1223
+ }
1224
+
1225
+ /** One past the offset of the last byte of the key of member `i` of object `o`, its closing quote. */
1226
+ keyEnd(o: JsonValue, i: i32): i32 {
1227
+ const s: i32 = this.keySlot(this.index(o), i)
1228
+ return this.hasKeyRange(s) ? jsonValueGet(this.rangeArrays().slotKeyEnd, s) : -1
1229
+ }
1230
+
1231
+ /** Internal: whether value `x` has a current range: below `rangeTop`, and written in this generation. */
1232
+ hasValueRange(x: i32): boolean {
1233
+ return x >= 0 && x < this.rangeTop && this.rangeGeneration === this.generation
1234
+ }
1235
+
1236
+ /** Internal: whether slot `s` has a current key range: below `slotRangeTop`, and written in this generation. */
1237
+ hasKeyRange(s: i32): boolean {
1238
+ return s >= 0 && s < this.slotRangeTop && this.rangeGeneration === this.generation
1239
+ }
1240
+
1241
+ /** Internal: the range arrays, made the first time a parse or setter writes a range. */
1242
+ rangeArrays(): JsonValueRanges {
1243
+ const had: JsonValueRanges | null = this.ranges
1244
+ if (had !== null) {
1245
+ return had
1246
+ }
1247
+ const made: JsonValueRanges = new JsonValueRanges()
1248
+ this.ranges = made
1249
+ return made
1250
+ }
1251
+
1252
+ /** Internal: before a setter writes a range, forgets an earlier generation's, so that none of them reads as this one's. */
1253
+ ownRanges(): void {
1254
+ if (this.rangeGeneration !== this.generation) {
1255
+ this.rangeTop = 0
1256
+ this.slotRangeTop = 0
1257
+ this.rangeGeneration = this.generation
1258
+ }
1259
+ }
1260
+
1261
+ /**
1262
+ * Gives `v` the range `[start, end)`, for a loader that builds a document
1263
+ * from another text with the `new*` methods and keeps where each value
1264
+ * came from. `JSON_VALUE_OK`, `JSON_VALUE_ERR_HANDLE`, or
1265
+ * `JSON_VALUE_ERR_RANGE` when `0 <= start <= end` does not hold.
1266
+ */
1267
+ setRange(v: JsonValue, start: i32, end: i32): i32 {
1268
+ const x: i32 = this.index(v)
1269
+ if (this.kindAt(x) === JSON_VALUE_NONE) {
1270
+ return JSON_VALUE_ERR_HANDLE
1271
+ }
1272
+ if (start < 0 || end < start) {
1273
+ return JSON_VALUE_ERR_RANGE
1274
+ }
1275
+ this.ownRanges()
1276
+ const r: JsonValueRanges = this.rangeArrays()
1277
+ // Values between the last current one and `x` have no range yet.
1278
+ for (let y: i32 = this.rangeTop; y < x; y += 1) {
1279
+ jsonValuePut(r.valueStart, y, -1)
1280
+ jsonValuePut(r.valueEnd, y, -1)
1281
+ }
1282
+ jsonValuePut(r.valueStart, x, start)
1283
+ jsonValuePut(r.valueEnd, x, end)
1284
+ if (x >= this.rangeTop) {
1285
+ this.rangeTop = x + 1
1286
+ }
1287
+ return JSON_VALUE_OK
1288
+ }
1289
+
1290
+ /**
1291
+ * Gives the key of member `i` of object `o` the range `[start, end)`, as
1292
+ * `setRange` does a value's. `JSON_VALUE_ERR_HANDLE` when `o` is not an
1293
+ * object or has no member `i`.
1294
+ */
1295
+ setKeyRange(o: JsonValue, i: i32, start: i32, end: i32): i32 {
1296
+ const s: i32 = this.keySlot(this.index(o), i)
1297
+ if (s < 0) {
1298
+ return JSON_VALUE_ERR_HANDLE
1299
+ }
1300
+ if (start < 0 || end < start) {
1301
+ return JSON_VALUE_ERR_RANGE
1302
+ }
1303
+ this.ownRanges()
1304
+ const r: JsonValueRanges = this.rangeArrays()
1305
+ for (let t: i32 = this.slotRangeTop; t < s; t += 1) {
1306
+ jsonValuePut(r.slotKeyStart, t, -1)
1307
+ jsonValuePut(r.slotKeyEnd, t, -1)
1308
+ }
1309
+ jsonValuePut(r.slotKeyStart, s, start)
1310
+ jsonValuePut(r.slotKeyEnd, s, end)
1311
+ if (s >= this.slotRangeTop) {
1312
+ this.slotRangeTop = s + 1
1313
+ }
1314
+ return JSON_VALUE_OK
1315
+ }
1316
+
1317
+ /** Internal: the slot of member `i` of the object at index `x`, or `-1` when `x` is not an object or has no member `i`. */
1318
+ keySlot(x: i32, i: i32): i32 {
1319
+ return this.kindAt(x) === JSON_VALUE_OBJECT ? this.slotAt(x, i) : -1
1320
+ }
1321
+
867
1322
  // ---- Building ---------------------------------------------------------------
868
1323
  // Each `new*` answers a handle, or `JSON_VALUE_ERR_VALUES` when the document
869
1324
  // already holds `maxValues`.
@@ -1015,13 +1470,7 @@ export class JsonDocument {
1015
1470
  if (n === capacity) {
1016
1471
  const grown: i32 = capacity < JSON_VALUE_FIRST_SLOTS ? JSON_VALUE_FIRST_SLOTS : capacity * 2
1017
1472
  if (start + capacity !== this.slotTop) {
1018
- const moved: i32 = this.slotTop
1019
- for (let i: i32 = 0; i < n; i += 1) {
1020
- jsonValuePut(this.slotValue, moved + i, jsonValueGet(this.slotValue, start + i))
1021
- jsonValuePut(this.slotKeyAt, moved + i, jsonValueGet(this.slotKeyAt, start + i))
1022
- jsonValuePut(this.slotKeyLength, moved + i, jsonValueGet(this.slotKeyLength, start + i))
1023
- }
1024
- start = moved
1473
+ start = this.moveSlots(start, n)
1025
1474
  jsonValuePut(this.first, c, start)
1026
1475
  }
1027
1476
  this.slotTop = start + grown
@@ -1042,6 +1491,50 @@ export class JsonDocument {
1042
1491
  return this.handle(this.newNode(kind, this.byteTop, this.storeText(s)))
1043
1492
  }
1044
1493
 
1494
+ /**
1495
+ * Internal: copies the `n` slots from `start` to the end of the slot arrays,
1496
+ * key ranges with them, and answers where they now start. Key ranges move
1497
+ * only when the slots are below `slotRangeTop` and of this generation, the
1498
+ * test that comes first: a document that never keeps ranges, or a builder's
1499
+ * own slots, skip the copy without leaving `append`'s loop.
1500
+ */
1501
+ moveSlots(start: i32, n: i32): i32 {
1502
+ const moved: i32 = this.slotTop
1503
+ for (let i: i32 = 0; i < n; i += 1) {
1504
+ jsonValuePut(this.slotValue, moved + i, jsonValueGet(this.slotValue, start + i))
1505
+ jsonValuePut(this.slotKeyAt, moved + i, jsonValueGet(this.slotKeyAt, start + i))
1506
+ jsonValuePut(this.slotKeyLength, moved + i, jsonValueGet(this.slotKeyLength, start + i))
1507
+ }
1508
+ if (start < this.slotRangeTop && this.rangeGeneration === this.generation) {
1509
+ this.moveKeyRanges(start, moved, n)
1510
+ }
1511
+ return moved
1512
+ }
1513
+
1514
+ /**
1515
+ * Internal: `append` moving the `n` slots from `from`, some of them below
1516
+ * `slotRangeTop`, to `to`, the end of the slots: their key ranges move with
1517
+ * them, and the slots between keep none. `moveSlots` has checked the
1518
+ * generation, and a slot below `slotRangeTop` means the arrays were made;
1519
+ * reading them here, not through `rangeArrays`, keeps `append` small.
1520
+ */
1521
+ moveKeyRanges(from: i32, to: i32, n: i32): void {
1522
+ const r: JsonValueRanges | null = this.ranges
1523
+ const top: i32 = this.slotRangeTop
1524
+ if (r !== null) {
1525
+ for (let t: i32 = top; t < to; t += 1) {
1526
+ jsonValuePut(r.slotKeyStart, t, -1)
1527
+ jsonValuePut(r.slotKeyEnd, t, -1)
1528
+ }
1529
+ for (let i: i32 = 0; i < n; i += 1) {
1530
+ const has: boolean = from + i < top
1531
+ jsonValuePut(r.slotKeyStart, to + i, has ? jsonValueGet(r.slotKeyStart, from + i) : -1)
1532
+ jsonValuePut(r.slotKeyEnd, to + i, has ? jsonValueGet(r.slotKeyEnd, from + i) : -1)
1533
+ }
1534
+ this.slotRangeTop = to + n
1535
+ }
1536
+ }
1537
+
1045
1538
  /** Internal: `s`'s bytes onto the end of `bytes`; answers how many. */
1046
1539
  storeText(s: string): i32 {
1047
1540
  const n: i32 = toI32(s.length)