@evolu/common 8.6.2 → 8.7.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.
@@ -1,9 +1,15 @@
1
1
  /**
2
2
  * Binary data handling and byte array utilities.
3
3
  *
4
+ * Buffer-based decoding functions intentionally throw errors instead of
5
+ * returning {@link Result}. This is a deliberate micro-optimization for Evolu
6
+ * Protocol's hot paths: Result is inexpensive, but returning decoded values
7
+ * directly avoids its success-case allocation, and using `Error` objects
8
+ * preserves stack traces. In the future, we will try returning `Error` objects
9
+ * in `Result` values to measure the real-world performance impact.
10
+ *
4
11
  * @module
5
12
  */
6
- import { NonNegativeInt, zeroNonNegativeInt } from "./Type.js";
7
13
  export { bytesToHex, bytesToUtf8, concatBytes, hexToBytes, utf8ToBytes, } from "@noble/ciphers/utils.js";
8
14
  /**
9
15
  * Custom error for {@link Buffer}-related failures like premature end of data.
@@ -18,15 +24,20 @@ export class BufferError extends Error {
18
24
  }
19
25
  /** Creates a {@link Buffer} for efficient byte operations. */
20
26
  export const createBuffer = (arrayLike) => {
27
+ const initialLength = arrayLike?.length ?? 0;
28
+ assertNonNegativeInt(initialLength, "arrayLike.length");
21
29
  let value = arrayLike
22
30
  ? new globalThis.Uint8Array(arrayLike)
23
31
  : new globalThis.Uint8Array(512);
24
- let length = NonNegativeInt.orThrow(arrayLike ? arrayLike.length : 0);
32
+ let length = initialLength;
25
33
  const buffer = {
26
- getCapacity: () => NonNegativeInt.orThrow(value.length),
34
+ getCapacity: () => value.length,
27
35
  getLength: () => length,
28
36
  extend: (arg) => {
29
- const targetSize = length + arg.length;
37
+ const argLength = arg.length;
38
+ assertNonNegativeInt(argLength, "arg.length");
39
+ const targetSize = length + argLength;
40
+ assertNonNegativeInt(targetSize, "Buffer length");
30
41
  if (value.length < targetSize) {
31
42
  const oldValue = value;
32
43
  const newCapacity = Math.max(value.length * 2, targetSize);
@@ -34,24 +45,20 @@ export const createBuffer = (arrayLike) => {
34
45
  value.set(oldValue);
35
46
  }
36
47
  value.set(arg, length);
37
- length = NonNegativeInt.orThrow(length + arg.length);
48
+ length = targetSize;
38
49
  },
39
50
  shift: () => {
40
- if (length === 0) {
41
- throw new BufferError("Buffer parse ended prematurely");
42
- }
51
+ assertBufferHasRemainingBytes(length, 1);
43
52
  const first = value[0];
44
53
  value = value.subarray(1);
45
54
  length--;
46
- return NonNegativeInt.orThrow(first);
55
+ return first;
47
56
  },
48
57
  shiftN: (n) => {
49
- if (length < n) {
50
- throw new BufferError("Buffer parse ended prematurely");
51
- }
58
+ assertBufferHasRemainingBytes(length, n);
52
59
  const subarray = value.subarray(0, n);
53
60
  value = value.subarray(n);
54
- length = NonNegativeInt.orThrow(length - n);
61
+ length = (length - n);
55
62
  return subarray;
56
63
  },
57
64
  truncate: (newLength) => {
@@ -61,9 +68,709 @@ export const createBuffer = (arrayLike) => {
61
68
  length = newLength;
62
69
  },
63
70
  reset: () => {
64
- length = zeroNonNegativeInt;
71
+ length = 0;
65
72
  },
66
- unwrap: () => value.subarray(0, length),
73
+ unwrap: () => (value.length === length ? value : value.subarray(0, length)),
67
74
  };
68
75
  return buffer;
69
76
  };
77
+ const jsonKeyCacheSize = 4096;
78
+ const maxCachedJsonKeyByteLength = 32;
79
+ const maxJsonNestingDepth = 1_000;
80
+ let jsonEncoderTarget = new globalThis.Uint8Array(8192);
81
+ let jsonEncoderTargetView = new globalThis.DataView(jsonEncoderTarget.buffer);
82
+ let jsonEncoderPosition = 0;
83
+ let jsonEncoderDepth = 0;
84
+ let jsonEncoderIsActive = false;
85
+ const emptyJsonDecoderSource = new globalThis.Uint8Array(0);
86
+ const emptyJsonDecoderView = new globalThis.DataView(emptyJsonDecoderSource.buffer);
87
+ let jsonDecoderSource = emptyJsonDecoderSource;
88
+ let jsonDecoderView = emptyJsonDecoderView;
89
+ let jsonDecoderPosition = 0;
90
+ let jsonDecoderDepth = 0;
91
+ const jsonStringFromCharCode = globalThis.String.fromCharCode;
92
+ // Cache only short keys and use a fixed table to bound retained memory.
93
+ const jsonKeyCache = globalThis.Array.from({ length: jsonKeyCacheSize }, () => undefined);
94
+ /**
95
+ * Encodes a {@link JsonValue} using the MessagePack format.
96
+ *
97
+ * ### Example
98
+ *
99
+ * ```ts
100
+ * import {
101
+ * assertEqual,
102
+ * createBuffer,
103
+ * encodeJsonValue,
104
+ * JsonValue,
105
+ * } from "@evolu/common";
106
+ *
107
+ * const buffer = createBuffer();
108
+ * const value = JsonValue.orThrow({ name: "Ada" });
109
+ *
110
+ * encodeJsonValue(buffer, value);
111
+ *
112
+ * assertEqual(
113
+ * buffer.unwrap(),
114
+ * new Uint8Array([
115
+ * 0x81, 0xa4, 0x6e, 0x61, 0x6d, 0x65, 0xa3, 0x41, 0x64, 0x61,
116
+ * ]),
117
+ * );
118
+ * ```
119
+ *
120
+ * Encoding is artificially limited to 1,000 nested arrays or objects to keep
121
+ * recursive encoding and decoding safe and symmetric. JSON data should not
122
+ * require such depth; flatten or split deeply nested data, or use a
123
+ * purpose-built serialization format.
124
+ */
125
+ export const encodeJsonValue = (buffer, value) => {
126
+ if (jsonEncoderIsActive) {
127
+ throw new BufferError("Reentrant JSON encoding is not supported.");
128
+ }
129
+ jsonEncoderIsActive = true;
130
+ jsonEncoderPosition = 0;
131
+ try {
132
+ encodeJsonValueToTarget(value);
133
+ buffer.extend(jsonEncoderTarget.subarray(0, jsonEncoderPosition));
134
+ }
135
+ finally {
136
+ jsonEncoderPosition = 0;
137
+ jsonEncoderDepth = 0;
138
+ jsonEncoderIsActive = false;
139
+ }
140
+ };
141
+ /**
142
+ * Decodes a {@link JsonValue} using the MessagePack format.
143
+ *
144
+ * Throws a {@link BufferError} without modifying the Buffer if the encoded value
145
+ * is malformed, truncated, unsupported, outside the JsonValue domain, or
146
+ * exceeds 1,000 nested arrays or objects.
147
+ *
148
+ * ### Example
149
+ *
150
+ * ```ts
151
+ * import {
152
+ * assertEqual,
153
+ * createBuffer,
154
+ * decodeJsonValue,
155
+ * } from "@evolu/common";
156
+ *
157
+ * const buffer = createBuffer([
158
+ * 0x81, 0xa4, 0x6e, 0x61, 0x6d, 0x65, 0xa3, 0x41, 0x64, 0x61,
159
+ * ]);
160
+ *
161
+ * assertEqual(decodeJsonValue(buffer), { name: "Ada" });
162
+ * assertEqual(buffer.unwrap(), new Uint8Array());
163
+ * ```
164
+ */
165
+ export const decodeJsonValue = (buffer) => {
166
+ const source = buffer.unwrap();
167
+ jsonDecoderSource = source;
168
+ jsonDecoderView = new globalThis.DataView(source.buffer, source.byteOffset, source.byteLength);
169
+ jsonDecoderPosition = 0;
170
+ try {
171
+ const value = decodeJsonValueFromSource();
172
+ buffer.shiftN(jsonDecoderPosition);
173
+ return value;
174
+ }
175
+ catch (error) {
176
+ if (error instanceof BufferError)
177
+ throw error;
178
+ throw new BufferError("Invalid MessagePack data");
179
+ }
180
+ finally {
181
+ jsonDecoderSource = emptyJsonDecoderSource;
182
+ jsonDecoderView = emptyJsonDecoderView;
183
+ jsonDecoderPosition = 0;
184
+ jsonDecoderDepth = 0;
185
+ }
186
+ };
187
+ const encodeJsonValueToTarget = (value) => {
188
+ if (value === null) {
189
+ writeJsonEncoderByte(0xc0);
190
+ return;
191
+ }
192
+ // oxlint-disable-next-line typescript/switch-exhaustiveness-check -- JsonValue excludes the additional runtime types reported by tsgolint.
193
+ switch (typeof value) {
194
+ case "string":
195
+ encodeJsonStringToTarget(value);
196
+ return;
197
+ case "number": {
198
+ if (globalThis.Object.is(value, -0)) {
199
+ ensureJsonEncoderCapacity(9);
200
+ jsonEncoderTarget[jsonEncoderPosition++] = 0xcb;
201
+ jsonEncoderTargetView.setFloat64(jsonEncoderPosition, value);
202
+ jsonEncoderPosition += 8;
203
+ return;
204
+ }
205
+ if (value >>> 0 === value) {
206
+ if (value < 0x80) {
207
+ writeJsonEncoderByte(value);
208
+ }
209
+ else if (value < 0x100) {
210
+ ensureJsonEncoderCapacity(2);
211
+ jsonEncoderTarget[jsonEncoderPosition++] = 0xcc;
212
+ jsonEncoderTarget[jsonEncoderPosition++] = value;
213
+ }
214
+ else if (value < 0x10000) {
215
+ ensureJsonEncoderCapacity(3);
216
+ jsonEncoderTarget[jsonEncoderPosition++] = 0xcd;
217
+ jsonEncoderTargetView.setUint16(jsonEncoderPosition, value);
218
+ jsonEncoderPosition += 2;
219
+ }
220
+ else {
221
+ ensureJsonEncoderCapacity(5);
222
+ jsonEncoderTarget[jsonEncoderPosition++] = 0xce;
223
+ jsonEncoderTargetView.setUint32(jsonEncoderPosition, value);
224
+ jsonEncoderPosition += 4;
225
+ }
226
+ return;
227
+ }
228
+ if (globalThis.Number.isInteger(value) &&
229
+ value >= -0x80000000 &&
230
+ value < 0) {
231
+ if (value >= -0x20) {
232
+ writeJsonEncoderByte(0x100 + value);
233
+ }
234
+ else if (value >= -0x80) {
235
+ ensureJsonEncoderCapacity(2);
236
+ jsonEncoderTarget[jsonEncoderPosition++] = 0xd0;
237
+ jsonEncoderTargetView.setInt8(jsonEncoderPosition++, value);
238
+ }
239
+ else if (value >= -0x8000) {
240
+ ensureJsonEncoderCapacity(3);
241
+ jsonEncoderTarget[jsonEncoderPosition++] = 0xd1;
242
+ jsonEncoderTargetView.setInt16(jsonEncoderPosition, value);
243
+ jsonEncoderPosition += 2;
244
+ }
245
+ else {
246
+ ensureJsonEncoderCapacity(5);
247
+ jsonEncoderTarget[jsonEncoderPosition++] = 0xd2;
248
+ jsonEncoderTargetView.setInt32(jsonEncoderPosition, value);
249
+ jsonEncoderPosition += 4;
250
+ }
251
+ return;
252
+ }
253
+ ensureJsonEncoderCapacity(9);
254
+ jsonEncoderTarget[jsonEncoderPosition++] = 0xcb;
255
+ jsonEncoderTargetView.setFloat64(jsonEncoderPosition, value);
256
+ jsonEncoderPosition += 8;
257
+ return;
258
+ }
259
+ case "boolean":
260
+ writeJsonEncoderByte(value ? 0xc3 : 0xc2);
261
+ return;
262
+ case "object": {
263
+ if (jsonEncoderDepth >= maxJsonNestingDepth) {
264
+ throw new BufferError(`JSON nesting exceeds the maximum depth of ${maxJsonNestingDepth}.`);
265
+ }
266
+ jsonEncoderDepth++;
267
+ if (globalThis.Array.isArray(value)) {
268
+ const array = value;
269
+ const length = array.length;
270
+ writeJsonCollectionHeader(length, 0x90, 0xdc, 0xdd);
271
+ for (const item of array)
272
+ encodeJsonValueToTarget(item);
273
+ jsonEncoderDepth--;
274
+ return;
275
+ }
276
+ const object = value;
277
+ const keys = globalThis.Object.keys(object);
278
+ writeJsonCollectionHeader(keys.length, 0x80, 0xde, 0xdf);
279
+ for (const key of keys) {
280
+ encodeJsonStringToTarget(key);
281
+ encodeJsonValueToTarget(object[key]);
282
+ }
283
+ jsonEncoderDepth--;
284
+ }
285
+ }
286
+ };
287
+ const encodeJsonStringToTarget = (value) => {
288
+ const valueLength = value.length;
289
+ const headerLength = valueLength < 0x20
290
+ ? 1
291
+ : valueLength < 0x100
292
+ ? 2
293
+ : valueLength < 0x10000
294
+ ? 3
295
+ : 5;
296
+ ensureJsonEncoderCapacity(5 + valueLength * 3);
297
+ const headerPosition = jsonEncoderPosition;
298
+ jsonEncoderPosition += headerLength;
299
+ for (let index = 0; index < valueLength; index++) {
300
+ let first = value.charCodeAt(index);
301
+ if (first < 0x80) {
302
+ jsonEncoderTarget[jsonEncoderPosition++] = first;
303
+ }
304
+ else if (first < 0x800) {
305
+ jsonEncoderTarget[jsonEncoderPosition++] = (first >> 6) | 0xc0;
306
+ jsonEncoderTarget[jsonEncoderPosition++] = (first & 0x3f) | 0x80;
307
+ }
308
+ else if ((first & 0xfc00) === 0xd800 &&
309
+ (value.charCodeAt(index + 1) & 0xfc00) === 0xdc00) {
310
+ const second = value.charCodeAt(++index);
311
+ first = 0x10000 + ((first & 0x03ff) << 10) + (second & 0x03ff);
312
+ jsonEncoderTarget[jsonEncoderPosition++] = (first >> 18) | 0xf0;
313
+ jsonEncoderTarget[jsonEncoderPosition++] = ((first >> 12) & 0x3f) | 0x80;
314
+ jsonEncoderTarget[jsonEncoderPosition++] = ((first >> 6) & 0x3f) | 0x80;
315
+ jsonEncoderTarget[jsonEncoderPosition++] = (first & 0x3f) | 0x80;
316
+ }
317
+ else {
318
+ jsonEncoderTarget[jsonEncoderPosition++] = (first >> 12) | 0xe0;
319
+ jsonEncoderTarget[jsonEncoderPosition++] = ((first >> 6) & 0x3f) | 0x80;
320
+ jsonEncoderTarget[jsonEncoderPosition++] = (first & 0x3f) | 0x80;
321
+ }
322
+ }
323
+ const byteLength = jsonEncoderPosition - headerPosition - headerLength;
324
+ assertMessagePackLength(byteLength, "String byte length");
325
+ if (byteLength < 0x20) {
326
+ jsonEncoderTarget[headerPosition] = 0xa0 | byteLength;
327
+ return;
328
+ }
329
+ if (byteLength < 0x100) {
330
+ if (headerLength === 1) {
331
+ jsonEncoderTarget.copyWithin(headerPosition + 2, headerPosition + 1, jsonEncoderPosition);
332
+ jsonEncoderPosition++;
333
+ }
334
+ jsonEncoderTarget[headerPosition] = 0xd9;
335
+ jsonEncoderTarget[headerPosition + 1] = byteLength;
336
+ return;
337
+ }
338
+ if (byteLength < 0x10000) {
339
+ if (headerLength < 3) {
340
+ const additionalHeaderLength = 3 - headerLength;
341
+ jsonEncoderTarget.copyWithin(headerPosition + 3, headerPosition + headerLength, jsonEncoderPosition);
342
+ jsonEncoderPosition += additionalHeaderLength;
343
+ }
344
+ jsonEncoderTarget[headerPosition] = 0xda;
345
+ jsonEncoderTargetView.setUint16(headerPosition + 1, byteLength);
346
+ return;
347
+ }
348
+ if (headerLength < 5) {
349
+ const additionalHeaderLength = 5 - headerLength;
350
+ jsonEncoderTarget.copyWithin(headerPosition + 5, headerPosition + headerLength, jsonEncoderPosition);
351
+ jsonEncoderPosition += additionalHeaderLength;
352
+ }
353
+ jsonEncoderTarget[headerPosition] = 0xdb;
354
+ jsonEncoderTargetView.setUint32(headerPosition + 1, byteLength);
355
+ };
356
+ const writeJsonCollectionHeader = (length, fixedMarker, marker16, marker32) => {
357
+ assertMessagePackLength(length, "Collection length");
358
+ if (length < 0x10) {
359
+ writeJsonEncoderByte(fixedMarker | length);
360
+ }
361
+ else if (length < 0x10000) {
362
+ ensureJsonEncoderCapacity(3);
363
+ jsonEncoderTarget[jsonEncoderPosition++] = marker16;
364
+ jsonEncoderTargetView.setUint16(jsonEncoderPosition, length);
365
+ jsonEncoderPosition += 2;
366
+ }
367
+ else {
368
+ ensureJsonEncoderCapacity(5);
369
+ jsonEncoderTarget[jsonEncoderPosition++] = marker32;
370
+ jsonEncoderTargetView.setUint32(jsonEncoderPosition, length);
371
+ jsonEncoderPosition += 4;
372
+ }
373
+ };
374
+ const writeJsonEncoderByte = (value) => {
375
+ ensureJsonEncoderCapacity(1);
376
+ jsonEncoderTarget[jsonEncoderPosition++] = value;
377
+ };
378
+ const ensureJsonEncoderCapacity = (additionalLength) => {
379
+ const requiredLength = jsonEncoderPosition + additionalLength;
380
+ assertNonNegativeInt(requiredLength, "Encoded JSON value length");
381
+ if (requiredLength <= jsonEncoderTarget.length)
382
+ return;
383
+ const newCapacity = globalThis.Math.max(jsonEncoderTarget.length * 2, requiredLength);
384
+ assertNonNegativeInt(newCapacity, "JSON encoder capacity");
385
+ const oldTarget = jsonEncoderTarget;
386
+ jsonEncoderTarget = new globalThis.Uint8Array(newCapacity);
387
+ jsonEncoderTarget.set(oldTarget.subarray(0, jsonEncoderPosition));
388
+ jsonEncoderTargetView = new globalThis.DataView(jsonEncoderTarget.buffer);
389
+ };
390
+ const assertMessagePackLength = (length, name) => {
391
+ if (length > 0xffffffff) {
392
+ throw new BufferError(`${name} exceeds the MessagePack uint32 limit.`);
393
+ }
394
+ };
395
+ const decodeJsonValueFromSource = () => {
396
+ const marker = readJsonDecoderByte();
397
+ if (marker <= 0x7f)
398
+ return marker;
399
+ if (marker <= 0x8f)
400
+ return decodeJsonMap(marker - 0x80);
401
+ if (marker <= 0x9f)
402
+ return decodeJsonArray(marker - 0x90);
403
+ if (marker <= 0xbf)
404
+ return decodeJsonString(marker - 0xa0);
405
+ if (marker >= 0xe0)
406
+ return (marker - 0x100);
407
+ switch (marker) {
408
+ case 0xc0:
409
+ return null;
410
+ case 0xc2:
411
+ return false;
412
+ case 0xc3:
413
+ return true;
414
+ case 0xca:
415
+ return decodeJsonFloat(4);
416
+ case 0xcb:
417
+ return decodeJsonFloat(8);
418
+ case 0xcc:
419
+ return readJsonDecoderByte();
420
+ case 0xcd:
421
+ return readJsonUint16();
422
+ case 0xce:
423
+ return readJsonUint32();
424
+ case 0xd0: {
425
+ assertJsonDecoderHasRemainingBytes(1);
426
+ return jsonDecoderView.getInt8(jsonDecoderPosition++);
427
+ }
428
+ case 0xd1: {
429
+ assertJsonDecoderHasRemainingBytes(2);
430
+ const value = jsonDecoderView.getInt16(jsonDecoderPosition);
431
+ jsonDecoderPosition += 2;
432
+ return value;
433
+ }
434
+ case 0xd2: {
435
+ assertJsonDecoderHasRemainingBytes(4);
436
+ const value = jsonDecoderView.getInt32(jsonDecoderPosition);
437
+ jsonDecoderPosition += 4;
438
+ return value;
439
+ }
440
+ case 0xd9:
441
+ return decodeJsonString(readJsonDecoderByte());
442
+ case 0xda:
443
+ return decodeJsonString(readJsonUint16());
444
+ case 0xdb:
445
+ return decodeJsonString(readJsonUint32());
446
+ case 0xdc:
447
+ return decodeJsonArray(readJsonUint16());
448
+ case 0xdd:
449
+ return decodeJsonArray(readJsonUint32());
450
+ case 0xde:
451
+ return decodeJsonMap(readJsonUint16());
452
+ case 0xdf:
453
+ return decodeJsonMap(readJsonUint32());
454
+ default:
455
+ throw new BufferError(`Unsupported MessagePack marker 0x${marker.toString(16).padStart(2, "0")}.`);
456
+ }
457
+ };
458
+ const decodeJsonFloat = (byteLength) => {
459
+ assertJsonDecoderHasRemainingBytes(byteLength);
460
+ const value = byteLength === 4
461
+ ? jsonDecoderView.getFloat32(jsonDecoderPosition)
462
+ : jsonDecoderView.getFloat64(jsonDecoderPosition);
463
+ jsonDecoderPosition += byteLength;
464
+ if (!globalThis.Number.isFinite(value)) {
465
+ throw new BufferError("A decoded JSON number must be finite.");
466
+ }
467
+ return value;
468
+ };
469
+ const decodeJsonString = (byteLength) => {
470
+ assertJsonDecoderHasRemainingBytes(byteLength);
471
+ shortAscii: if (byteLength < 16) {
472
+ if (byteLength === 0)
473
+ return "";
474
+ const start = jsonDecoderPosition;
475
+ const first = jsonDecoderSource[jsonDecoderPosition++];
476
+ if ((first & 0x80) !== 0) {
477
+ jsonDecoderPosition = start;
478
+ break shortAscii;
479
+ }
480
+ if (byteLength === 1)
481
+ return jsonStringFromCharCode(first);
482
+ const second = jsonDecoderSource[jsonDecoderPosition++];
483
+ if ((second & 0x80) !== 0) {
484
+ jsonDecoderPosition = start;
485
+ break shortAscii;
486
+ }
487
+ if (byteLength === 2)
488
+ return jsonStringFromCharCode(first, second);
489
+ const third = jsonDecoderSource[jsonDecoderPosition++];
490
+ if ((third & 0x80) !== 0) {
491
+ jsonDecoderPosition = start;
492
+ break shortAscii;
493
+ }
494
+ if (byteLength === 3)
495
+ return jsonStringFromCharCode(first, second, third);
496
+ const fourth = jsonDecoderSource[jsonDecoderPosition++];
497
+ if ((fourth & 0x80) !== 0) {
498
+ jsonDecoderPosition = start;
499
+ break shortAscii;
500
+ }
501
+ if (byteLength === 4) {
502
+ return jsonStringFromCharCode(first, second, third, fourth);
503
+ }
504
+ const fifth = jsonDecoderSource[jsonDecoderPosition++];
505
+ if ((fifth & 0x80) !== 0) {
506
+ jsonDecoderPosition = start;
507
+ break shortAscii;
508
+ }
509
+ if (byteLength === 5) {
510
+ return jsonStringFromCharCode(first, second, third, fourth, fifth);
511
+ }
512
+ const sixth = jsonDecoderSource[jsonDecoderPosition++];
513
+ if ((sixth & 0x80) !== 0) {
514
+ jsonDecoderPosition = start;
515
+ break shortAscii;
516
+ }
517
+ if (byteLength === 6) {
518
+ return jsonStringFromCharCode(first, second, third, fourth, fifth, sixth);
519
+ }
520
+ const seventh = jsonDecoderSource[jsonDecoderPosition++];
521
+ if ((seventh & 0x80) !== 0) {
522
+ jsonDecoderPosition = start;
523
+ break shortAscii;
524
+ }
525
+ if (byteLength === 7) {
526
+ return jsonStringFromCharCode(first, second, third, fourth, fifth, sixth, seventh);
527
+ }
528
+ const eighth = jsonDecoderSource[jsonDecoderPosition++];
529
+ if ((eighth & 0x80) !== 0) {
530
+ jsonDecoderPosition = start;
531
+ break shortAscii;
532
+ }
533
+ if (byteLength === 8) {
534
+ return jsonStringFromCharCode(first, second, third, fourth, fifth, sixth, seventh, eighth);
535
+ }
536
+ const ninth = jsonDecoderSource[jsonDecoderPosition++];
537
+ if ((ninth & 0x80) !== 0) {
538
+ jsonDecoderPosition = start;
539
+ break shortAscii;
540
+ }
541
+ if (byteLength === 9) {
542
+ return jsonStringFromCharCode(first, second, third, fourth, fifth, sixth, seventh, eighth, ninth);
543
+ }
544
+ const tenth = jsonDecoderSource[jsonDecoderPosition++];
545
+ if ((tenth & 0x80) !== 0) {
546
+ jsonDecoderPosition = start;
547
+ break shortAscii;
548
+ }
549
+ if (byteLength === 10) {
550
+ return jsonStringFromCharCode(first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth);
551
+ }
552
+ const eleventh = jsonDecoderSource[jsonDecoderPosition++];
553
+ if ((eleventh & 0x80) !== 0) {
554
+ jsonDecoderPosition = start;
555
+ break shortAscii;
556
+ }
557
+ if (byteLength === 11) {
558
+ return jsonStringFromCharCode(first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth, eleventh);
559
+ }
560
+ const twelfth = jsonDecoderSource[jsonDecoderPosition++];
561
+ if ((twelfth & 0x80) !== 0) {
562
+ jsonDecoderPosition = start;
563
+ break shortAscii;
564
+ }
565
+ if (byteLength === 12) {
566
+ return jsonStringFromCharCode(first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth, eleventh, twelfth);
567
+ }
568
+ const thirteenth = jsonDecoderSource[jsonDecoderPosition++];
569
+ if ((thirteenth & 0x80) !== 0) {
570
+ jsonDecoderPosition = start;
571
+ break shortAscii;
572
+ }
573
+ if (byteLength === 13) {
574
+ return jsonStringFromCharCode(first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth, eleventh, twelfth, thirteenth);
575
+ }
576
+ const fourteenth = jsonDecoderSource[jsonDecoderPosition++];
577
+ if ((fourteenth & 0x80) !== 0) {
578
+ jsonDecoderPosition = start;
579
+ break shortAscii;
580
+ }
581
+ if (byteLength === 14) {
582
+ return jsonStringFromCharCode(first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth, eleventh, twelfth, thirteenth, fourteenth);
583
+ }
584
+ const fifteenth = jsonDecoderSource[jsonDecoderPosition++];
585
+ if ((fifteenth & 0x80) !== 0) {
586
+ jsonDecoderPosition = start;
587
+ break shortAscii;
588
+ }
589
+ return jsonStringFromCharCode(first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth, eleventh, twelfth, thirteenth, fourteenth, fifteenth);
590
+ }
591
+ const end = jsonDecoderPosition + byteLength;
592
+ const units = [];
593
+ let result = "";
594
+ while (jsonDecoderPosition < end) {
595
+ const first = jsonDecoderSource[jsonDecoderPosition++];
596
+ if (first < 0x80) {
597
+ units.push(first);
598
+ }
599
+ else if (first >= 0xc2 && first <= 0xdf) {
600
+ assertJsonStringHasRemainingBytes(end, 1);
601
+ const second = readJsonContinuationByte();
602
+ units.push(((first & 0x1f) << 6) | second);
603
+ }
604
+ else if (first >= 0xe0 && first <= 0xef) {
605
+ assertJsonStringHasRemainingBytes(end, 2);
606
+ const secondByte = jsonDecoderSource[jsonDecoderPosition];
607
+ if (first === 0xe0 && secondByte < 0xa0) {
608
+ throw new BufferError("Invalid UTF-8 string encoding.");
609
+ }
610
+ const second = readJsonContinuationByte();
611
+ const third = readJsonContinuationByte();
612
+ units.push(((first & 0x0f) << 12) | (second << 6) | third);
613
+ }
614
+ else if (first >= 0xf0 && first <= 0xf4) {
615
+ assertJsonStringHasRemainingBytes(end, 3);
616
+ const secondByte = jsonDecoderSource[jsonDecoderPosition];
617
+ if ((first === 0xf0 && secondByte < 0x90) ||
618
+ (first === 0xf4 && secondByte > 0x8f)) {
619
+ throw new BufferError("Invalid UTF-8 string encoding.");
620
+ }
621
+ const second = readJsonContinuationByte();
622
+ const third = readJsonContinuationByte();
623
+ const fourth = readJsonContinuationByte();
624
+ const codePoint = ((first & 0x07) << 18) | (second << 12) | (third << 6) | fourth;
625
+ const pair = codePoint - 0x10000;
626
+ units.push(0xd800 | (pair >> 10), 0xdc00 | (pair & 0x3ff));
627
+ }
628
+ else {
629
+ throw new BufferError("Invalid UTF-8 string encoding.");
630
+ }
631
+ if (units.length >= 0x1000) {
632
+ result += jsonStringFromCharCode(...units);
633
+ units.length = 0;
634
+ }
635
+ }
636
+ if (units.length > 0) {
637
+ result += jsonStringFromCharCode(...units);
638
+ }
639
+ return result;
640
+ };
641
+ const decodeJsonArray = (length) => {
642
+ if (jsonDecoderDepth >= maxJsonNestingDepth) {
643
+ throw new BufferError(`JSON nesting exceeds the maximum depth of ${maxJsonNestingDepth}.`);
644
+ }
645
+ if (length > jsonDecoderSource.length - jsonDecoderPosition) {
646
+ throw new BufferError("Buffer parse ended prematurely");
647
+ }
648
+ // oxlint-disable-next-line unicorn/no-new-array -- Preallocation is intentional in this decoding hot path.
649
+ const value = new Array(length);
650
+ jsonDecoderDepth++;
651
+ for (let index = 0; index < length; index++) {
652
+ value[index] = decodeJsonValueFromSource();
653
+ }
654
+ jsonDecoderDepth--;
655
+ return value;
656
+ };
657
+ const decodeJsonMap = (length) => {
658
+ if (jsonDecoderDepth >= maxJsonNestingDepth) {
659
+ throw new BufferError(`JSON nesting exceeds the maximum depth of ${maxJsonNestingDepth}.`);
660
+ }
661
+ if (length > (jsonDecoderSource.length - jsonDecoderPosition) / 2) {
662
+ throw new BufferError("Buffer parse ended prematurely");
663
+ }
664
+ const value = {};
665
+ jsonDecoderDepth++;
666
+ for (let index = 0; index < length; index++) {
667
+ const marker = readJsonDecoderByte();
668
+ let key;
669
+ if (marker >= 0xa0 && marker <= 0xbf) {
670
+ key = decodeCachedJsonKey(marker - 0xa0);
671
+ }
672
+ else if (marker === 0xd9) {
673
+ key = decodeCachedJsonKey(readJsonDecoderByte());
674
+ }
675
+ else if (marker === 0xda) {
676
+ key = decodeCachedJsonKey(readJsonUint16());
677
+ }
678
+ else if (marker === 0xdb) {
679
+ key = decodeCachedJsonKey(readJsonUint32());
680
+ }
681
+ else {
682
+ jsonDecoderPosition--;
683
+ decodeJsonValueFromSource();
684
+ throw new BufferError("A decoded JSON object key must be a string.");
685
+ }
686
+ const entryValue = decodeJsonValueFromSource();
687
+ if (key === "__proto__") {
688
+ globalThis.Object.defineProperty(value, key, {
689
+ value: entryValue,
690
+ configurable: true,
691
+ enumerable: true,
692
+ writable: true,
693
+ });
694
+ }
695
+ else {
696
+ value[key] = entryValue;
697
+ }
698
+ }
699
+ jsonDecoderDepth--;
700
+ return value;
701
+ };
702
+ const decodeCachedJsonKey = (byteLength) => {
703
+ if (byteLength > maxCachedJsonKeyByteLength) {
704
+ return decodeJsonString(byteLength);
705
+ }
706
+ assertJsonDecoderHasRemainingBytes(byteLength);
707
+ const start = jsonDecoderPosition;
708
+ const end = start + byteLength;
709
+ const firstBytes = byteLength > 1
710
+ ? jsonDecoderView.getUint16(start)
711
+ : byteLength === 1
712
+ ? jsonDecoderSource[start]
713
+ : 0;
714
+ const cacheIndex = ((byteLength << 5) ^ firstBytes) & (jsonKeyCacheSize - 1);
715
+ const entry = jsonKeyCache[cacheIndex];
716
+ if (entry?.bytes.length === byteLength) {
717
+ let index = 0;
718
+ while (index < byteLength &&
719
+ entry.bytes[index] === jsonDecoderSource[start + index]) {
720
+ index++;
721
+ }
722
+ if (index === byteLength) {
723
+ jsonDecoderPosition = end;
724
+ return entry.value;
725
+ }
726
+ }
727
+ const value = decodeJsonString(byteLength);
728
+ jsonKeyCache[cacheIndex] = {
729
+ bytes: jsonDecoderSource.slice(start, end),
730
+ value,
731
+ };
732
+ return value;
733
+ };
734
+ const readJsonContinuationByte = () => {
735
+ const byte = jsonDecoderSource[jsonDecoderPosition++];
736
+ if ((byte & 0xc0) !== 0x80) {
737
+ throw new BufferError("Invalid UTF-8 string encoding.");
738
+ }
739
+ return byte & 0x3f;
740
+ };
741
+ const assertJsonStringHasRemainingBytes = (end, requiredBytes) => {
742
+ if (end - jsonDecoderPosition < requiredBytes) {
743
+ throw new BufferError("Invalid UTF-8 string encoding.");
744
+ }
745
+ };
746
+ const readJsonDecoderByte = () => {
747
+ assertJsonDecoderHasRemainingBytes(1);
748
+ return jsonDecoderSource[jsonDecoderPosition++];
749
+ };
750
+ const readJsonUint16 = () => {
751
+ assertJsonDecoderHasRemainingBytes(2);
752
+ const value = jsonDecoderView.getUint16(jsonDecoderPosition);
753
+ jsonDecoderPosition += 2;
754
+ return value;
755
+ };
756
+ const readJsonUint32 = () => {
757
+ assertJsonDecoderHasRemainingBytes(4);
758
+ const value = jsonDecoderView.getUint32(jsonDecoderPosition);
759
+ jsonDecoderPosition += 4;
760
+ return value;
761
+ };
762
+ const assertJsonDecoderHasRemainingBytes = (requiredBytes) => {
763
+ if (jsonDecoderSource.length - jsonDecoderPosition < requiredBytes) {
764
+ throw new BufferError("Buffer parse ended prematurely");
765
+ }
766
+ };
767
+ const assertNonNegativeInt = (value, name) => {
768
+ if (!globalThis.Number.isSafeInteger(value) || value < 0) {
769
+ throw new BufferError(`${name} must be a non-negative safe integer.`);
770
+ }
771
+ };
772
+ const assertBufferHasRemainingBytes = (remainingBytes, requiredBytes) => {
773
+ if (remainingBytes < requiredBytes) {
774
+ throw new BufferError("Buffer parse ended prematurely");
775
+ }
776
+ };