knitting 0.1.63 → 0.1.73
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 +623 -342
- package/knitting.browser.d.ts +3 -1
- package/knitting.browser.js +1 -1
- package/knitting.d.ts +3 -1
- package/knitting.js +2 -1
- package/map.md +0 -6
- package/package.json +10 -5
- 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 +158 -71
- 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/node-addons.js +11 -1
- package/src/connections/shared-array-buffer-payload.d.ts +7 -0
- package/src/connections/shared-array-buffer-payload.js +27 -11
- package/src/debug/gate.js +1 -1
- package/src/debug/handle.d.ts +6 -1
- package/src/debug/handle.js +14 -6
- package/src/error.d.ts +9 -0
- package/src/error.js +16 -2
- 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 +80 -15
- package/src/memory/lock.js +473 -139
- package/src/memory/payloadCodec.d.ts +18 -2
- package/src/memory/payloadCodec.js +340 -76
- 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/permission/protocol.d.ts +1 -0
- package/src/permission/protocol.js +8 -3
- package/src/runtime/deno-doorbell.d.ts +26 -0
- package/src/runtime/deno-doorbell.js +117 -0
- package/src/runtime/dispatcher.d.ts +13 -6
- package/src/runtime/dispatcher.js +101 -63
- package/src/runtime/host-arg-arena.d.ts +3 -0
- package/src/runtime/host-arg-arena.js +16 -0
- package/src/runtime/inline-executor.js +2 -1
- package/src/runtime/node-doorbell.d.ts +14 -0
- package/src/runtime/node-doorbell.js +84 -0
- package/src/runtime/pool.d.ts +30 -15
- package/src/runtime/pool.js +199 -151
- package/src/runtime/process-worker.d.ts +9 -0
- package/src/runtime/process-worker.js +32 -3
- package/src/runtime/tx-queue.d.ts +4 -6
- package/src/runtime/tx-queue.js +63 -48
- package/src/runtime/worker-common.d.ts +7 -0
- package/src/runtime/worker-common.js +28 -2
- package/src/types.d.ts +66 -78
- package/src/worker/loop.js +95 -60
- package/src/worker/rx-queue.d.ts +2 -3
- package/src/worker/rx-queue.js +34 -40
- package/src/worker/safety/index.d.ts +1 -1
- package/src/worker/safety/index.js +1 -1
- package/src/worker/safety/process.d.ts +2 -0
- package/src/worker/safety/process.js +8 -1
- package/src/worker/safety/startup.js +11 -6
- 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 +39 -22
- 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);
|
|
@@ -39,7 +40,7 @@ const objectGetPrototypeOf = Object.getPrototypeOf;
|
|
|
39
40
|
const objectHasOwn = Object.prototype.hasOwnProperty;
|
|
40
41
|
const arrayIsArray = Array.isArray;
|
|
41
42
|
const objectPrototype = Object.prototype;
|
|
42
|
-
const UNSUPPORTED_OBJECT_DETAIL = "Unsupported object type. Allowed: plain object, array, Error, Date, Envelope, Buffer, ArrayBuffer, DataView,
|
|
43
|
+
const UNSUPPORTED_OBJECT_DETAIL = "Unsupported object type. Allowed: plain object, array, Error, Date, Envelope, Buffer, ArrayBuffer, DataView, Uint8Array, Int32Array, Float64Array, BigInt64Array, BigUint64Array, and registered external payloads. Serialize it yourself.";
|
|
43
44
|
const ENVELOPE_PAYLOAD_DETAIL = "Envelope payload must be an ArrayBuffer, SharedArrayBuffer, " +
|
|
44
45
|
"ProcessSharedBuffer, or BufferReference.";
|
|
45
46
|
const ENVELOPE_HEADER_DETAIL = "Envelope header must be a JSON-like value or string.";
|
|
@@ -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) {
|
|
@@ -240,18 +252,23 @@ const toErrorPayload = (error) => {
|
|
|
240
252
|
}
|
|
241
253
|
return payload;
|
|
242
254
|
};
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
255
|
+
// The shape `toErrorCause` writes for an Error cause. A plain-object cause
|
|
256
|
+
// with any other key is left as the object it was.
|
|
257
|
+
const isErrorPayload = (value) => {
|
|
258
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
259
|
+
return false;
|
|
247
260
|
}
|
|
248
|
-
|
|
249
|
-
|
|
261
|
+
const payload = value;
|
|
262
|
+
if (typeof payload.name !== "string" || typeof payload.message !== "string") {
|
|
263
|
+
return false;
|
|
250
264
|
}
|
|
251
|
-
|
|
252
|
-
|
|
265
|
+
for (const key in payload) {
|
|
266
|
+
if (key !== "name" && key !== "message" && key !== "stack" && key !== "cause")
|
|
267
|
+
return false;
|
|
253
268
|
}
|
|
254
|
-
|
|
269
|
+
return true;
|
|
270
|
+
};
|
|
271
|
+
const reviveErrorPayload = (payload) => {
|
|
255
272
|
const err = new Error(typeof payload.message === "string" ? payload.message : "");
|
|
256
273
|
if (typeof payload.name === "string" && payload.name.length > 0) {
|
|
257
274
|
err.name = payload.name;
|
|
@@ -264,10 +281,25 @@ const parseErrorPayload = (raw) => {
|
|
|
264
281
|
}
|
|
265
282
|
}
|
|
266
283
|
if (objectHasOwn.call(payload, "cause")) {
|
|
267
|
-
err.cause = payload.cause
|
|
284
|
+
err.cause = isErrorPayload(payload.cause)
|
|
285
|
+
? reviveErrorPayload(payload.cause)
|
|
286
|
+
: payload.cause;
|
|
268
287
|
}
|
|
269
288
|
return err;
|
|
270
289
|
};
|
|
290
|
+
const parseErrorPayload = (raw) => {
|
|
291
|
+
let parsed;
|
|
292
|
+
try {
|
|
293
|
+
parsed = parseJSON(raw);
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
return new Error(raw);
|
|
297
|
+
}
|
|
298
|
+
if (parsed == null || typeof parsed !== "object") {
|
|
299
|
+
return new Error(String(parsed));
|
|
300
|
+
}
|
|
301
|
+
return reviveErrorPayload(parsed);
|
|
302
|
+
};
|
|
271
303
|
const decodeBigIntBinary = (bytes) => {
|
|
272
304
|
const sign = bytes[0];
|
|
273
305
|
let value = 0n;
|
|
@@ -304,15 +336,28 @@ const requireStaticIO = (headersBuffer, headerSlotStrideU32, textCompat) => {
|
|
|
304
336
|
* Returns `true` when the payload is encoded successfully.
|
|
305
337
|
* Returns `false` when dynamic payload space could not be reserved.
|
|
306
338
|
*/
|
|
307
|
-
export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, onPromise, processBoundary = false, }) => {
|
|
339
|
+
export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, onPromise, processBoundary = false, sharedReturn = false, moveReturn = false, }) => {
|
|
308
340
|
const payloadSab = payload?.sab ?? sab;
|
|
309
341
|
const resolvedPayloadConfig = resolvePayloadBufferOptions({
|
|
310
342
|
sab: payloadSab,
|
|
311
343
|
options: payload?.config ?? payloadConfig,
|
|
312
344
|
});
|
|
313
345
|
const maxPayloadBytes = resolvedPayloadConfig.maxPayloadBytes;
|
|
314
|
-
|
|
315
|
-
|
|
346
|
+
// Automatic moves are an optimization. Keep ordinary byte returns working
|
|
347
|
+
// when this runtime has no usable ownership/FFI backend.
|
|
348
|
+
const canMoveReturn = moveReturn && (() => {
|
|
349
|
+
try {
|
|
350
|
+
getBufferReferenceCapabilities();
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
})();
|
|
357
|
+
const registryHandle = register({ lockSector });
|
|
358
|
+
const { allocTask, setSlotLength, tagTaskSlot, free } = registryHandle;
|
|
359
|
+
const { allocRegion, regionStart } = registryHandle;
|
|
360
|
+
const { writeBinary: writeDynamicBinary, writeBuffer: writeDynamicBuffer, writeArrayBuffer: writeDynamicArrayBuffer, write8Binary: writeDynamic8Binary, writeUtf8: writeDynamicUtf8, readBytesView: dynamicRegionView, ensureCapacity: ensureDynamicCapacity, currentBuffer: currentDynamicBuffer, regionBase: dynamicRegionBase, } = createSharedDynamicBufferIO({
|
|
316
361
|
sab: payloadSab,
|
|
317
362
|
payloadConfig: resolvedPayloadConfig,
|
|
318
363
|
textCompat: textCompat?.payload,
|
|
@@ -350,9 +395,72 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
350
395
|
return exactBytes;
|
|
351
396
|
};
|
|
352
397
|
const dynamicUtf8ReserveBytes = (task, text, label) => dynamicUtf8ReserveBytesWithExtra(task, text, 0, label);
|
|
398
|
+
// Borrowed regions are tracked as [slot, start, end] triples, oldest first.
|
|
399
|
+
const borrowed = [];
|
|
400
|
+
const BORROW_STRIDE = 3;
|
|
401
|
+
/** Limit borrowed regions to half the payload arena. */
|
|
402
|
+
const maxBorrowBytes = ((resolvedPayloadConfig.payloadMaxByteLength >> 1) /
|
|
403
|
+
SHARED_RETURN_BORROW_WINDOW) | 0;
|
|
404
|
+
/** Reserve a region to hand over by reference, or -1 when none is free. */
|
|
405
|
+
const reserveBorrowedRegion = (bytes) => {
|
|
406
|
+
if (bytes > maxBorrowBytes)
|
|
407
|
+
return -1;
|
|
408
|
+
const slot = allocRegion(bytes);
|
|
409
|
+
if (slot === -1)
|
|
410
|
+
return -1;
|
|
411
|
+
const start = regionStart(slot);
|
|
412
|
+
if (!ensureDynamicCapacity(start + bytes)) {
|
|
413
|
+
free(slot);
|
|
414
|
+
return -1;
|
|
415
|
+
}
|
|
416
|
+
borrowed.push(slot, start, start + bytes);
|
|
417
|
+
if (borrowed.length > SHARED_RETURN_BORROW_WINDOW * BORROW_STRIDE) {
|
|
418
|
+
free(borrowed[0]);
|
|
419
|
+
borrowed.splice(0, BORROW_STRIDE);
|
|
420
|
+
}
|
|
421
|
+
return slot;
|
|
422
|
+
};
|
|
423
|
+
const dropBorrowedRegion = (slot) => {
|
|
424
|
+
for (let at = 0; at < borrowed.length; at += BORROW_STRIDE) {
|
|
425
|
+
if (borrowed[at] === slot) {
|
|
426
|
+
borrowed.splice(at, BORROW_STRIDE);
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
free(slot);
|
|
431
|
+
};
|
|
432
|
+
/** Return the arena offset when `view` is inside a live borrowed region. */
|
|
433
|
+
const borrowedStartOf = (view) => {
|
|
434
|
+
if (borrowed.length === 0)
|
|
435
|
+
return -1;
|
|
436
|
+
if (view.buffer !== currentDynamicBuffer())
|
|
437
|
+
return -1;
|
|
438
|
+
const start = view.byteOffset - dynamicRegionBase();
|
|
439
|
+
const end = start + view.byteLength;
|
|
440
|
+
for (let at = borrowed.length - BORROW_STRIDE; at >= 0; at -= BORROW_STRIDE) {
|
|
441
|
+
if (start >= borrowed[at + 1] && end <= borrowed[at + 2])
|
|
442
|
+
return start;
|
|
443
|
+
}
|
|
444
|
+
return -1;
|
|
445
|
+
};
|
|
446
|
+
/** Allocate a borrowed return region, or fall back when none is available. */
|
|
447
|
+
const allocateSharedReturn = (byteLength, zeroFill = false) => {
|
|
448
|
+
const slot = reserveBorrowedRegion(byteLength);
|
|
449
|
+
if (slot === -1)
|
|
450
|
+
return undefined;
|
|
451
|
+
const start = regionStart(slot);
|
|
452
|
+
const view = dynamicRegionView(start, start + byteLength);
|
|
453
|
+
if (zeroFill)
|
|
454
|
+
view.fill(0);
|
|
455
|
+
return view;
|
|
456
|
+
};
|
|
457
|
+
if (sharedReturn && payloadSab !== undefined) {
|
|
458
|
+
sharedReturnAllocators.set(payloadSab, allocateSharedReturn);
|
|
459
|
+
}
|
|
353
460
|
const reserveDynamic = (task, bytes) => {
|
|
354
461
|
task[TaskIndex.PayloadLen] = bytes;
|
|
355
|
-
//
|
|
462
|
+
// -1 is dynamic-region backpressure. Returning false leaves this frame on
|
|
463
|
+
// the lock's existing pending list until a receiver releases a region.
|
|
356
464
|
return allocTask(task);
|
|
357
465
|
};
|
|
358
466
|
let objectDynamicSlot = -1;
|
|
@@ -419,6 +527,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
419
527
|
return false;
|
|
420
528
|
task[TaskIndex.Type] = PayloadBuffer.Error;
|
|
421
529
|
const reservedSlot = reserveDynamicObject(task, reserveBytes);
|
|
530
|
+
if (reservedSlot === -1)
|
|
531
|
+
return false;
|
|
422
532
|
const written = writeDynamicUtf8(text, task[TaskIndex.Start], reserveBytes);
|
|
423
533
|
if (written < 0)
|
|
424
534
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -443,6 +553,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
443
553
|
return false;
|
|
444
554
|
}
|
|
445
555
|
const reservedSlot = reserveDynamicObject(task, bytes);
|
|
556
|
+
if (reservedSlot === -1)
|
|
557
|
+
return false;
|
|
446
558
|
const written = writeDynamicBinary(bytesView, task[TaskIndex.Start]);
|
|
447
559
|
if (written < 0)
|
|
448
560
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -453,6 +565,15 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
453
565
|
};
|
|
454
566
|
const encodeObjectUint8Array = (task, slotIndex, bytesView) => {
|
|
455
567
|
const bytes = bytesView.byteLength;
|
|
568
|
+
// Preserve an arena-backed subarray as an offset/length frame.
|
|
569
|
+
const borrowedStart = borrowedStartOf(bytesView);
|
|
570
|
+
if (borrowedStart !== -1) {
|
|
571
|
+
task[TaskIndex.Type] = PayloadBuffer.ArenaBinary;
|
|
572
|
+
task[TaskIndex.Start] = borrowedStart;
|
|
573
|
+
task[TaskIndex.PayloadLen] = bytes;
|
|
574
|
+
task.value = null;
|
|
575
|
+
return true;
|
|
576
|
+
}
|
|
456
577
|
if (bytes <= staticMaxBytes) {
|
|
457
578
|
writeStaticExactUint8Array(bytesView, slotIndex);
|
|
458
579
|
task[TaskIndex.Type] = PayloadBuffer.StaticBinary;
|
|
@@ -460,10 +581,16 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
460
581
|
task.value = null;
|
|
461
582
|
return true;
|
|
462
583
|
}
|
|
584
|
+
if (canMoveReturn && bytes >= SHARED_RETURN_MIN_BYTES &&
|
|
585
|
+
tryEncodeMovedReturn(task, slotIndex, bytesView, PayloadBuffer.MovedBinary)) {
|
|
586
|
+
return true;
|
|
587
|
+
}
|
|
463
588
|
task[TaskIndex.Type] = PayloadBuffer.Binary;
|
|
464
589
|
if (!ensureWithinDynamicLimit(task, bytes, "Binary"))
|
|
465
590
|
return false;
|
|
466
591
|
const reservedSlot = reserveDynamicObject(task, bytes);
|
|
592
|
+
if (reservedSlot === -1)
|
|
593
|
+
return false;
|
|
467
594
|
const written = writeDynamicBinary(bytesView, task[TaskIndex.Start]);
|
|
468
595
|
if (written < 0)
|
|
469
596
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -487,6 +614,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
487
614
|
if (!ensureWithinDynamicLimit(task, bytes, "Buffer"))
|
|
488
615
|
return false;
|
|
489
616
|
const reservedSlot = reserveDynamicObject(task, bytes);
|
|
617
|
+
if (reservedSlot === -1)
|
|
618
|
+
return false;
|
|
490
619
|
const written = writeDynamicBuffer(buffer, task[TaskIndex.Start]);
|
|
491
620
|
if (written < 0)
|
|
492
621
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -510,6 +639,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
510
639
|
if (!ensureWithinDynamicLimit(task, bytes, "Float64Array"))
|
|
511
640
|
return false;
|
|
512
641
|
const reservedSlot = reserveDynamicObject(task, bytes);
|
|
642
|
+
if (reservedSlot === -1)
|
|
643
|
+
return false;
|
|
513
644
|
const written = writeDynamic8Binary(float64, task[TaskIndex.Start]);
|
|
514
645
|
if (written < 0)
|
|
515
646
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -541,6 +672,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
541
672
|
if (!ensureWithinDynamicLimit(task, bytes, "NumericArray"))
|
|
542
673
|
return false;
|
|
543
674
|
const reservedSlot = reserveDynamicObject(task, bytes);
|
|
675
|
+
if (reservedSlot === -1)
|
|
676
|
+
return false;
|
|
544
677
|
const written = writeDynamic8Binary(float64, task[TaskIndex.Start]);
|
|
545
678
|
if (written < 0)
|
|
546
679
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -560,10 +693,16 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
560
693
|
return true;
|
|
561
694
|
}
|
|
562
695
|
}
|
|
696
|
+
if (canMoveReturn && bytes >= SHARED_RETURN_MIN_BYTES &&
|
|
697
|
+
tryEncodeMovedReturn(task, slotIndex, arrayBuffer, PayloadBuffer.MovedArrayBuffer)) {
|
|
698
|
+
return true;
|
|
699
|
+
}
|
|
563
700
|
task[TaskIndex.Type] = PayloadBuffer.ArrayBuffer;
|
|
564
701
|
if (!ensureWithinDynamicLimit(task, bytes, "ArrayBuffer"))
|
|
565
702
|
return false;
|
|
566
703
|
const reservedSlot = reserveDynamicObject(task, bytes);
|
|
704
|
+
if (reservedSlot === -1)
|
|
705
|
+
return false;
|
|
567
706
|
const written = writeDynamicArrayBuffer(arrayBuffer, task[TaskIndex.Start]);
|
|
568
707
|
if (written < 0)
|
|
569
708
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -619,6 +758,68 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
619
758
|
task.value = null;
|
|
620
759
|
return true;
|
|
621
760
|
};
|
|
761
|
+
/**
|
|
762
|
+
* The safe counterpart to an arena borrow. The producer's source is detached
|
|
763
|
+
* only after its task has settled; the host adopts the backing store before
|
|
764
|
+
* its acknowledgement lets the producer release the registry pin.
|
|
765
|
+
*
|
|
766
|
+
* This needs its own frame type because public task results remain ordinary
|
|
767
|
+
* Uint8Array / ArrayBuffer values rather than exposing BufferReference.
|
|
768
|
+
*/
|
|
769
|
+
const tryEncodeMovedReturn = (task, slotIndex, source, type) => {
|
|
770
|
+
if (source.byteLength > 0xffffffff)
|
|
771
|
+
return false;
|
|
772
|
+
const backing = ArrayBuffer.isView(source) ? source.buffer : source;
|
|
773
|
+
if (typeof SharedArrayBuffer === "function" &&
|
|
774
|
+
backing instanceof SharedArrayBuffer) {
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
777
|
+
// Narrowed by the check above: what is left is a detachable ArrayBuffer.
|
|
778
|
+
const store = backing;
|
|
779
|
+
// A move takes the whole backing store, not the slice that was returned.
|
|
780
|
+
// `return scratch.subarray(0, n)` would detach the producer's entire
|
|
781
|
+
// scratch buffer and break its next call, so a partial view is copied
|
|
782
|
+
// like any other return. Only a view that *is* its buffer can move.
|
|
783
|
+
if (ArrayBuffer.isView(source) &&
|
|
784
|
+
(source.byteOffset !== 0 || source.byteLength !== store.byteLength)) {
|
|
785
|
+
return false;
|
|
786
|
+
}
|
|
787
|
+
// Moving is an optimization, so a source this runtime cannot detach --
|
|
788
|
+
// WASM memory, a buffer an external API pinned -- copies rather than
|
|
789
|
+
// failing the task. Construction is what detaches: a throw before it
|
|
790
|
+
// leaves the bytes intact and the copy path still works, while a throw
|
|
791
|
+
// after it has already consumed them has to be reported.
|
|
792
|
+
let reference;
|
|
793
|
+
try {
|
|
794
|
+
reference = new BufferReference(source);
|
|
795
|
+
}
|
|
796
|
+
catch (error) {
|
|
797
|
+
if (isArrayBufferDetached(store))
|
|
798
|
+
throw error;
|
|
799
|
+
return false;
|
|
800
|
+
}
|
|
801
|
+
// Belt and braces for a runtime that reports success without detaching.
|
|
802
|
+
// The host would be handed a store still aliasing memory the producer can
|
|
803
|
+
// write; the source is intact in that case, so dropping the reference and
|
|
804
|
+
// copying is both safe and the honest answer.
|
|
805
|
+
if (!isArrayBufferDetached(store)) {
|
|
806
|
+
reference.release();
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
const words = BufferReference.prototype[BUFFER_REFERENCE_NUMERIC_TRANSFER]
|
|
810
|
+
.call(reference);
|
|
811
|
+
// The size check above guarantees the numeric form for a local reference.
|
|
812
|
+
// Do not fall back after construction: the source has intentionally moved.
|
|
813
|
+
if (words === undefined) {
|
|
814
|
+
reference.release();
|
|
815
|
+
throw new Error("Moved BufferReference has no numeric transport form");
|
|
816
|
+
}
|
|
817
|
+
task[TaskIndex.Type] = type;
|
|
818
|
+
task[TaskIndex.PayloadLen] = writeStaticU32Words(words, BUFFER_REFERENCE_NUMERIC_WORDS, slotIndex);
|
|
819
|
+
attachPayloadTransportFinalizer(task, reference);
|
|
820
|
+
task.value = null;
|
|
821
|
+
return true;
|
|
822
|
+
};
|
|
622
823
|
const tryEncodeSharedArrayBufferNumeric = (task, slotIndex, value) => {
|
|
623
824
|
const words = value[SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER]?.(lockSector);
|
|
624
825
|
if (words === undefined)
|
|
@@ -719,6 +920,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
719
920
|
if (reserveBytes < 0)
|
|
720
921
|
return false;
|
|
721
922
|
const reservedSlot = reserveDynamicObject(task, reserveBytes);
|
|
923
|
+
if (reservedSlot === -1)
|
|
924
|
+
return false;
|
|
722
925
|
const written = writeDynamicUtf8(text, task[TaskIndex.Start], reserveBytes);
|
|
723
926
|
if (written < 0)
|
|
724
927
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -728,6 +931,48 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
728
931
|
task.value = null;
|
|
729
932
|
return true;
|
|
730
933
|
};
|
|
934
|
+
/** Encode plain objects, arrays, and null-prototype objects as JSON. */
|
|
935
|
+
const encodeObjectJson = (task, slotIndex, objectValue) => {
|
|
936
|
+
let text;
|
|
937
|
+
try {
|
|
938
|
+
text = stringifyJSON(objectValue);
|
|
939
|
+
}
|
|
940
|
+
catch (error) {
|
|
941
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
942
|
+
return encoderError({
|
|
943
|
+
task,
|
|
944
|
+
type: ErrorKnitting.Json,
|
|
945
|
+
onPromise,
|
|
946
|
+
detail,
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
if (text.length <= staticMaxBytes) {
|
|
950
|
+
const written = writeStaticUtf8(text, slotIndex);
|
|
951
|
+
if (written !== -1) {
|
|
952
|
+
task[TaskIndex.Type] = PayloadBuffer.StaticJson;
|
|
953
|
+
task[TaskIndex.PayloadLen] = written;
|
|
954
|
+
// Retain any transport-owned memory until the call settles.
|
|
955
|
+
attachPayloadTransportFinalizer(task, objectValue);
|
|
956
|
+
task.value = null;
|
|
957
|
+
return true;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
task[TaskIndex.Type] = PayloadBuffer.Json;
|
|
961
|
+
const reserveBytes = dynamicUtf8ReserveBytes(task, text, "Json");
|
|
962
|
+
if (reserveBytes < 0)
|
|
963
|
+
return false;
|
|
964
|
+
const reservedSlot = reserveDynamicObject(task, reserveBytes);
|
|
965
|
+
if (reservedSlot === -1)
|
|
966
|
+
return false;
|
|
967
|
+
const written = writeDynamicUtf8(text, task[TaskIndex.Start], reserveBytes);
|
|
968
|
+
if (written < 0)
|
|
969
|
+
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
970
|
+
task[TaskIndex.PayloadLen] = written;
|
|
971
|
+
setSlotLength(reservedSlot, written);
|
|
972
|
+
attachPayloadTransportFinalizer(task, objectValue);
|
|
973
|
+
task.value = null;
|
|
974
|
+
return true;
|
|
975
|
+
};
|
|
731
976
|
const encodeObjectDate = (task, date) => {
|
|
732
977
|
Float64View[0] = date.getTime();
|
|
733
978
|
task[TaskIndex.Type] = PayloadBuffer.Date;
|
|
@@ -799,6 +1044,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
799
1044
|
if (!ensureWithinDynamicLimit(task, payloadReserveBytes, "EnvelopeStaticHeaderPayload"))
|
|
800
1045
|
return false;
|
|
801
1046
|
const reservedSlot = reserveDynamicObject(task, payloadReserveBytes);
|
|
1047
|
+
if (reservedSlot === -1)
|
|
1048
|
+
return false;
|
|
802
1049
|
task[TaskIndex.Type] = headerIsString
|
|
803
1050
|
? PayloadBuffer.EnvelopeStaticHeaderString
|
|
804
1051
|
: PayloadBuffer.EnvelopeStaticHeader;
|
|
@@ -811,7 +1058,11 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
811
1058
|
}
|
|
812
1059
|
setSlotLength(reservedSlot, payloadWritten);
|
|
813
1060
|
}
|
|
1061
|
+
else {
|
|
1062
|
+
setSlotLength(reservedSlot, 0);
|
|
1063
|
+
}
|
|
814
1064
|
task.value = null;
|
|
1065
|
+
tagTaskSlot(task, reservedSlot);
|
|
815
1066
|
return true;
|
|
816
1067
|
}
|
|
817
1068
|
const headerReserveBytes = dynamicUtf8ReserveBytesWithExtra(task, headerText, payloadLength, headerIsString ? "EnvelopeDynamicHeaderString" : "EnvelopeDynamicHeader");
|
|
@@ -821,6 +1072,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
821
1072
|
? PayloadBuffer.EnvelopeDynamicHeaderString
|
|
822
1073
|
: PayloadBuffer.EnvelopeDynamicHeader;
|
|
823
1074
|
const reservedSlot = reserveDynamicObject(task, headerReserveBytes + payloadLength);
|
|
1075
|
+
if (reservedSlot === -1)
|
|
1076
|
+
return false;
|
|
824
1077
|
const baseStart = task[TaskIndex.Start];
|
|
825
1078
|
const writtenHeaderBytes = writeDynamicUtf8(headerText, baseStart, headerReserveBytes);
|
|
826
1079
|
if (writtenHeaderBytes < 0) {
|
|
@@ -836,6 +1089,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
836
1089
|
task[TaskIndex.End] = payloadLength;
|
|
837
1090
|
setSlotLength(reservedSlot, writtenHeaderBytes + payloadLength);
|
|
838
1091
|
task.value = null;
|
|
1092
|
+
tagTaskSlot(task, reservedSlot);
|
|
839
1093
|
return true;
|
|
840
1094
|
};
|
|
841
1095
|
const encodeEnvelopeExternalBody = (task, slotIndex, headerText, headerIsString, externalBody, trustedReservedCodec) => {
|
|
@@ -901,6 +1155,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
901
1155
|
if (!ensureWithinDynamicLimit(task, bodyLength, "EnvelopeStaticHeaderExternal"))
|
|
902
1156
|
return false;
|
|
903
1157
|
const reservedSlot = reserveDynamicObject(task, bodyLength);
|
|
1158
|
+
if (reservedSlot === -1)
|
|
1159
|
+
return false;
|
|
904
1160
|
task[TaskIndex.Type] = headerIsString
|
|
905
1161
|
? PayloadBuffer.EnvelopeStaticHeaderStringExternal
|
|
906
1162
|
: PayloadBuffer.EnvelopeStaticHeaderExternal;
|
|
@@ -913,6 +1169,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
913
1169
|
setSlotLength(reservedSlot, bodyWritten);
|
|
914
1170
|
attachPayloadTransportFinalizer(task, externalBody);
|
|
915
1171
|
task.value = null;
|
|
1172
|
+
tagTaskSlot(task, reservedSlot);
|
|
916
1173
|
return true;
|
|
917
1174
|
}
|
|
918
1175
|
const headerReserveBytes = dynamicUtf8ReserveBytesWithExtra(task, headerText, bodyLength, headerIsString
|
|
@@ -924,6 +1181,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
924
1181
|
? PayloadBuffer.EnvelopeDynamicHeaderStringExternal
|
|
925
1182
|
: PayloadBuffer.EnvelopeDynamicHeaderExternal;
|
|
926
1183
|
const reservedSlot = reserveDynamicObject(task, headerReserveBytes + bodyLength);
|
|
1184
|
+
if (reservedSlot === -1)
|
|
1185
|
+
return false;
|
|
927
1186
|
const baseStart = task[TaskIndex.Start];
|
|
928
1187
|
const writtenHeaderBytes = writeDynamicUtf8(headerText, baseStart, headerReserveBytes);
|
|
929
1188
|
if (writtenHeaderBytes < 0) {
|
|
@@ -938,6 +1197,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
938
1197
|
setSlotLength(reservedSlot, writtenHeaderBytes + bodyLength);
|
|
939
1198
|
attachPayloadTransportFinalizer(task, externalBody);
|
|
940
1199
|
task.value = null;
|
|
1200
|
+
tagTaskSlot(task, reservedSlot);
|
|
941
1201
|
return true;
|
|
942
1202
|
};
|
|
943
1203
|
const encodeObjectEnvelope = (task, slotIndex, envelope) => {
|
|
@@ -1005,6 +1265,10 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1005
1265
|
return false;
|
|
1006
1266
|
}
|
|
1007
1267
|
const reservedSlot = reserveDynamic(task, binaryBytes);
|
|
1268
|
+
if (reservedSlot === -1) {
|
|
1269
|
+
clearBigIntScratch(binaryBytes);
|
|
1270
|
+
return false;
|
|
1271
|
+
}
|
|
1008
1272
|
const written = writeDynamicBinary(binary, task[TaskIndex.Start]);
|
|
1009
1273
|
if (written < 0) {
|
|
1010
1274
|
clearBigIntScratch(binaryBytes);
|
|
@@ -1026,57 +1290,23 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1026
1290
|
objectDynamicSlot = -1;
|
|
1027
1291
|
try {
|
|
1028
1292
|
const objectValue = args;
|
|
1293
|
+
const objectProto = objectGetPrototypeOf(objectValue);
|
|
1294
|
+
// Plain objects can skip the typed-array and external-payload checks.
|
|
1295
|
+
if (objectProto === objectPrototype) {
|
|
1296
|
+
return encodeObjectJson(task, slotIndex, objectValue);
|
|
1297
|
+
}
|
|
1029
1298
|
const sharedArrayBufferPayload = getSharedArrayBufferPayload(objectValue);
|
|
1030
1299
|
if (sharedArrayBufferPayload !== undefined) {
|
|
1031
1300
|
return encodeObjectExternalPayload(task, slotIndex, sharedArrayBufferPayload, true);
|
|
1032
1301
|
}
|
|
1033
|
-
const objectProto = objectGetPrototypeOf(objectValue);
|
|
1034
1302
|
if (isRuntimeUint8Array(objectValue)) {
|
|
1035
1303
|
return encodeObjectUint8Array(task, slotIndex, objectValue);
|
|
1036
1304
|
}
|
|
1037
1305
|
if (arrayIsArray(objectValue) && isNumericArray(objectValue)) {
|
|
1038
1306
|
return encodeObjectNumericArray(task, slotIndex, objectValue);
|
|
1039
1307
|
}
|
|
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;
|
|
1308
|
+
if (arrayIsArray(objectValue) || objectProto === null) {
|
|
1309
|
+
return encodeObjectJson(task, slotIndex, objectValue);
|
|
1080
1310
|
}
|
|
1081
1311
|
if (isBufferReferenceValue(objectValue) ||
|
|
1082
1312
|
isProcessSharedBufferValue(objectValue)) {
|
|
@@ -1165,6 +1395,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1165
1395
|
if (reserveBytes < 0)
|
|
1166
1396
|
return false;
|
|
1167
1397
|
const reservedSlot = reserveDynamic(task, reserveBytes);
|
|
1398
|
+
if (reservedSlot === -1)
|
|
1399
|
+
return false;
|
|
1168
1400
|
const written = writeDynamicUtf8(text, task[TaskIndex.Start], reserveBytes);
|
|
1169
1401
|
if (written < 0) {
|
|
1170
1402
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -1197,6 +1429,8 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1197
1429
|
if (reserveBytes < 0)
|
|
1198
1430
|
return false;
|
|
1199
1431
|
const reservedSlot = reserveDynamic(task, reserveBytes);
|
|
1432
|
+
if (reservedSlot === -1)
|
|
1433
|
+
return false;
|
|
1200
1434
|
const written = writeDynamicUtf8(key, task[TaskIndex.Start], reserveBytes);
|
|
1201
1435
|
if (written < 0) {
|
|
1202
1436
|
return failDynamicWriteAfterReserve(task, reservedSlot);
|
|
@@ -1218,8 +1452,11 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1218
1452
|
options: payload?.config ?? payloadConfig,
|
|
1219
1453
|
});
|
|
1220
1454
|
const { free } = register({ lockSector });
|
|
1221
|
-
|
|
1222
|
-
|
|
1455
|
+
// Region identity is 6 bits only for dynamic payloads. Its high bit rides in
|
|
1456
|
+
// End's otherwise-unused top bit, preserving the 5-bit queue-slot field.
|
|
1457
|
+
const freeTaskSlot = (task) => free((task[TaskIndex.slotBuffer] & TASK_SLOT_INDEX_MASK) |
|
|
1458
|
+
((task[TaskIndex.End] >>> 31) << 5));
|
|
1459
|
+
const { readUtf8: readDynamicUtf8, readBytesCopy: readDynamicBytesCopy, readBytesBufferCopy: readDynamicBufferCopy, readBufferCopy: readDynamicBuffer, readBytesArrayBufferCopy: readDynamicArrayBufferCopy, readArrayBufferCopy: readDynamicArrayBuffer, read8BytesFloatCopy: readDynamic8BytesFloatCopy, read8BytesFloatView: readDynamic8BytesFloatView, readBytesView: readDynamicBytesView, syncGrowth: syncDynamicGrowth, } = createSharedDynamicBufferIO({
|
|
1223
1460
|
sab: payloadSab,
|
|
1224
1461
|
payloadConfig: resolvedPayloadConfig,
|
|
1225
1462
|
textCompat: textCompat?.payload,
|
|
@@ -1230,10 +1467,13 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1230
1467
|
// synchronously when building the ProcessSharedBuffer.
|
|
1231
1468
|
const processSharedBufferWords = new Uint32Array(PROCESS_SHARED_BUFFER_NUMERIC_WORDS);
|
|
1232
1469
|
const sharedArrayBufferWords = new Uint32Array(SHARED_ARRAY_BUFFER_NUMERIC_WORDS);
|
|
1470
|
+
// `readStaticU32Words` returns the whole scratch, and the SAB codec dispatches
|
|
1471
|
+
// on word count, so hand it a view cut to the frame's own length.
|
|
1472
|
+
const sharedArrayBufferWordViews = [];
|
|
1473
|
+
for (let count = 0; count <= SHARED_ARRAY_BUFFER_NUMERIC_WORDS; count++) {
|
|
1474
|
+
sharedArrayBufferWordViews.push(sharedArrayBufferWords.subarray(0, count));
|
|
1475
|
+
}
|
|
1233
1476
|
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
1477
|
return (task, slotIndex, specialFlags) => {
|
|
1238
1478
|
const payloadType = task[TaskIndex.Type];
|
|
1239
1479
|
if (processBoundary &&
|
|
@@ -1287,7 +1527,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1287
1527
|
const header = task[TaskIndex.Type] === PayloadBuffer.EnvelopeStaticHeaderString
|
|
1288
1528
|
? rawHeader
|
|
1289
1529
|
: parseJSON(rawHeader);
|
|
1290
|
-
const payloadLength = task[TaskIndex.End];
|
|
1530
|
+
const payloadLength = task[TaskIndex.End] & 0x7FFFFFFF;
|
|
1291
1531
|
const payload = payloadLength > 0
|
|
1292
1532
|
? readDynamicArrayBufferCopy(task[TaskIndex.Start], task[TaskIndex.Start] + payloadLength)
|
|
1293
1533
|
: new ArrayBuffer(0);
|
|
@@ -1299,7 +1539,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1299
1539
|
case PayloadBuffer.EnvelopeDynamicHeaderString: {
|
|
1300
1540
|
const headerStart = task[TaskIndex.Start];
|
|
1301
1541
|
const payloadStart = headerStart + task[TaskIndex.PayloadLen];
|
|
1302
|
-
const payloadLength = task[TaskIndex.End];
|
|
1542
|
+
const payloadLength = task[TaskIndex.End] & 0x7FFFFFFF;
|
|
1303
1543
|
const rawHeader = readDynamicUtf8(headerStart, payloadStart);
|
|
1304
1544
|
const header = task[TaskIndex.Type] === PayloadBuffer.EnvelopeDynamicHeaderString
|
|
1305
1545
|
? rawHeader
|
|
@@ -1319,7 +1559,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1319
1559
|
? rawHeader
|
|
1320
1560
|
: parseJSON(rawHeader);
|
|
1321
1561
|
const bodyStart = task[TaskIndex.Start];
|
|
1322
|
-
const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]), processBoundary);
|
|
1562
|
+
const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + (task[TaskIndex.End] & 0x7FFFFFFF)), processBoundary);
|
|
1323
1563
|
task.value = new Envelope(header, body);
|
|
1324
1564
|
freeTaskSlot(task);
|
|
1325
1565
|
return;
|
|
@@ -1333,7 +1573,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1333
1573
|
PayloadBuffer.EnvelopeDynamicHeaderStringExternal
|
|
1334
1574
|
? rawHeader
|
|
1335
1575
|
: parseJSON(rawHeader);
|
|
1336
|
-
const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]), processBoundary);
|
|
1576
|
+
const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + (task[TaskIndex.End] & 0x7FFFFFFF)), processBoundary);
|
|
1337
1577
|
task.value = new Envelope(header, body);
|
|
1338
1578
|
freeTaskSlot(task);
|
|
1339
1579
|
return;
|
|
@@ -1413,21 +1653,39 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1413
1653
|
return;
|
|
1414
1654
|
}
|
|
1415
1655
|
case PayloadBuffer.ExternalPayload:
|
|
1416
|
-
task.value = decodeExternalPayload(readDynamicUtf8(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]), processBoundary);
|
|
1656
|
+
task.value = decodeExternalPayload(readDynamicUtf8(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]), processBoundary, lockSector);
|
|
1417
1657
|
freeTaskSlot(task);
|
|
1418
1658
|
return;
|
|
1419
1659
|
case PayloadBuffer.StaticExternalPayload:
|
|
1420
|
-
task.value = decodeExternalPayload(readStaticUtf8(0, task[TaskIndex.PayloadLen], slotIndex), processBoundary);
|
|
1660
|
+
task.value = decodeExternalPayload(readStaticUtf8(0, task[TaskIndex.PayloadLen], slotIndex), processBoundary, lockSector);
|
|
1421
1661
|
return;
|
|
1422
1662
|
case PayloadBuffer.ProcessSharedBuffer:
|
|
1423
1663
|
task.value = decodeProcessSharedBufferNumericWords(readStaticU32Words(processSharedBufferWords, PROCESS_SHARED_BUFFER_NUMERIC_WORDS, slotIndex));
|
|
1424
1664
|
return;
|
|
1425
|
-
case PayloadBuffer.SharedArrayBuffer:
|
|
1426
|
-
|
|
1665
|
+
case PayloadBuffer.SharedArrayBuffer: {
|
|
1666
|
+
const wordCount = task[TaskIndex.PayloadLen] >>> 2;
|
|
1667
|
+
task.value = decodeSharedArrayBufferNumericWords(readStaticU32Words(sharedArrayBufferWordViews[wordCount] ?? sharedArrayBufferWords, wordCount, slotIndex), lockSector);
|
|
1427
1668
|
return;
|
|
1669
|
+
}
|
|
1428
1670
|
case PayloadBuffer.BufferReference:
|
|
1429
1671
|
task.value = decodeBufferReferenceNumericWords(readStaticU32Words(bufferReferenceWords, BUFFER_REFERENCE_NUMERIC_WORDS, slotIndex));
|
|
1430
1672
|
return;
|
|
1673
|
+
case PayloadBuffer.MovedBinary: {
|
|
1674
|
+
const reference = decodeBufferReferenceNumericWords(readStaticU32Words(bufferReferenceWords, BUFFER_REFERENCE_NUMERIC_WORDS, slotIndex));
|
|
1675
|
+
if (!isBufferReferenceValue(reference)) {
|
|
1676
|
+
throw new TypeError("Invalid moved Uint8Array payload");
|
|
1677
|
+
}
|
|
1678
|
+
task.value = reference.toUint8Array();
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
case PayloadBuffer.MovedArrayBuffer: {
|
|
1682
|
+
const reference = decodeBufferReferenceNumericWords(readStaticU32Words(bufferReferenceWords, BUFFER_REFERENCE_NUMERIC_WORDS, slotIndex));
|
|
1683
|
+
if (!isBufferReferenceValue(reference)) {
|
|
1684
|
+
throw new TypeError("Invalid moved ArrayBuffer payload");
|
|
1685
|
+
}
|
|
1686
|
+
task.value = reference.toArrayBuffer();
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1431
1689
|
case PayloadBuffer.Date:
|
|
1432
1690
|
Uint32View[0] = task[TaskIndex.Start];
|
|
1433
1691
|
Uint32View[1] = task[TaskIndex.End];
|
|
@@ -1437,6 +1695,12 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
|
|
|
1437
1695
|
task.value = parseErrorPayload(readDynamicUtf8(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]));
|
|
1438
1696
|
freeTaskSlot(task);
|
|
1439
1697
|
return;
|
|
1698
|
+
// Borrowed region: decode a view into the shared arena.
|
|
1699
|
+
case PayloadBuffer.ArenaBinary:
|
|
1700
|
+
// A borrowed region can sit past the length this endpoint last saw.
|
|
1701
|
+
syncDynamicGrowth();
|
|
1702
|
+
task.value = readDynamicBytesView(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]);
|
|
1703
|
+
return;
|
|
1440
1704
|
case PayloadBuffer.Binary:
|
|
1441
1705
|
{
|
|
1442
1706
|
const buffer = readDynamicBufferCopy(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]);
|