@amritk/nish-x86_64-linux 0.26.0 → 0.28.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 +13 -8
- package/bin/nish +0 -0
- package/docs/AI.md +27 -5
- package/package.json +1 -1
- package/runtime/core.d.ts +47 -0
- package/runtime/core.mjs +67 -0
- package/runtime/nish.d.ts +54 -5
- package/runtime/nish.h +67 -8
- package/runtime/nish.mjs +6 -0
- package/runtime/runtime-checksum.c +270 -0
- package/runtime/runtime-os.c +9 -4
- package/runtime/runtime-parallel.c +21 -0
- package/runtime/runtime-sandbox.c +470 -0
- package/runtime/runtime-sha256.c +239 -0
- package/runtime/runtime-simd.c +63 -2
- package/runtime/runtime-wasm.c +18 -0
- package/scripts/build.sh +60 -24
- package/std/README.md +16 -10
- package/std/compress/checksum.ts +88 -75
- package/std/compress/deflate.ts +224 -0
- package/std/compress/gzip.ts +52 -0
- package/std/crypto/bcrypt.ts +816 -280
- package/std/crypto/sha256.ts +335 -188
- package/std/json-equal.ts +335 -0
- package/std/json-format.ts +1070 -0
- package/std/json-number.ts +428 -0
- package/std/json-pointer.ts +245 -0
- package/std/json-value.ts +510 -17
- package/std/net/http-client.ts +1645 -0
- package/std/net/http-compress.ts +259 -16
- package/std/net/http1-client.ts +104 -19
- package/std/net/http1-server.ts +61 -12
- package/std/net/postgres.ts +576 -8
- package/std/net/websocket.ts +295 -14
- package/std/regex.ts +613 -52
- package/std/term-io.ts +35 -8
- package/std/yaml-json.ts +841 -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,
|
|
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`,
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
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)
|