knitting 0.1.51 → 0.1.52
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 +213 -54
- package/knitting.d.ts +1 -0
- package/map.md +15 -3
- package/package.json +14 -2
- package/prebuilds/darwin-arm64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-x64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-x64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/linux-x64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/linux-x64-node-137/knitting_buffer_pointer.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_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_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/src/api.d.ts +3 -10
- package/src/api.js +25 -21
- package/src/common/envelope.d.ts +9 -3
- package/src/common/envelope.js +14 -0
- package/src/common/worker-runtime.d.ts +2 -0
- package/src/common/worker-runtime.js +9 -0
- package/src/connections/buffer-reference-native.d.ts +56 -0
- package/src/connections/buffer-reference-native.js +217 -0
- package/src/connections/buffer-reference.d.ts +76 -0
- package/src/connections/buffer-reference.js +459 -0
- package/src/connections/index.d.ts +1 -0
- package/src/connections/index.js +1 -0
- package/src/connections/node-addons.d.ts +1 -1
- package/src/connections/node-buffer-pointer.d.ts +20 -0
- package/src/connections/node-buffer-pointer.js +16 -0
- package/src/connections/shared-array-buffer-payload.d.ts +36 -0
- package/src/connections/shared-array-buffer-payload.js +235 -0
- package/src/knitting_buffer_pointer.cc +425 -0
- package/src/memory/lock.d.ts +12 -1
- package/src/memory/lock.js +47 -4
- package/src/memory/payloadCodec.js +220 -37
- package/src/runtime/pool.d.ts +2 -1
- package/src/runtime/pool.js +8 -1
- package/src/runtime/process-worker.js +3 -1
- package/src/runtime/tx-queue.d.ts +3 -2
- package/src/runtime/tx-queue.js +18 -13
- package/src/types.d.ts +26 -18
- package/src/utils/http.d.ts +21 -0
- package/src/utils/http.js +93 -0
- package/src/worker/loop.js +23 -3
- package/src/worker/rx-queue.d.ts +4 -1
- package/src/worker/rx-queue.js +53 -4
- package/unsafe.d.ts +1 -0
- package/unsafe.js +1 -0
- package/utils.d.ts +1 -0
- package/utils.js +1 -0
package/src/common/envelope.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
import type { BufferReference } from "../connections/buffer-reference.js";
|
|
2
|
+
import type { ProcessSharedBuffer } from "../connections/process-shared-buffer.js";
|
|
1
3
|
type EnvelopeHeaderPrimitive = string | number | boolean | null;
|
|
2
4
|
type EnvelopeHeaderValue = EnvelopeHeaderPrimitive | EnvelopeHeaderValue[] | {
|
|
3
5
|
[key: string]: EnvelopeHeaderValue;
|
|
4
6
|
};
|
|
5
7
|
export type EnvelopeHeader = EnvelopeHeaderValue;
|
|
6
|
-
export
|
|
8
|
+
export type EnvelopeBody = ArrayBuffer | SharedArrayBuffer | BufferReference | ProcessSharedBuffer;
|
|
9
|
+
declare const PayloadTransportFinalizer: unique symbol;
|
|
10
|
+
export declare class Envelope<H extends EnvelopeHeader = EnvelopeHeader, B extends EnvelopeBody = ArrayBuffer> {
|
|
7
11
|
readonly header: H;
|
|
8
|
-
readonly payload:
|
|
9
|
-
constructor(header: H, payload:
|
|
12
|
+
readonly payload: B;
|
|
13
|
+
constructor(header: H, payload: B);
|
|
14
|
+
[Symbol.dispose](): void;
|
|
15
|
+
[PayloadTransportFinalizer](): (() => void) | undefined;
|
|
10
16
|
}
|
|
11
17
|
export {};
|
package/src/common/envelope.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const PayloadTransportFinalizer = Symbol.for("knitting.payloadCodec.transportFinalizer");
|
|
1
2
|
export class Envelope {
|
|
2
3
|
header;
|
|
3
4
|
payload;
|
|
@@ -5,4 +6,17 @@ export class Envelope {
|
|
|
5
6
|
this.header = header;
|
|
6
7
|
this.payload = payload;
|
|
7
8
|
}
|
|
9
|
+
[Symbol.dispose]() {
|
|
10
|
+
const body = this.payload;
|
|
11
|
+
if (body !== null && typeof body === "object") {
|
|
12
|
+
body[Symbol.dispose]?.();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
[PayloadTransportFinalizer]() {
|
|
16
|
+
const body = this.payload;
|
|
17
|
+
if (body === null || typeof body !== "object")
|
|
18
|
+
return undefined;
|
|
19
|
+
const finalizer = body[PayloadTransportFinalizer];
|
|
20
|
+
return typeof finalizer === "function" ? finalizer.call(body) : undefined;
|
|
21
|
+
}
|
|
8
22
|
}
|
|
@@ -28,7 +28,9 @@ export type RuntimeMessageChannelLike = {
|
|
|
28
28
|
export declare const RUNTIME_PROCESS_WORKER_ENV = "KNITTING_PROCESS_WORKER";
|
|
29
29
|
export declare const RUNTIME_PROCESS_WORKER_BOOT_ENV = "KNITTING_PROCESS_WORKER_BOOT";
|
|
30
30
|
export declare const RUNTIME_PROCESS_WORKER_BOOT_VERSION = 1;
|
|
31
|
+
export declare const RUNTIME_POOL_DEPTH_ENV = "KNITTING_POOL_DEPTH";
|
|
31
32
|
export declare const RUNTIME_IS_PROCESS_WORKER: boolean;
|
|
33
|
+
export declare const RUNTIME_POOL_DEPTH: number;
|
|
32
34
|
export declare const RUNTIME_WORKER: new (specifier: string | URL, options?: Record<string, unknown>) => RuntimeWorkerLike;
|
|
33
35
|
export declare const RUNTIME_MESSAGE_CHANNEL: new () => RuntimeMessageChannelLike;
|
|
34
36
|
export declare const HAS_NODE_WORKER_THREADS: boolean;
|
|
@@ -2,8 +2,17 @@ import { getNodeBuiltinModule, getNodeProcess } from "./node-compat.js";
|
|
|
2
2
|
export const RUNTIME_PROCESS_WORKER_ENV = "KNITTING_PROCESS_WORKER";
|
|
3
3
|
export const RUNTIME_PROCESS_WORKER_BOOT_ENV = "KNITTING_PROCESS_WORKER_BOOT";
|
|
4
4
|
export const RUNTIME_PROCESS_WORKER_BOOT_VERSION = 1;
|
|
5
|
+
export const RUNTIME_POOL_DEPTH_ENV = "KNITTING_POOL_DEPTH";
|
|
5
6
|
const nodeProcess = getNodeProcess();
|
|
6
7
|
export const RUNTIME_IS_PROCESS_WORKER = nodeProcess?.env?.[RUNTIME_PROCESS_WORKER_ENV] === "1";
|
|
8
|
+
const readPoolDepth = () => {
|
|
9
|
+
const raw = nodeProcess?.env?.[RUNTIME_POOL_DEPTH_ENV];
|
|
10
|
+
if (typeof raw !== "string")
|
|
11
|
+
return 0;
|
|
12
|
+
const parsed = Number.parseInt(raw, 10);
|
|
13
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
14
|
+
};
|
|
15
|
+
export const RUNTIME_POOL_DEPTH = readPoolDepth();
|
|
7
16
|
const workerThreads = getNodeBuiltinModule("node:worker_threads");
|
|
8
17
|
const isWorkerGlobalScope = () => {
|
|
9
18
|
const scopeCtor = globalThis.WorkerGlobalScope;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type BufferReferenceRuntime = "node" | "deno" | "bun";
|
|
2
|
+
/** A backing store the producer has taken exclusive ownership of (source detached). */
|
|
3
|
+
export type ProducedBuffer = {
|
|
4
|
+
/** Producer-side release handle. */
|
|
5
|
+
token: bigint;
|
|
6
|
+
/** Address of the region start (view offset already applied). */
|
|
7
|
+
pointer: bigint;
|
|
8
|
+
/** Offset of the region inside the backing store (used by the Node owning adopt). */
|
|
9
|
+
byteOffset: number;
|
|
10
|
+
byteLength: number;
|
|
11
|
+
};
|
|
12
|
+
export type AdoptInput = {
|
|
13
|
+
token: bigint;
|
|
14
|
+
pointer: bigint;
|
|
15
|
+
byteOffset: number;
|
|
16
|
+
byteLength: number;
|
|
17
|
+
};
|
|
18
|
+
/** The materialized region: bytes live in `buffer` at `[byteOffset, byteOffset+byteLength)`. */
|
|
19
|
+
export type AdoptedRegion = {
|
|
20
|
+
buffer: ArrayBuffer;
|
|
21
|
+
byteOffset: number;
|
|
22
|
+
byteLength: number;
|
|
23
|
+
};
|
|
24
|
+
/** Materialized SAB region; `isShared` means `buffer` is a real SharedArrayBuffer. */
|
|
25
|
+
export type SharedAdoptedRegion = {
|
|
26
|
+
buffer: SharedArrayBuffer | ArrayBuffer;
|
|
27
|
+
byteOffset: number;
|
|
28
|
+
byteLength: number;
|
|
29
|
+
isShared: boolean;
|
|
30
|
+
};
|
|
31
|
+
export type AdoptOptions = {
|
|
32
|
+
/** Copy when bytes must survive producer release on non-owning runtimes. */
|
|
33
|
+
copy?: boolean;
|
|
34
|
+
};
|
|
35
|
+
type MovableBufferSource = ArrayBuffer | ArrayBufferView;
|
|
36
|
+
export type BufferReferenceCapabilities = {
|
|
37
|
+
readonly runtime: BufferReferenceRuntime;
|
|
38
|
+
/** True when `adopt` returns a buffer that co-owns the store and survives `release`. */
|
|
39
|
+
readonly supportsOwningAdopt: boolean;
|
|
40
|
+
/** Take exclusive ownership of `source`'s bytes, detaching the source. */
|
|
41
|
+
produce(source: MovableBufferSource): ProducedBuffer;
|
|
42
|
+
/** Materialize the region in the current isolate (owning on Node, alias/copy elsewhere). */
|
|
43
|
+
adopt(input: AdoptInput, opts?: AdoptOptions): AdoptedRegion;
|
|
44
|
+
/** Drop the producer's hold on the backing store. */
|
|
45
|
+
release(token: bigint): void;
|
|
46
|
+
/** True when `adoptShared` returns a real SharedArrayBuffer (Node). */
|
|
47
|
+
readonly supportsSharedAdopt: boolean;
|
|
48
|
+
/** Share a SharedArrayBuffer by reference (no detach — SABs persist). */
|
|
49
|
+
produceShared(sab: SharedArrayBuffer): ProducedBuffer;
|
|
50
|
+
/** Materialize a shared region as a non-owning alias over the same bytes. */
|
|
51
|
+
adoptShared(input: AdoptInput): SharedAdoptedRegion;
|
|
52
|
+
/** Drop the producer's pin on a shared buffer (JS-side; teardown-safe). */
|
|
53
|
+
releaseShared(token: bigint): void;
|
|
54
|
+
};
|
|
55
|
+
export declare const getBufferReferenceCapabilities: () => BufferReferenceCapabilities;
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { RUNTIME } from "../common/runtime.js";
|
|
2
|
+
import { loadNodeBufferPointerAddon } from "./node-buffer-pointer.js";
|
|
3
|
+
const isSharedArrayBufferInstance = (value) => typeof SharedArrayBuffer === "function" && value instanceof SharedArrayBuffer;
|
|
4
|
+
const assertMovableSource = (source) => {
|
|
5
|
+
const buffer = ArrayBuffer.isView(source) ? source.buffer : source;
|
|
6
|
+
if (isSharedArrayBufferInstance(buffer)) {
|
|
7
|
+
throw new TypeError("BufferReference expects ArrayBuffer-backed views; SharedArrayBuffer is already " +
|
|
8
|
+
"shared and cannot be moved");
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const readSourceRegion = (source) => {
|
|
12
|
+
if (ArrayBuffer.isView(source)) {
|
|
13
|
+
return {
|
|
14
|
+
buffer: source.buffer,
|
|
15
|
+
byteOffset: source.byteOffset,
|
|
16
|
+
byteLength: source.byteLength,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return { buffer: source, byteOffset: 0, byteLength: source.byteLength };
|
|
20
|
+
};
|
|
21
|
+
/** Detach `buffer`, returning a fixed-length buffer over the same bytes (the move). */
|
|
22
|
+
const detachIntoFixedLength = (buffer) => {
|
|
23
|
+
const transferable = buffer;
|
|
24
|
+
if (typeof transferable.transferToFixedLength === "function") {
|
|
25
|
+
return transferable.transferToFixedLength();
|
|
26
|
+
}
|
|
27
|
+
if (typeof transferable.transfer === "function") {
|
|
28
|
+
return transferable.transfer();
|
|
29
|
+
}
|
|
30
|
+
throw new TypeError("BufferReference requires ArrayBuffer.prototype.transfer to move a buffer in this runtime");
|
|
31
|
+
};
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Deno/Bun pin moved buffers in JS and expose non-owning FFI aliases.
|
|
34
|
+
// Tokens stay producer-isolate-local; consumers materialize from the pointer.
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
const ffiPinned = new Map();
|
|
37
|
+
let nextFfiToken = 1n;
|
|
38
|
+
const pinTransferred = (buffer) => {
|
|
39
|
+
const token = nextFfiToken++;
|
|
40
|
+
ffiPinned.set(token, buffer);
|
|
41
|
+
return token;
|
|
42
|
+
};
|
|
43
|
+
const pinShared = (sab) => {
|
|
44
|
+
const token = nextFfiToken++;
|
|
45
|
+
ffiPinned.set(token, sab);
|
|
46
|
+
return token;
|
|
47
|
+
};
|
|
48
|
+
const releaseFfi = (token) => {
|
|
49
|
+
ffiPinned.delete(token);
|
|
50
|
+
};
|
|
51
|
+
const produceSharedFfi = (sab, readPointer) => {
|
|
52
|
+
// SABs are shared, not detached; pin keeps producer bytes alive.
|
|
53
|
+
const pointer = readPointer(new Uint8Array(sab));
|
|
54
|
+
const token = pinShared(sab);
|
|
55
|
+
return { token, pointer, byteOffset: 0, byteLength: sab.byteLength };
|
|
56
|
+
};
|
|
57
|
+
const getDeno = () => {
|
|
58
|
+
const deno = globalThis.Deno;
|
|
59
|
+
if (deno?.UnsafePointer === undefined || deno.UnsafePointerView === undefined) {
|
|
60
|
+
throw new Error("Deno FFI (UnsafePointer) is not available in this runtime");
|
|
61
|
+
}
|
|
62
|
+
return deno;
|
|
63
|
+
};
|
|
64
|
+
const getBunFFI = () => {
|
|
65
|
+
const ffi = globalThis.Bun?.FFI;
|
|
66
|
+
if (typeof ffi?.ptr !== "function" || typeof ffi?.toArrayBuffer !== "function") {
|
|
67
|
+
throw new Error("Bun FFI (ptr/toArrayBuffer) is not available in this runtime");
|
|
68
|
+
}
|
|
69
|
+
return ffi;
|
|
70
|
+
};
|
|
71
|
+
const produceFfi = (source, readPointer) => {
|
|
72
|
+
assertMovableSource(source);
|
|
73
|
+
const { buffer, byteOffset, byteLength } = readSourceRegion(source);
|
|
74
|
+
const moved = detachIntoFixedLength(buffer);
|
|
75
|
+
// Region view over the moved buffer; the pointer captures the offset.
|
|
76
|
+
const region = new Uint8Array(moved, byteOffset, byteLength);
|
|
77
|
+
const pointer = readPointer(region);
|
|
78
|
+
const token = pinTransferred(moved);
|
|
79
|
+
return { token, pointer, byteOffset, byteLength };
|
|
80
|
+
};
|
|
81
|
+
const finishAlias = (alias, copy) => {
|
|
82
|
+
if (copy) {
|
|
83
|
+
const owned = alias.slice(0);
|
|
84
|
+
return { buffer: owned, byteOffset: 0, byteLength: owned.byteLength };
|
|
85
|
+
}
|
|
86
|
+
return { buffer: alias, byteOffset: 0, byteLength: alias.byteLength };
|
|
87
|
+
};
|
|
88
|
+
const createDenoCapabilities = () => {
|
|
89
|
+
const deno = getDeno();
|
|
90
|
+
return {
|
|
91
|
+
runtime: "deno",
|
|
92
|
+
supportsOwningAdopt: false,
|
|
93
|
+
produce: (view) => produceFfi(view, (region) => deno.UnsafePointer.value(deno.UnsafePointer.of(region))),
|
|
94
|
+
adopt: ({ pointer, byteLength }, opts) => {
|
|
95
|
+
const alias = denoAlias(deno, pointer, byteLength);
|
|
96
|
+
return finishAlias(alias, opts?.copy);
|
|
97
|
+
},
|
|
98
|
+
release: releaseFfi,
|
|
99
|
+
supportsSharedAdopt: false,
|
|
100
|
+
produceShared: (sab) => produceSharedFfi(sab, (region) => deno.UnsafePointer.value(deno.UnsafePointer.of(region))),
|
|
101
|
+
adoptShared: ({ pointer, byteLength }) => {
|
|
102
|
+
const alias = denoAlias(deno, pointer, byteLength);
|
|
103
|
+
return { buffer: alias, byteOffset: 0, byteLength, isShared: false };
|
|
104
|
+
},
|
|
105
|
+
releaseShared: releaseFfi,
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
const denoAlias = (deno, pointer, byteLength) => {
|
|
109
|
+
const handle = deno.UnsafePointer.create(pointer);
|
|
110
|
+
if (handle === null || handle === undefined) {
|
|
111
|
+
throw new Error("Deno.UnsafePointer.create returned null");
|
|
112
|
+
}
|
|
113
|
+
return new deno.UnsafePointerView(handle).getArrayBuffer(byteLength);
|
|
114
|
+
};
|
|
115
|
+
const createBunCapabilities = () => {
|
|
116
|
+
const ffi = getBunFFI();
|
|
117
|
+
return {
|
|
118
|
+
runtime: "bun",
|
|
119
|
+
supportsOwningAdopt: false,
|
|
120
|
+
produce: (view) => produceFfi(view, (region) => BigInt(ffi.ptr(region))),
|
|
121
|
+
adopt: ({ pointer, byteLength }, opts) => {
|
|
122
|
+
const alias = ffi.toArrayBuffer(Number(pointer), 0, byteLength);
|
|
123
|
+
return finishAlias(alias, opts?.copy);
|
|
124
|
+
},
|
|
125
|
+
release: releaseFfi,
|
|
126
|
+
supportsSharedAdopt: false,
|
|
127
|
+
produceShared: (sab) => produceSharedFfi(sab, (region) => BigInt(ffi.ptr(region))),
|
|
128
|
+
adoptShared: ({ pointer, byteLength }) => {
|
|
129
|
+
const alias = ffi.toArrayBuffer(Number(pointer), 0, byteLength);
|
|
130
|
+
return { buffer: alias, byteOffset: 0, byteLength, isShared: false };
|
|
131
|
+
},
|
|
132
|
+
releaseShared: releaseFfi,
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// Node: addon-backed owning move via shared_ptr<BackingStore>.
|
|
137
|
+
// Older prebuilds fall back to non-owning retain/wrap.
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
const createNodeCapabilities = () => {
|
|
140
|
+
const addon = loadNodeBufferPointerAddon();
|
|
141
|
+
const owning = typeof addon.retainBackingStore === "function" &&
|
|
142
|
+
typeof addon.adoptBackingStore === "function" &&
|
|
143
|
+
typeof addon.releaseBackingStore === "function";
|
|
144
|
+
// SABs use JS pins plus non-owning aliases so no native handle survives teardown.
|
|
145
|
+
const sharedCapabilities = {
|
|
146
|
+
supportsSharedAdopt: false,
|
|
147
|
+
produceShared: (sab) => produceSharedFfi(sab, (region) => addon.getPointer(region)),
|
|
148
|
+
adoptShared: ({ pointer, byteLength }) => {
|
|
149
|
+
const alias = addon.wrapPointer(pointer, byteLength);
|
|
150
|
+
return { buffer: alias, byteOffset: 0, byteLength, isShared: false };
|
|
151
|
+
},
|
|
152
|
+
releaseShared: releaseFfi,
|
|
153
|
+
};
|
|
154
|
+
if (owning) {
|
|
155
|
+
return {
|
|
156
|
+
runtime: "node",
|
|
157
|
+
supportsOwningAdopt: true,
|
|
158
|
+
produce: (source) => {
|
|
159
|
+
assertMovableSource(source);
|
|
160
|
+
const r = addon.retainBackingStore(source);
|
|
161
|
+
return {
|
|
162
|
+
token: r.token,
|
|
163
|
+
pointer: r.pointer,
|
|
164
|
+
byteOffset: r.byteOffset,
|
|
165
|
+
byteLength: r.byteLength,
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
adopt: ({ token, byteOffset, byteLength }, opts) => {
|
|
169
|
+
const buffer = addon.adoptBackingStore(token);
|
|
170
|
+
if (opts?.copy) {
|
|
171
|
+
const owned = buffer.slice(byteOffset, byteOffset + byteLength);
|
|
172
|
+
return { buffer: owned, byteOffset: 0, byteLength: owned.byteLength };
|
|
173
|
+
}
|
|
174
|
+
return { buffer, byteOffset, byteLength };
|
|
175
|
+
},
|
|
176
|
+
release: (token) => void addon.releaseBackingStore(token),
|
|
177
|
+
...sharedCapabilities,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// Legacy fallback: producer pins via retainPointer, consumer wraps non-owning.
|
|
181
|
+
return {
|
|
182
|
+
runtime: "node",
|
|
183
|
+
supportsOwningAdopt: false,
|
|
184
|
+
produce: (source) => {
|
|
185
|
+
assertMovableSource(source);
|
|
186
|
+
const retained = addon.retainPointer(source);
|
|
187
|
+
const byteOffset = ArrayBuffer.isView(source) ? source.byteOffset : 0;
|
|
188
|
+
return {
|
|
189
|
+
token: retained.token,
|
|
190
|
+
pointer: retained.pointer,
|
|
191
|
+
byteOffset,
|
|
192
|
+
byteLength: retained.byteLength,
|
|
193
|
+
};
|
|
194
|
+
},
|
|
195
|
+
adopt: ({ pointer, byteLength }, opts) => {
|
|
196
|
+
const alias = addon.wrapPointer(pointer, byteLength);
|
|
197
|
+
return finishAlias(alias, opts?.copy);
|
|
198
|
+
},
|
|
199
|
+
release: (token) => void addon.releasePointer(token),
|
|
200
|
+
...sharedCapabilities,
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
let cached;
|
|
204
|
+
export const getBufferReferenceCapabilities = () => {
|
|
205
|
+
if (cached !== undefined)
|
|
206
|
+
return cached;
|
|
207
|
+
switch (RUNTIME) {
|
|
208
|
+
case "node":
|
|
209
|
+
return cached = createNodeCapabilities();
|
|
210
|
+
case "deno":
|
|
211
|
+
return cached = createDenoCapabilities();
|
|
212
|
+
case "bun":
|
|
213
|
+
return cached = createBunCapabilities();
|
|
214
|
+
default:
|
|
215
|
+
throw new Error(`BufferReference cannot run in runtime "${RUNTIME}"`);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type BufferReferenceRuntime } from "./buffer-reference-native.js";
|
|
2
|
+
export type { BufferReferenceRuntime } from "./buffer-reference-native.js";
|
|
3
|
+
export declare const BUFFER_REFERENCE_KIND: "knitting.bufferReference";
|
|
4
|
+
/** Named values for the `unsafe.BufferReferenceReturn` pool option. */
|
|
5
|
+
export declare const BufferReferenceReturn: {
|
|
6
|
+
/** Safe default: Deno/Bun copy the returned bytes so they outlive the worker. */
|
|
7
|
+
readonly Copy: "copy";
|
|
8
|
+
/** Zero-copy: borrow the worker's backing store until the reference is released. */
|
|
9
|
+
readonly Borrow: "borrow";
|
|
10
|
+
};
|
|
11
|
+
export type BufferReferenceReturn = (typeof BufferReferenceReturn)[keyof typeof BufferReferenceReturn];
|
|
12
|
+
export declare const BUFFER_REFERENCE_NUMERIC_TRANSFER: unique symbol;
|
|
13
|
+
export declare const BUFFER_REFERENCE_RETURN_RELEASE_TOKEN: unique symbol;
|
|
14
|
+
declare const EXTERNAL_PAYLOAD_BRAND: unique symbol;
|
|
15
|
+
export declare const BUFFER_REFERENCE_CODEC_ID: "knitting.bufferReference";
|
|
16
|
+
declare const BUFFER_REFERENCE_RETURN_RELEASE_MESSAGE_KEY = "__knittingBufferReferenceRelease";
|
|
17
|
+
export type BufferReferenceNumericMetadata = readonly [
|
|
18
|
+
pointerLow: number,
|
|
19
|
+
pointerHigh: number,
|
|
20
|
+
tokenLow: number,
|
|
21
|
+
tokenHigh: number,
|
|
22
|
+
byteOffset: number,
|
|
23
|
+
byteLength: number,
|
|
24
|
+
runtime: number,
|
|
25
|
+
originPid: number
|
|
26
|
+
];
|
|
27
|
+
export type BufferReferenceMetadata = {
|
|
28
|
+
readonly kind: typeof BUFFER_REFERENCE_KIND;
|
|
29
|
+
readonly origin: string;
|
|
30
|
+
readonly runtime: BufferReferenceRuntime;
|
|
31
|
+
readonly pointer: string;
|
|
32
|
+
/** Producer-side release handle (process-local). */
|
|
33
|
+
readonly token: string;
|
|
34
|
+
readonly byteOffset: number;
|
|
35
|
+
readonly byteLength: number;
|
|
36
|
+
};
|
|
37
|
+
export type BufferReferenceReturnReleaseMessage = {
|
|
38
|
+
readonly [BUFFER_REFERENCE_RETURN_RELEASE_MESSAGE_KEY]: string;
|
|
39
|
+
};
|
|
40
|
+
type BufferReferenceReturnReleaser = (token: bigint) => void;
|
|
41
|
+
type BufferSource = ArrayBufferView | ArrayBuffer;
|
|
42
|
+
declare const PAYLOAD_TRANSPORT_FINALIZER: unique symbol;
|
|
43
|
+
export declare const withBufferReferenceReturnReleaser: <T>(releaser: BufferReferenceReturnReleaser | undefined, run: () => T) => T;
|
|
44
|
+
export declare const createBufferReferenceReturnReleaseMessage: (token: bigint) => BufferReferenceReturnReleaseMessage;
|
|
45
|
+
export declare const readBufferReferenceReturnReleaseMessage: (value: unknown) => bigint | undefined;
|
|
46
|
+
export declare const isBufferReferenceMetadata: (value: unknown) => value is BufferReferenceMetadata;
|
|
47
|
+
/**
|
|
48
|
+
* Zero-copy handle for moving ArrayBuffer bytes to **thread** workers.
|
|
49
|
+
*
|
|
50
|
+
* Construction detaches the source. Consumers materialize the moved region in
|
|
51
|
+
* their isolate: owning on Node, alias/copy on Deno/Bun.
|
|
52
|
+
*/
|
|
53
|
+
export declare class BufferReference {
|
|
54
|
+
#private;
|
|
55
|
+
readonly [EXTERNAL_PAYLOAD_BRAND]: "knitting.bufferReference";
|
|
56
|
+
readonly runtime: BufferReferenceRuntime;
|
|
57
|
+
readonly origin: string;
|
|
58
|
+
readonly pointer: bigint;
|
|
59
|
+
readonly byteOffset: number;
|
|
60
|
+
readonly byteLength: number;
|
|
61
|
+
constructor(source: BufferSource);
|
|
62
|
+
constructor(source: BufferSource | undefined, meta: BufferReferenceMetadata);
|
|
63
|
+
static fromMetadata(meta: BufferReferenceMetadata): BufferReference;
|
|
64
|
+
toMetadata(): BufferReferenceMetadata;
|
|
65
|
+
[BUFFER_REFERENCE_NUMERIC_TRANSFER](): BufferReferenceNumericMetadata | undefined;
|
|
66
|
+
get isLocal(): boolean;
|
|
67
|
+
/** Prepare a returned reference before the worker-side producer hold drains. */
|
|
68
|
+
claimOwnership(releaser?: BufferReferenceReturnReleaser): this;
|
|
69
|
+
toArrayBuffer(): ArrayBuffer;
|
|
70
|
+
toUint8Array(): Uint8Array;
|
|
71
|
+
/** The source is moved on construction, so it is never retained here. */
|
|
72
|
+
get source(): undefined;
|
|
73
|
+
release(): void;
|
|
74
|
+
[Symbol.dispose](): void;
|
|
75
|
+
[PAYLOAD_TRANSPORT_FINALIZER](): (() => void) | undefined;
|
|
76
|
+
}
|