Offload functions to worker threads with shared memory primitives for Node.js.
1import { SharedStruct } from './shared-struct.ts';
2import { Bytes } from './bytes.ts';
3import { SharedString } from './string.ts';
4import { Tuple } from './tuple.ts';
5
6const SHARED = Symbol.for('moroutine.shared');
7
8const registry = new Map<string, new (buffer: SharedArrayBuffer, byteOffset: number) => unknown>();
9
10export function registerSync(tag: string, ctor: new (buffer: SharedArrayBuffer, byteOffset: number) => unknown): void {
11 registry.set(tag, ctor);
12}
13
14export function serializeArg(arg: unknown): unknown {
15 if (typeof arg === 'object' && arg !== null && SHARED in arg) {
16 const data = (arg as any)[SHARED]();
17 if (data.tag === 'SharedStruct') {
18 const serializedFields: Record<string, unknown> = {};
19 for (const key in data.fields) {
20 serializedFields[key] = serializeStructField(data.fields[key]);
21 }
22 return { __shared__: 'SharedStruct', fields: serializedFields };
23 }
24 if (data.tag === 'Tuple') {
25 const serializedElements = (data.elements as any[]).map((el: any) => serializeStructField(el));
26 return { __shared__: 'Tuple', elements: serializedElements };
27 }
28 return {
29 __shared__: data.tag,
30 buffer: data.buffer,
31 byteOffset: data.byteOffset,
32 ...(data.size !== undefined && { size: data.size }),
33 ...(data.maxBytes !== undefined && { maxBytes: data.maxBytes }),
34 };
35 }
36 return arg;
37}
38
39function serializeStructField(data: { tag: string; [key: string]: unknown }): unknown {
40 if (data.tag === 'SharedStruct') {
41 const serializedFields: Record<string, unknown> = {};
42 for (const key in data.fields as Record<string, unknown>) {
43 serializedFields[key] = serializeStructField((data.fields as any)[key]);
44 }
45 return { __shared__: 'SharedStruct', fields: serializedFields };
46 }
47 return {
48 __shared__: data.tag,
49 buffer: data.buffer,
50 byteOffset: data.byteOffset,
51 ...(data.size !== undefined && { size: data.size }),
52 ...(data.maxBytes !== undefined && { maxBytes: data.maxBytes }),
53 };
54}
55
56export function deserializeArg(arg: unknown): unknown {
57 if (typeof arg === 'object' && arg !== null && '__shared__' in arg) {
58 const data = arg as { __shared__: string; [key: string]: unknown };
59 if (data.__shared__ === 'Bytes') {
60 const typedData = data as { __shared__: string; buffer: SharedArrayBuffer; byteOffset: number; size: number };
61 return new Bytes(typedData.size, typedData.buffer, typedData.byteOffset);
62 }
63 if (data.__shared__ === 'SharedString') {
64 const typedData = data as { __shared__: string; buffer: SharedArrayBuffer; byteOffset: number; maxBytes: number };
65 return new SharedString(typedData.maxBytes, typedData.buffer, typedData.byteOffset);
66 }
67 if (data.__shared__ === 'SharedStruct') {
68 const fields = data.fields as Record<string, unknown>;
69 const reconstructed: Record<string, unknown> = {};
70 for (const key in fields) {
71 reconstructed[key] = deserializeArg(fields[key]);
72 }
73 return new SharedStruct(reconstructed as any);
74 }
75 if (data.__shared__ === 'Tuple') {
76 const elements = (data.elements as unknown[]).map((el) => deserializeArg(el));
77 return new Tuple(elements as any);
78 }
79 const typedData = data as { __shared__: string; buffer: SharedArrayBuffer; byteOffset: number };
80 const ctor = registry.get(typedData.__shared__);
81 if (ctor) return new ctor(typedData.buffer, typedData.byteOffset);
82 }
83 return arg;
84}