knitting 0.1.62 → 0.1.70
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/README.md +525 -335
- package/knitting.browser.js +1 -1
- package/map.md +0 -6
- package/package.json +3 -3
- package/prebuilds/darwin-arm64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-arm64-node-127/knitting_doorbell.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_doorbell.node +0 -0
- package/prebuilds/darwin-x64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-x64-node-127/knitting_doorbell.node +0 -0
- package/prebuilds/darwin-x64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-x64-node-137/knitting_doorbell.node +0 -0
- package/prebuilds/linux-x64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/linux-x64-node-127/knitting_doorbell.node +0 -0
- package/prebuilds/linux-x64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/linux-x64-node-137/knitting_doorbell.node +0 -0
- package/prebuilds/win32-x64/knitting_windows_shared_memory.dll +0 -0
- package/prebuilds/win32-x64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/win32-x64-node-127/knitting_doorbell.node +0 -0
- package/prebuilds/win32-x64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/win32-x64-node-127/knitting_shm.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_doorbell.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_shm.node +0 -0
- package/scripts/build-native-addons.ts +5 -0
- package/shared-memory.d.ts +3 -0
- package/shared-memory.js +3 -0
- package/src/api.js +109 -42
- package/src/common/with-resolvers.js +2 -5
- package/src/common/worker-runtime.d.ts +7 -0
- package/src/common/worker-runtime.js +7 -0
- package/src/connections/buffer-reference.d.ts +10 -36
- package/src/connections/buffer-reference.js +15 -170
- package/src/connections/node-addons.d.ts +1 -1
- package/src/connections/shared-array-buffer-payload.d.ts +7 -0
- package/src/connections/shared-array-buffer-payload.js +27 -11
- package/src/ipc/transport/shared-memory.d.ts +9 -1
- package/src/ipc/transport/shared-memory.js +13 -1
- package/src/knitting_buffer_pointer.cc +57 -2
- package/src/knitting_doorbell.cc +220 -0
- package/src/memory/knitting-body.d.ts +44 -0
- package/src/memory/knitting-body.js +51 -0
- package/src/memory/knitting-buffer-http.d.ts +116 -0
- package/src/memory/knitting-buffer-http.js +255 -0
- package/src/memory/knitting-buffer.d.ts +250 -0
- package/src/memory/knitting-buffer.js +695 -0
- package/src/memory/lazy-region-registry.d.ts +83 -0
- package/src/memory/lazy-region-registry.js +355 -0
- package/src/memory/lock.d.ts +38 -15
- package/src/memory/lock.js +227 -109
- package/src/memory/payloadCodec.d.ts +18 -2
- package/src/memory/payloadCodec.js +309 -65
- package/src/memory/regionRegistry.d.ts +6 -0
- package/src/memory/regionRegistry.js +125 -240
- package/src/memory/shared-buffer-io.d.ts +7 -0
- package/src/memory/shared-buffer-io.js +34 -8
- package/src/runtime/deno-doorbell.d.ts +26 -0
- package/src/runtime/deno-doorbell.js +117 -0
- package/src/runtime/dispatcher.d.ts +8 -6
- package/src/runtime/dispatcher.js +80 -58
- package/src/runtime/host-arg-arena.d.ts +3 -0
- package/src/runtime/host-arg-arena.js +16 -0
- package/src/runtime/node-doorbell.d.ts +14 -0
- package/src/runtime/node-doorbell.js +84 -0
- package/src/runtime/pool.d.ts +27 -15
- package/src/runtime/pool.js +138 -116
- package/src/runtime/process-worker.d.ts +9 -0
- package/src/runtime/process-worker.js +22 -2
- package/src/runtime/tx-queue.d.ts +2 -5
- package/src/runtime/tx-queue.js +52 -48
- package/src/runtime/worker-common.d.ts +2 -1
- package/src/runtime/worker-common.js +19 -5
- package/src/types.d.ts +36 -70
- package/src/worker/loop.js +95 -62
- package/src/worker/rx-queue.d.ts +2 -3
- package/src/worker/rx-queue.js +34 -40
- package/src/worker/shared-return.d.ts +9 -0
- package/src/worker/shared-return.js +22 -0
- package/src/worker/task-loader.js +1 -2
- package/src/worker/timers.d.ts +2 -6
- package/src/worker/timers.js +14 -19
- package/unsafe.d.ts +2 -1
- package/unsafe.js +2 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { attachPayloadTransportFinalizer, beginPromisePayload, finishPromisePayload,
|
|
1
|
+
import { attachPayloadTransportFinalizer, beginPromisePayload, finishPromisePayload, HEADER_BYTE_LENGTH, HEADER_SLOT_STRIDE_U32, HEADER_STATIC_PAYLOAD_U32, LockBound, PayloadBuffer, payloadBufferName, PayloadSignal, registerLockPayloadCodec, TASK_SLOT_INDEX_MASK, TaskIndex, } from "./lock.js";
|
|
2
2
|
import { register } from "./regionRegistry.js";
|
|
3
3
|
import { createSharedDynamicBufferIO, createSharedStaticBufferIO, } from "./shared-buffer-io.js";
|
|
4
4
|
import { getStridedRegionSpanBytes } from "./byte-carpet.js";
|
|
@@ -6,7 +6,8 @@ import { encoderError, ErrorKnitting } from "../error.js";
|
|
|
6
6
|
import { Envelope } from "../common/envelope.js";
|
|
7
7
|
import { resolvePayloadBufferOptions, } from "./payload-config.js";
|
|
8
8
|
import { getSharedArrayBufferPayload, SHARED_ARRAY_BUFFER_CODEC_ID, SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER, SHARED_ARRAY_BUFFER_NUMERIC_WORDS, } from "../connections/shared-array-buffer-payload.js";
|
|
9
|
-
import { BUFFER_REFERENCE_NUMERIC_TRANSFER, BufferReference, isBufferReferenceValue, } from "../connections/buffer-reference.js";
|
|
9
|
+
import { BUFFER_REFERENCE_NUMERIC_TRANSFER, BufferReference, isArrayBufferDetached, isBufferReferenceValue, } from "../connections/buffer-reference.js";
|
|
10
|
+
import { getBufferReferenceCapabilities, } from "../connections/buffer-reference-native.js";
|
|
10
11
|
import { isProcessSharedBufferValue, ProcessSharedBuffer, } from "../connections/process-shared-buffer.js";
|
|
11
12
|
import { isNumericArray, numericArrayFromFloat64, } from "../connections/numeric-array.js";
|
|
12
13
|
const memory = new ArrayBuffer(8);
|
|
@@ -99,7 +100,7 @@ const readTrustedExternalPayloadMetadata = (value) => {
|
|
|
99
100
|
}
|
|
100
101
|
return value.toMetadata();
|
|
101
102
|
};
|
|
102
|
-
const decodeExternalPayload = (raw, processBoundary) => {
|
|
103
|
+
const decodeExternalPayload = (raw, processBoundary, transportKey) => {
|
|
103
104
|
const payload = parseJSON(raw);
|
|
104
105
|
if (!arrayIsArray(payload) || payload.length !== 2)
|
|
105
106
|
return payload;
|
|
@@ -113,22 +114,33 @@ const decodeExternalPayload = (raw, processBoundary) => {
|
|
|
113
114
|
}
|
|
114
115
|
const codec = externalPayloadGlobal.__KNITTING_PAYLOAD_CODECS__?.[codecId];
|
|
115
116
|
return typeof codec?.decode === "function"
|
|
116
|
-
? codec.decode(metadata)
|
|
117
|
+
? codec.decode(metadata, transportKey)
|
|
117
118
|
: { codec: codecId, metadata };
|
|
118
119
|
};
|
|
119
120
|
const PROCESS_SHARED_BUFFER_NUMERIC_WORDS = 8;
|
|
120
121
|
const BUFFER_REFERENCE_NUMERIC_WORDS = 8;
|
|
121
122
|
const NUMERIC_SENTINEL = 0xffffffff;
|
|
122
|
-
|
|
123
|
+
/** Minimum size for automatically borrowed byte returns. */
|
|
124
|
+
export const SHARED_RETURN_MIN_BYTES = 256 * 1024;
|
|
125
|
+
/** Number of later borrowed returns kept before a region is recycled. */
|
|
126
|
+
export const SHARED_RETURN_BORROW_WINDOW = 32;
|
|
127
|
+
// Each payload buffer has one allocator for the worker return lane.
|
|
128
|
+
const sharedReturnAllocators = new WeakMap();
|
|
129
|
+
/** The borrowed-region allocator for `payload`, if an encoder built one. */
|
|
130
|
+
export const getSharedReturnAllocator = (payload) => sharedReturnAllocators.get(payload);
|
|
131
|
+
const decodeNumericExternalPayload = (codecId, words, transportKey) => {
|
|
123
132
|
const codec = externalPayloadGlobal.__KNITTING_PAYLOAD_CODECS__?.[codecId];
|
|
124
133
|
if (typeof codec?.decodeNumeric === "function") {
|
|
125
|
-
return codec.decodeNumeric(words);
|
|
134
|
+
return codec.decodeNumeric(words, transportKey);
|
|
126
135
|
}
|
|
127
136
|
return { codec: codecId, metadata: Array.from(words) };
|
|
128
137
|
};
|
|
129
138
|
const decodeProcessSharedBufferNumericWords = (words) => decodeNumericExternalPayload(PROCESS_SHARED_BUFFER_CODEC_ID, words);
|
|
130
139
|
const decodeBufferReferenceNumericWords = (words) => decodeNumericExternalPayload(BUFFER_REFERENCE_CODEC_ID, words);
|
|
131
|
-
|
|
140
|
+
// The transport key scopes the host's adopted-alias cache to one return lane:
|
|
141
|
+
// producer tokens restart at 1 in every worker isolate, so a shared cache would
|
|
142
|
+
// serve one worker's buffer for another worker's identical token.
|
|
143
|
+
const decodeSharedArrayBufferNumericWords = (words, transportKey) => decodeNumericExternalPayload(SHARED_ARRAY_BUFFER_CODEC_ID, words, transportKey);
|
|
132
144
|
const tryEncodePrimitiveTask = (task) => {
|
|
133
145
|
const value = task.value;
|
|
134
146
|
switch (typeof value) {
|
|
@@ -304,15 +316,28 @@ const requireStaticIO = (headersBuffer, headerSlotStrideU32, textCompat) => {
|
|
|
304
316
|
* Returns `true` when the payload is encoded successfully.
|
|
305
317
|
* Returns `false` when dynamic payload space could not be reserved.
|
|
306
318
|
*/
|
|
307
|
-
export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, onPromise, processBoundary = false, }) => {
|
|
319
|
+
export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, onPromise, processBoundary = false, sharedReturn = false, moveReturn = false, }) => {
|
|
308
320
|
const payloadSab = payload?.sab ?? sab;
|
|
309
321
|
const resolvedPayloadConfig = resolvePayloadBufferOptions({
|
|
310
322
|
sab: payloadSab,
|
|
311
323
|
options: payload?.config ?? payloadConfig,
|
|
312
324
|
});
|
|
313
325
|
const maxPayloadBytes = resolvedPayloadConfig.maxPayloadBytes;
|
|
314
|
-
|
|
315
|
-
|
|
326
|
+
// Automatic moves are an optimization. Keep ordinary byte returns working
|
|
327
|
+
// when this runtime has no usable ownership/FFI backend.
|
|
328
|
+
const canMoveReturn = moveReturn && (() => {
|
|
329
|
+
try {
|
|
330
|
+
getBufferReferenceCapabilities();
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
})();
|
|
337
|
+
const registryHandle = register({ lockSector });
|
|
338
|
+
const { allocTask, setSlotLength, tagTaskSlot, free } = registryHandle;
|
|
339
|
+
const { allocRegion, regionStart } = registryHandle;
|
|
340
|
+
const { writeBinary: writeDynamicBinary, writeBuffer: writeDynamicBuffer, writeArrayBuffer: writeDynamicArrayBuffer, write8Binary: writeDynamic8Binary, writeUtf8: writeDynamicUtf8, readBytesView: dynamicRegionView, ensureCapacity: ensureDynamicCapacity, currentBuffer: currentDynamicBuffer, regionBase: dynamicRegionBase, } = createSharedDynamicBufferIO({
|
|
316
341
|
sab: payloadSab,
|
|
317
342
|
payloadConfig: resolvedPayloadConfig,
|
|
318
343
|
textCompat: textCompat?.payload,
|
|
@@ -350,9 +375,72 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
350
375
|
return exactBytes;
|
|
351
376
|
};
|
|
352
377
|
const dynamicUtf8ReserveBytes = (task, text, label) => dynamicUtf8ReserveBytesWithExtra(task, text, 0, label);
|
|
378
|
+
// Borrowed regions are tracked as [slot, start, end] triples, oldest first.
|
|
379
|
+
const borrowed = [];
|
|
380
|
+
const BORROW_STRIDE = 3;
|
|
381
|
+
/** Limit borrowed regions to half the payload arena. */
|
|
382
|
+
const maxBorrowBytes = ((resolvedPayloadConfig.payloadMaxByteLength >> 1) /
|
|
383
|
+
SHARED_RETURN_BORROW_WINDOW) | 0;
|
|
384
|
+
/** Reserve a region to hand over by reference, or -1 when none is free. */
|
|
385
|
+
const reserveBorrowedRegion = (bytes) => {
|
|
386
|
+
if (bytes > maxBorrowBytes)
|
|
387
|
+
return -1;
|
|
388
|
+
const slot = allocRegion(bytes);
|
|
389
|
+
if (slot === -1)
|
|
390
|
+
return -1;
|
|
391
|
+
const start = regionStart(slot);
|
|
392
|
+
if (!ensureDynamicCapacity(start + bytes)) {
|
|
393
|
+
free(slot);
|
|
394
|
+
return -1;
|
|
395
|
+
}
|
|
396
|
+
borrowed.push(slot, start, start + bytes);
|
|
397
|
+
if (borrowed.length > SHARED_RETURN_BORROW_WINDOW * BORROW_STRIDE) {
|
|
398
|
+
free(borrowed[0]);
|
|
399
|
+
borrowed.splice(0, BORROW_STRIDE);
|
|
400
|
+
}
|
|
401
|
+
return slot;
|
|
402
|
+
};
|
|
403
|
+
const dropBorrowedRegion = (slot) => {
|
|
404
|
+
for (let at = 0; at < borrowed.length; at += BORROW_STRIDE) {
|
|
405
|
+
if (borrowed[at] === slot) {
|
|
406
|
+
borrowed.splice(at, BORROW_STRIDE);
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
free(slot);
|
|
411
|
+
};
|
|
412
|
+
/** Return the arena offset when `view` is inside a live borrowed region. */
|
|
413
|
+
const borrowedStartOf = (view) => {
|
|
414
|
+
if (borrowed.length === 0)
|
|
415
|
+
return -1;
|
|
416
|
+
if (view.buffer !== currentDynamicBuffer())
|
|
417
|
+
return -1;
|
|
418
|
+
const start = view.byteOffset - dynamicRegionBase();
|
|
419
|
+
const end = start + view.byteLength;
|
|
420
|
+
for (let at = borrowed.length - BORROW_STRIDE; at >= 0; at -= BORROW_STRIDE) {
|
|
421
|
+
if (start >= borrowed[at + 1] && end <= borrowed[at + 2])
|
|
422
|
+
return start;
|
|
423
|
+
}
|
|
424
|
+
return -1;
|
|
425
|
+
};
|
|
426
|
+
/** Allocate a borrowed return region, or fall back when none is available. */
|
|
427
|
+
const allocateSharedReturn = (byteLength, zeroFill = false) => {
|
|
428
|
+
const slot = reserveBorrowedRegion(byteLength);
|
|
429
|
+
if (slot === -1)
|
|
430
|
+
return undefined;
|
|
431
|
+
const start = regionStart(slot);
|
|
432
|
+
const view = dynamicRegionView(start, start + byteLength);
|
|
433
|
+
if (zeroFill)
|
|
434
|
+
view.fill(0);
|
|
435
|
+
return view;
|
|
436
|
+
};
|
|
437
|
+
if (sharedReturn && payloadSab !== undefined) {
|
|
438
|
+
sharedReturnAllocators.set(payloadSab, allocateSharedReturn);
|
|
439
|
+
}
|
|
353
440
|
const reserveDynamic = (task, bytes) => {
|
|
354
441
|
task[TaskIndex.PayloadLen] = bytes;
|
|
355
|
-
//
|
|
442
|
+
// -1 is dynamic-region backpressure. Returning false leaves this frame on
|
|
443
|
+
// the lock's existing pending list until a receiver releases a region.
|
|
356
444
|
return allocTask(task);
|
|
357
445
|
};
|
|
358
446
|
let objectDynamicSlot = -1;
|
|
@@ -419,6 +507,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
419
507
|
return false;
|
|
420
508
|
task[TaskIndex.Type] = PayloadBuffer.Error;
|
|
421
509
|
const reservedSlot = reserveDynamicObject(task, reserveBytes);
|
|
510
|
+
if (reservedSlot === -1)
|
|
511
|
+
return false;
|
|
422
512
|
const written = writeDynamicUtf8(text, task[TaskIndex.Start], reserveBytes);
|
|
423
513
|
if (written < 0)
|
|
424
514
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -443,6 +533,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
443
533
|
return false;
|
|
444
534
|
}
|
|
445
535
|
const reservedSlot = reserveDynamicObject(task, bytes);
|
|
536
|
+
if (reservedSlot === -1)
|
|
537
|
+
return false;
|
|
446
538
|
const written = writeDynamicBinary(bytesView, task[TaskIndex.Start]);
|
|
447
539
|
if (written < 0)
|
|
448
540
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -453,6 +545,15 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
453
545
|
};
|
|
454
546
|
const encodeObjectUint8Array = (task, slotIndex, bytesView) => {
|
|
455
547
|
const bytes = bytesView.byteLength;
|
|
548
|
+
// Preserve an arena-backed subarray as an offset/length frame.
|
|
549
|
+
const borrowedStart = borrowedStartOf(bytesView);
|
|
550
|
+
if (borrowedStart !== -1) {
|
|
551
|
+
task[TaskIndex.Type] = PayloadBuffer.ArenaBinary;
|
|
552
|
+
task[TaskIndex.Start] = borrowedStart;
|
|
553
|
+
task[TaskIndex.PayloadLen] = bytes;
|
|
554
|
+
task.value = null;
|
|
555
|
+
return true;
|
|
556
|
+
}
|
|
456
557
|
if (bytes <= staticMaxBytes) {
|
|
457
558
|
writeStaticExactUint8Array(bytesView, slotIndex);
|
|
458
559
|
task[TaskIndex.Type] = PayloadBuffer.StaticBinary;
|
|
@@ -460,10 +561,16 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
460
561
|
task.value = null;
|
|
461
562
|
return true;
|
|
462
563
|
}
|
|
564
|
+
if (canMoveReturn && bytes >= SHARED_RETURN_MIN_BYTES &&
|
|
565
|
+
tryEncodeMovedReturn(task, slotIndex, bytesView, PayloadBuffer.MovedBinary)) {
|
|
566
|
+
return true;
|
|
567
|
+
}
|
|
463
568
|
task[TaskIndex.Type] = PayloadBuffer.Binary;
|
|
464
569
|
if (!ensureWithinDynamicLimit(task, bytes, "Binary"))
|
|
465
570
|
return false;
|
|
466
571
|
const reservedSlot = reserveDynamicObject(task, bytes);
|
|
572
|
+
if (reservedSlot === -1)
|
|
573
|
+
return false;
|
|
467
574
|
const written = writeDynamicBinary(bytesView, task[TaskIndex.Start]);
|
|
468
575
|
if (written < 0)
|
|
469
576
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -487,6 +594,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
487
594
|
if (!ensureWithinDynamicLimit(task, bytes, "Buffer"))
|
|
488
595
|
return false;
|
|
489
596
|
const reservedSlot = reserveDynamicObject(task, bytes);
|
|
597
|
+
if (reservedSlot === -1)
|
|
598
|
+
return false;
|
|
490
599
|
const written = writeDynamicBuffer(buffer, task[TaskIndex.Start]);
|
|
491
600
|
if (written < 0)
|
|
492
601
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -510,6 +619,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
510
619
|
if (!ensureWithinDynamicLimit(task, bytes, "Float64Array"))
|
|
511
620
|
return false;
|
|
512
621
|
const reservedSlot = reserveDynamicObject(task, bytes);
|
|
622
|
+
if (reservedSlot === -1)
|
|
623
|
+
return false;
|
|
513
624
|
const written = writeDynamic8Binary(float64, task[TaskIndex.Start]);
|
|
514
625
|
if (written < 0)
|
|
515
626
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -541,6 +652,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
541
652
|
if (!ensureWithinDynamicLimit(task, bytes, "NumericArray"))
|
|
542
653
|
return false;
|
|
543
654
|
const reservedSlot = reserveDynamicObject(task, bytes);
|
|
655
|
+
if (reservedSlot === -1)
|
|
656
|
+
return false;
|
|
544
657
|
const written = writeDynamic8Binary(float64, task[TaskIndex.Start]);
|
|
545
658
|
if (written < 0)
|
|
546
659
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -560,10 +673,16 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
560
673
|
return true;
|
|
561
674
|
}
|
|
562
675
|
}
|
|
676
|
+
if (canMoveReturn && bytes >= SHARED_RETURN_MIN_BYTES &&
|
|
677
|
+
tryEncodeMovedReturn(task, slotIndex, arrayBuffer, PayloadBuffer.MovedArrayBuffer)) {
|
|
678
|
+
return true;
|
|
679
|
+
}
|
|
563
680
|
task[TaskIndex.Type] = PayloadBuffer.ArrayBuffer;
|
|
564
681
|
if (!ensureWithinDynamicLimit(task, bytes, "ArrayBuffer"))
|
|
565
682
|
return false;
|
|
566
683
|
const reservedSlot = reserveDynamicObject(task, bytes);
|
|
684
|
+
if (reservedSlot === -1)
|
|
685
|
+
return false;
|
|
567
686
|
const written = writeDynamicArrayBuffer(arrayBuffer, task[TaskIndex.Start]);
|
|
568
687
|
if (written < 0)
|
|
569
688
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -619,6 +738,68 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
619
738
|
task.value = null;
|
|
620
739
|
return true;
|
|
621
740
|
};
|
|
741
|
+
/**
|
|
742
|
+
* The safe counterpart to an arena borrow. The producer's source is detached
|
|
743
|
+
* only after its task has settled; the host adopts the backing store before
|
|
744
|
+
* its acknowledgement lets the producer release the registry pin.
|
|
745
|
+
*
|
|
746
|
+
* This needs its own frame type because public task results remain ordinary
|
|
747
|
+
* Uint8Array / ArrayBuffer values rather than exposing BufferReference.
|
|
748
|
+
*/
|
|
749
|
+
const tryEncodeMovedReturn = (task, slotIndex, source, type) => {
|
|
750
|
+
if (source.byteLength > 0xffffffff)
|
|
751
|
+
return false;
|
|
752
|
+
const backing = ArrayBuffer.isView(source) ? source.buffer : source;
|
|
753
|
+
if (typeof SharedArrayBuffer === "function" &&
|
|
754
|
+
backing instanceof SharedArrayBuffer) {
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
// Narrowed by the check above: what is left is a detachable ArrayBuffer.
|
|
758
|
+
const store = backing;
|
|
759
|
+
// A move takes the whole backing store, not the slice that was returned.
|
|
760
|
+
// `return scratch.subarray(0, n)` would detach the producer's entire
|
|
761
|
+
// scratch buffer and break its next call, so a partial view is copied
|
|
762
|
+
// like any other return. Only a view that *is* its buffer can move.
|
|
763
|
+
if (ArrayBuffer.isView(source) &&
|
|
764
|
+
(source.byteOffset !== 0 || source.byteLength !== store.byteLength)) {
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
// Moving is an optimization, so a source this runtime cannot detach --
|
|
768
|
+
// WASM memory, a buffer an external API pinned -- copies rather than
|
|
769
|
+
// failing the task. Construction is what detaches: a throw before it
|
|
770
|
+
// leaves the bytes intact and the copy path still works, while a throw
|
|
771
|
+
// after it has already consumed them has to be reported.
|
|
772
|
+
let reference;
|
|
773
|
+
try {
|
|
774
|
+
reference = new BufferReference(source);
|
|
775
|
+
}
|
|
776
|
+
catch (error) {
|
|
777
|
+
if (isArrayBufferDetached(store))
|
|
778
|
+
throw error;
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
781
|
+
// Belt and braces for a runtime that reports success without detaching.
|
|
782
|
+
// The host would be handed a store still aliasing memory the producer can
|
|
783
|
+
// write; the source is intact in that case, so dropping the reference and
|
|
784
|
+
// copying is both safe and the honest answer.
|
|
785
|
+
if (!isArrayBufferDetached(store)) {
|
|
786
|
+
reference.release();
|
|
787
|
+
return false;
|
|
788
|
+
}
|
|
789
|
+
const words = BufferReference.prototype[BUFFER_REFERENCE_NUMERIC_TRANSFER]
|
|
790
|
+
.call(reference);
|
|
791
|
+
// The size check above guarantees the numeric form for a local reference.
|
|
792
|
+
// Do not fall back after construction: the source has intentionally moved.
|
|
793
|
+
if (words === undefined) {
|
|
794
|
+
reference.release();
|
|
795
|
+
throw new Error("Moved BufferReference has no numeric transport form");
|
|
796
|
+
}
|
|
797
|
+
task[TaskIndex.Type] = type;
|
|
798
|
+
task[TaskIndex.PayloadLen] = writeStaticU32Words(words, BUFFER_REFERENCE_NUMERIC_WORDS, slotIndex);
|
|
799
|
+
attachPayloadTransportFinalizer(task, reference);
|
|
800
|
+
task.value = null;
|
|
801
|
+
return true;
|
|
802
|
+
};
|
|
622
803
|
const tryEncodeSharedArrayBufferNumeric = (task, slotIndex, value) => {
|
|
623
804
|
const words = value[SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER]?.(lockSector);
|
|
624
805
|
if (words === undefined)
|
|
@@ -719,6 +900,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
719
900
|
if (reserveBytes < 0)
|
|
720
901
|
return false;
|
|
721
902
|
const reservedSlot = reserveDynamicObject(task, reserveBytes);
|
|
903
|
+
if (reservedSlot === -1)
|
|
904
|
+
return false;
|
|
722
905
|
const written = writeDynamicUtf8(text, task[TaskIndex.Start], reserveBytes);
|
|
723
906
|
if (written < 0)
|
|
724
907
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -728,6 +911,48 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
728
911
|
task.value = null;
|
|
729
912
|
return true;
|
|
730
913
|
};
|
|
914
|
+
/** Encode plain objects, arrays, and null-prototype objects as JSON. */
|
|
915
|
+
const encodeObjectJson = (task, slotIndex, objectValue) => {
|
|
916
|
+
let text;
|
|
917
|
+
try {
|
|
918
|
+
text = stringifyJSON(objectValue);
|
|
919
|
+
}
|
|
920
|
+
catch (error) {
|
|
921
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
922
|
+
return encoderError({
|
|
923
|
+
task,
|
|
924
|
+
type: ErrorKnitting.Json,
|
|
925
|
+
onPromise,
|
|
926
|
+
detail,
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
if (text.length <= staticMaxBytes) {
|
|
930
|
+
const written = writeStaticUtf8(text, slotIndex);
|
|
931
|
+
if (written !== -1) {
|
|
932
|
+
task[TaskIndex.Type] = PayloadBuffer.StaticJson;
|
|
933
|
+
task[TaskIndex.PayloadLen] = written;
|
|
934
|
+
// Retain any transport-owned memory until the call settles.
|
|
935
|
+
attachPayloadTransportFinalizer(task, objectValue);
|
|
936
|
+
task.value = null;
|
|
937
|
+
return true;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
task[TaskIndex.Type] = PayloadBuffer.Json;
|
|
941
|
+
const reserveBytes = dynamicUtf8ReserveBytes(task, text, "Json");
|
|
942
|
+
if (reserveBytes < 0)
|
|
943
|
+
return false;
|
|
944
|
+
const reservedSlot = reserveDynamicObject(task, reserveBytes);
|
|
945
|
+
if (reservedSlot === -1)
|
|
946
|
+
return false;
|
|
947
|
+
const written = writeDynamicUtf8(text, task[TaskIndex.Start], reserveBytes);
|
|
948
|
+
if (written < 0)
|
|
949
|
+
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
950
|
+
task[TaskIndex.PayloadLen] = written;
|
|
951
|
+
setSlotLength(reservedSlot, written);
|
|
952
|
+
attachPayloadTransportFinalizer(task, objectValue);
|
|
953
|
+
task.value = null;
|
|
954
|
+
return true;
|
|
955
|
+
};
|
|
731
956
|
const encodeObjectDate = (task, date) => {
|
|
732
957
|
Float64View[0] = date.getTime();
|
|
733
958
|
task[TaskIndex.Type] = PayloadBuffer.Date;
|
|
@@ -799,6 +1024,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
799
1024
|
if (!ensureWithinDynamicLimit(task, payloadReserveBytes, "EnvelopeStaticHeaderPayload"))
|
|
800
1025
|
return false;
|
|
801
1026
|
const reservedSlot = reserveDynamicObject(task, payloadReserveBytes);
|
|
1027
|
+
if (reservedSlot === -1)
|
|
1028
|
+
return false;
|
|
802
1029
|
task[TaskIndex.Type] = headerIsString
|
|
803
1030
|
? PayloadBuffer.EnvelopeStaticHeaderString
|
|
804
1031
|
: PayloadBuffer.EnvelopeStaticHeader;
|
|
@@ -811,7 +1038,11 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
811
1038
|
}
|
|
812
1039
|
setSlotLength(reservedSlot, payloadWritten);
|
|
813
1040
|
}
|
|
1041
|
+
else {
|
|
1042
|
+
setSlotLength(reservedSlot, 0);
|
|
1043
|
+
}
|
|
814
1044
|
task.value = null;
|
|
1045
|
+
tagTaskSlot(task, reservedSlot);
|
|
815
1046
|
return true;
|
|
816
1047
|
}
|
|
817
1048
|
const headerReserveBytes = dynamicUtf8ReserveBytesWithExtra(task, headerText, payloadLength, headerIsString ? "EnvelopeDynamicHeaderString" : "EnvelopeDynamicHeader");
|
|
@@ -821,6 +1052,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
821
1052
|
? PayloadBuffer.EnvelopeDynamicHeaderString
|
|
822
1053
|
: PayloadBuffer.EnvelopeDynamicHeader;
|
|
823
1054
|
const reservedSlot = reserveDynamicObject(task, headerReserveBytes + payloadLength);
|
|
1055
|
+
if (reservedSlot === -1)
|
|
1056
|
+
return false;
|
|
824
1057
|
const baseStart = task[TaskIndex.Start];
|
|
825
1058
|
const writtenHeaderBytes = writeDynamicUtf8(headerText, baseStart, headerReserveBytes);
|
|
826
1059
|
if (writtenHeaderBytes < 0) {
|
|
@@ -836,6 +1069,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
836
1069
|
task[TaskIndex.End] = payloadLength;
|
|
837
1070
|
setSlotLength(reservedSlot, writtenHeaderBytes + payloadLength);
|
|
838
1071
|
task.value = null;
|
|
1072
|
+
tagTaskSlot(task, reservedSlot);
|
|
839
1073
|
return true;
|
|
840
1074
|
};
|
|
841
1075
|
const encodeEnvelopeExternalBody = (task, slotIndex, headerText, headerIsString, externalBody, trustedReservedCodec) => {
|
|
@@ -901,6 +1135,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
901
1135
|
if (!ensureWithinDynamicLimit(task, bodyLength, "EnvelopeStaticHeaderExternal"))
|
|
902
1136
|
return false;
|
|
903
1137
|
const reservedSlot = reserveDynamicObject(task, bodyLength);
|
|
1138
|
+
if (reservedSlot === -1)
|
|
1139
|
+
return false;
|
|
904
1140
|
task[TaskIndex.Type] = headerIsString
|
|
905
1141
|
? PayloadBuffer.EnvelopeStaticHeaderStringExternal
|
|
906
1142
|
: PayloadBuffer.EnvelopeStaticHeaderExternal;
|
|
@@ -913,6 +1149,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
913
1149
|
setSlotLength(reservedSlot, bodyWritten);
|
|
914
1150
|
attachPayloadTransportFinalizer(task, externalBody);
|
|
915
1151
|
task.value = null;
|
|
1152
|
+
tagTaskSlot(task, reservedSlot);
|
|
916
1153
|
return true;
|
|
917
1154
|
}
|
|
918
1155
|
const headerReserveBytes = dynamicUtf8ReserveBytesWithExtra(task, headerText, bodyLength, headerIsString
|
|
@@ -924,6 +1161,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
924
1161
|
? PayloadBuffer.EnvelopeDynamicHeaderStringExternal
|
|
925
1162
|
: PayloadBuffer.EnvelopeDynamicHeaderExternal;
|
|
926
1163
|
const reservedSlot = reserveDynamicObject(task, headerReserveBytes + bodyLength);
|
|
1164
|
+
if (reservedSlot === -1)
|
|
1165
|
+
return false;
|
|
927
1166
|
const baseStart = task[TaskIndex.Start];
|
|
928
1167
|
const writtenHeaderBytes = writeDynamicUtf8(headerText, baseStart, headerReserveBytes);
|
|
929
1168
|
if (writtenHeaderBytes < 0) {
|
|
@@ -938,6 +1177,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
938
1177
|
setSlotLength(reservedSlot, writtenHeaderBytes + bodyLength);
|
|
939
1178
|
attachPayloadTransportFinalizer(task, externalBody);
|
|
940
1179
|
task.value = null;
|
|
1180
|
+
tagTaskSlot(task, reservedSlot);
|
|
941
1181
|
return true;
|
|
942
1182
|
};
|
|
943
1183
|
const encodeObjectEnvelope = (task, slotIndex, envelope) => {
|
|
@@ -1005,6 +1245,10 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1005
1245
|
return false;
|
|
1006
1246
|
}
|
|
1007
1247
|
const reservedSlot = reserveDynamic(task, binaryBytes);
|
|
1248
|
+
if (reservedSlot === -1) {
|
|
1249
|
+
clearBigIntScratch(binaryBytes);
|
|
1250
|
+
return false;
|
|
1251
|
+
}
|
|
1008
1252
|
const written = writeDynamicBinary(binary, task[TaskIndex.Start]);
|
|
1009
1253
|
if (written < 0) {
|
|
1010
1254
|
clearBigIntScratch(binaryBytes);
|
|
@@ -1026,57 +1270,23 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1026
1270
|
objectDynamicSlot = -1;
|
|
1027
1271
|
try {
|
|
1028
1272
|
const objectValue = args;
|
|
1273
|
+
const objectProto = objectGetPrototypeOf(objectValue);
|
|
1274
|
+
// Plain objects can skip the typed-array and external-payload checks.
|
|
1275
|
+
if (objectProto === objectPrototype) {
|
|
1276
|
+
return encodeObjectJson(task, slotIndex, objectValue);
|
|
1277
|
+
}
|
|
1029
1278
|
const sharedArrayBufferPayload = getSharedArrayBufferPayload(objectValue);
|
|
1030
1279
|
if (sharedArrayBufferPayload !== undefined) {
|
|
1031
1280
|
return encodeObjectExternalPayload(task, slotIndex, sharedArrayBufferPayload, true);
|
|
1032
1281
|
}
|
|
1033
|
-
const objectProto = objectGetPrototypeOf(objectValue);
|
|
1034
1282
|
if (isRuntimeUint8Array(objectValue)) {
|
|
1035
1283
|
return encodeObjectUint8Array(task, slotIndex, objectValue);
|
|
1036
1284
|
}
|
|
1037
1285
|
if (arrayIsArray(objectValue) && isNumericArray(objectValue)) {
|
|
1038
1286
|
return encodeObjectNumericArray(task, slotIndex, objectValue);
|
|
1039
1287
|
}
|
|
1040
|
-
if (arrayIsArray(objectValue) ||
|
|
1041
|
-
|
|
1042
|
-
objectProto === null) {
|
|
1043
|
-
let text;
|
|
1044
|
-
try {
|
|
1045
|
-
text = stringifyJSON(objectValue);
|
|
1046
|
-
}
|
|
1047
|
-
catch (error) {
|
|
1048
|
-
const detail = error instanceof Error
|
|
1049
|
-
? error.message
|
|
1050
|
-
: String(error);
|
|
1051
|
-
return encoderError({
|
|
1052
|
-
task,
|
|
1053
|
-
type: ErrorKnitting.Json,
|
|
1054
|
-
onPromise,
|
|
1055
|
-
detail,
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
|
-
if (text.length <= staticMaxBytes) {
|
|
1059
|
-
const written = writeStaticUtf8(text, slotIndex);
|
|
1060
|
-
if (written !== -1) {
|
|
1061
|
-
task[TaskIndex.Type] = PayloadBuffer.StaticJson;
|
|
1062
|
-
task[TaskIndex.PayloadLen] = written;
|
|
1063
|
-
task.value = null;
|
|
1064
|
-
return true;
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
task[TaskIndex.Type] = PayloadBuffer.Json;
|
|
1068
|
-
const reserveBytes = dynamicUtf8ReserveBytes(task, text, "Json");
|
|
1069
|
-
if (reserveBytes < 0)
|
|
1070
|
-
return false;
|
|
1071
|
-
const reservedSlot = reserveDynamicObject(task, reserveBytes);
|
|
1072
|
-
const written = writeDynamicUtf8(text, task[TaskIndex.Start], reserveBytes);
|
|
1073
|
-
if (written < 0) {
|
|
1074
|
-
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
1075
|
-
}
|
|
1076
|
-
task[TaskIndex.PayloadLen] = written;
|
|
1077
|
-
setSlotLength(reservedSlot, written);
|
|
1078
|
-
task.value = null;
|
|
1079
|
-
return true;
|
|
1288
|
+
if (arrayIsArray(objectValue) || objectProto === null) {
|
|
1289
|
+
return encodeObjectJson(task, slotIndex, objectValue);
|
|
1080
1290
|
}
|
|
1081
1291
|
if (isBufferReferenceValue(objectValue) ||
|
|
1082
1292
|
isProcessSharedBufferValue(objectValue)) {
|
|
@@ -1165,6 +1375,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1165
1375
|
if (reserveBytes < 0)
|
|
1166
1376
|
return false;
|
|
1167
1377
|
const reservedSlot = reserveDynamic(task, reserveBytes);
|
|
1378
|
+
if (reservedSlot === -1)
|
|
1379
|
+
return false;
|
|
1168
1380
|
const written = writeDynamicUtf8(text, task[TaskIndex.Start], reserveBytes);
|
|
1169
1381
|
if (written < 0) {
|
|
1170
1382
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -1197,6 +1409,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1197
1409
|
if (reserveBytes < 0)
|
|
1198
1410
|
return false;
|
|
1199
1411
|
const reservedSlot = reserveDynamic(task, reserveBytes);
|
|
1412
|
+
if (reservedSlot === -1)
|
|
1413
|
+
return false;
|
|
1200
1414
|
const written = writeDynamicUtf8(key, task[TaskIndex.Start], reserveBytes);
|
|
1201
1415
|
if (written < 0) {
|
|
1202
1416
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -1218,8 +1432,11 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1218
1432
|
options: payload?.config ?? payloadConfig,
|
|
1219
1433
|
});
|
|
1220
1434
|
const { free } = register({ lockSector });
|
|
1221
|
-
|
|
1222
|
-
|
|
1435
|
+
// Region identity is 6 bits only for dynamic payloads. Its high bit rides in
|
|
1436
|
+
// End's otherwise-unused top bit, preserving the 5-bit queue-slot field.
|
|
1437
|
+
const freeTaskSlot = (task) => free((task[TaskIndex.slotBuffer] & TASK_SLOT_INDEX_MASK) |
|
|
1438
|
+
((task[TaskIndex.End] >>> 31) << 5));
|
|
1439
|
+
const { readUtf8: readDynamicUtf8, readBytesCopy: readDynamicBytesCopy, readBytesBufferCopy: readDynamicBufferCopy, readBufferCopy: readDynamicBuffer, readBytesArrayBufferCopy: readDynamicArrayBufferCopy, readArrayBufferCopy: readDynamicArrayBuffer, read8BytesFloatCopy: readDynamic8BytesFloatCopy, read8BytesFloatView: readDynamic8BytesFloatView, readBytesView: readDynamicBytesView, syncGrowth: syncDynamicGrowth, } = createSharedDynamicBufferIO({
|
|
1223
1440
|
sab: payloadSab,
|
|
1224
1441
|
payloadConfig: resolvedPayloadConfig,
|
|
1225
1442
|
textCompat: textCompat?.payload,
|
|
@@ -1230,10 +1447,13 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1230
1447
|
// synchronously when building the ProcessSharedBuffer.
|
|
1231
1448
|
const processSharedBufferWords = new Uint32Array(PROCESS_SHARED_BUFFER_NUMERIC_WORDS);
|
|
1232
1449
|
const sharedArrayBufferWords = new Uint32Array(SHARED_ARRAY_BUFFER_NUMERIC_WORDS);
|
|
1450
|
+
// `readStaticU32Words` returns the whole scratch, and the SAB codec dispatches
|
|
1451
|
+
// on word count, so hand it a view cut to the frame's own length.
|
|
1452
|
+
const sharedArrayBufferWordViews = [];
|
|
1453
|
+
for (let count = 0; count <= SHARED_ARRAY_BUFFER_NUMERIC_WORDS; count++) {
|
|
1454
|
+
sharedArrayBufferWordViews.push(sharedArrayBufferWords.subarray(0, count));
|
|
1455
|
+
}
|
|
1233
1456
|
const bufferReferenceWords = new Uint32Array(BUFFER_REFERENCE_NUMERIC_WORDS);
|
|
1234
|
-
// TODO: remove slotIndex and make that all their callers
|
|
1235
|
-
// store the slot in their Task, to just get it when it comes
|
|
1236
|
-
// to the static versions of decoding
|
|
1237
1457
|
return (task, slotIndex, specialFlags) => {
|
|
1238
1458
|
const payloadType = task[TaskIndex.Type];
|
|
1239
1459
|
if (processBoundary &&
|
|
@@ -1287,7 +1507,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1287
1507
|
const header = task[TaskIndex.Type] === PayloadBuffer.EnvelopeStaticHeaderString
|
|
1288
1508
|
? rawHeader
|
|
1289
1509
|
: parseJSON(rawHeader);
|
|
1290
|
-
const payloadLength = task[TaskIndex.End];
|
|
1510
|
+
const payloadLength = task[TaskIndex.End] & 0x7FFFFFFF;
|
|
1291
1511
|
const payload = payloadLength > 0
|
|
1292
1512
|
? readDynamicArrayBufferCopy(task[TaskIndex.Start], task[TaskIndex.Start] + payloadLength)
|
|
1293
1513
|
: new ArrayBuffer(0);
|
|
@@ -1299,7 +1519,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1299
1519
|
case PayloadBuffer.EnvelopeDynamicHeaderString: {
|
|
1300
1520
|
const headerStart = task[TaskIndex.Start];
|
|
1301
1521
|
const payloadStart = headerStart + task[TaskIndex.PayloadLen];
|
|
1302
|
-
const payloadLength = task[TaskIndex.End];
|
|
1522
|
+
const payloadLength = task[TaskIndex.End] & 0x7FFFFFFF;
|
|
1303
1523
|
const rawHeader = readDynamicUtf8(headerStart, payloadStart);
|
|
1304
1524
|
const header = task[TaskIndex.Type] === PayloadBuffer.EnvelopeDynamicHeaderString
|
|
1305
1525
|
? rawHeader
|
|
@@ -1319,7 +1539,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1319
1539
|
? rawHeader
|
|
1320
1540
|
: parseJSON(rawHeader);
|
|
1321
1541
|
const bodyStart = task[TaskIndex.Start];
|
|
1322
|
-
const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]), processBoundary);
|
|
1542
|
+
const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + (task[TaskIndex.End] & 0x7FFFFFFF)), processBoundary);
|
|
1323
1543
|
task.value = new Envelope(header, body);
|
|
1324
1544
|
freeTaskSlot(task);
|
|
1325
1545
|
return;
|
|
@@ -1333,7 +1553,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1333
1553
|
PayloadBuffer.EnvelopeDynamicHeaderStringExternal
|
|
1334
1554
|
? rawHeader
|
|
1335
1555
|
: parseJSON(rawHeader);
|
|
1336
|
-
const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]), processBoundary);
|
|
1556
|
+
const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + (task[TaskIndex.End] & 0x7FFFFFFF)), processBoundary);
|
|
1337
1557
|
task.value = new Envelope(header, body);
|
|
1338
1558
|
freeTaskSlot(task);
|
|
1339
1559
|
return;
|
|
@@ -1413,21 +1633,39 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1413
1633
|
return;
|
|
1414
1634
|
}
|
|
1415
1635
|
case PayloadBuffer.ExternalPayload:
|
|
1416
|
-
task.value = decodeExternalPayload(readDynamicUtf8(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]), processBoundary);
|
|
1636
|
+
task.value = decodeExternalPayload(readDynamicUtf8(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]), processBoundary, lockSector);
|
|
1417
1637
|
freeTaskSlot(task);
|
|
1418
1638
|
return;
|
|
1419
1639
|
case PayloadBuffer.StaticExternalPayload:
|
|
1420
|
-
task.value = decodeExternalPayload(readStaticUtf8(0, task[TaskIndex.PayloadLen], slotIndex), processBoundary);
|
|
1640
|
+
task.value = decodeExternalPayload(readStaticUtf8(0, task[TaskIndex.PayloadLen], slotIndex), processBoundary, lockSector);
|
|
1421
1641
|
return;
|
|
1422
1642
|
case PayloadBuffer.ProcessSharedBuffer:
|
|
1423
1643
|
task.value = decodeProcessSharedBufferNumericWords(readStaticU32Words(processSharedBufferWords, PROCESS_SHARED_BUFFER_NUMERIC_WORDS, slotIndex));
|
|
1424
1644
|
return;
|
|
1425
|
-
case PayloadBuffer.SharedArrayBuffer:
|
|
1426
|
-
|
|
1645
|
+
case PayloadBuffer.SharedArrayBuffer: {
|
|
1646
|
+
const wordCount = task[TaskIndex.PayloadLen] >>> 2;
|
|
1647
|
+
task.value = decodeSharedArrayBufferNumericWords(readStaticU32Words(sharedArrayBufferWordViews[wordCount] ?? sharedArrayBufferWords, wordCount, slotIndex), lockSector);
|
|
1427
1648
|
return;
|
|
1649
|
+
}
|
|
1428
1650
|
case PayloadBuffer.BufferReference:
|
|
1429
1651
|
task.value = decodeBufferReferenceNumericWords(readStaticU32Words(bufferReferenceWords, BUFFER_REFERENCE_NUMERIC_WORDS, slotIndex));
|
|
1430
1652
|
return;
|
|
1653
|
+
case PayloadBuffer.MovedBinary: {
|
|
1654
|
+
const reference = decodeBufferReferenceNumericWords(readStaticU32Words(bufferReferenceWords, BUFFER_REFERENCE_NUMERIC_WORDS, slotIndex));
|
|
1655
|
+
if (!isBufferReferenceValue(reference)) {
|
|
1656
|
+
throw new TypeError("Invalid moved Uint8Array payload");
|
|
1657
|
+
}
|
|
1658
|
+
task.value = reference.toUint8Array();
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
case PayloadBuffer.MovedArrayBuffer: {
|
|
1662
|
+
const reference = decodeBufferReferenceNumericWords(readStaticU32Words(bufferReferenceWords, BUFFER_REFERENCE_NUMERIC_WORDS, slotIndex));
|
|
1663
|
+
if (!isBufferReferenceValue(reference)) {
|
|
1664
|
+
throw new TypeError("Invalid moved ArrayBuffer payload");
|
|
1665
|
+
}
|
|
1666
|
+
task.value = reference.toArrayBuffer();
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1431
1669
|
case PayloadBuffer.Date:
|
|
1432
1670
|
Uint32View[0] = task[TaskIndex.Start];
|
|
1433
1671
|
Uint32View[1] = task[TaskIndex.End];
|
|
@@ -1437,6 +1675,12 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1437
1675
|
task.value = parseErrorPayload(readDynamicUtf8(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]));
|
|
1438
1676
|
freeTaskSlot(task);
|
|
1439
1677
|
return;
|
|
1678
|
+
// Borrowed region: decode a view into the shared arena.
|
|
1679
|
+
case PayloadBuffer.ArenaBinary:
|
|
1680
|
+
// A borrowed region can sit past the length this endpoint last saw.
|
|
1681
|
+
syncDynamicGrowth();
|
|
1682
|
+
task.value = readDynamicBytesView(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]);
|
|
1683
|
+
return;
|
|
1440
1684
|
case PayloadBuffer.Binary:
|
|
1441
1685
|
{
|
|
1442
1686
|
const buffer = readDynamicBufferCopy(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]);
|