// Copyright 2023 Fahmi Akbar Wildana // SPDX-License-Identifier: BSD-2-Clause export function malloc(buffer: ArrayBufferLike) { let prevType: BYTES_PER_ELEMENT = 1, count = 0, prevSize = 0; // IIFE return (TypedArray: T, size: number) => (($) => (prevType = $.BYTES_PER_ELEMENT, $ as InstanceType))( new TypedArray(buffer, prevType * prevSize * count++, prevSize = size), //👈 ); } export class WorkerThread extends Worker { constructor( public url: string, public buffer: SharedArrayBuffer, public id: number = 0, ) { super(url, { type: "module" }); this.postMessage([id, this.buffer]); // share memory buffer with the/another worker thread } /** tell worker to run while send info about the memory layout */ run(layout: ArrayBufferLike, config?: { copy: true }) { this.postMessage(layout, config?.copy ? undefined : { transfer: [layout] }); } relayMessage(message: unknown, options?: StructuredSerializeOptions) { this.postMessage(["relay", message, ...options ? [options] : []]); } // concurrent? 🤔 static selfSpawn(url: string, limit = 1) { return new Promise((resolve) => { const $ = {} as MayRelay; self.addEventListener("message", ({ data }) => { if (data[1] instanceof SharedArrayBuffer) { [$.id, $.memory] = data; if ($.id < limit) { $.relay = new WorkerThread(url, $.memory, ($.id = +$.id) + 1); } } else if ($.memory && data instanceof ArrayBuffer) { $.layout = data; resolve($); if ($.relay) $.relay.run($.layout, { copy: true }); } else if (($.relay || $.id === limit) && data[0] === "relay") { $.onrelay?.(data[1]); $.relay?.relayMessage(data[1], data[2]); } }); }); } // await-ing will wait Shared memory before returning static get self(): Promise { return new Promise((resolve) => { let memory: SharedArrayBuffer, id: number; self.onmessage = ({ data }) => { if (data[1] instanceof SharedArrayBuffer) [id, memory] = data; else if (memory && data instanceof ArrayBuffer) { resolve({ id, memory, layout: data }); } }; }); } } export function loop(fn: () => void) { if ("Deno" in globalThis) { const chan = new MessageChannel(); chan.port1.postMessage(null); chan.port2.onmessage = () => { fn(); chan.port1.postMessage(null); }; chan.port2.start(); } else {requestAnimationFrame(function loop() { fn(); requestAnimationFrame(loop); });} } interface MayRelay extends SharedData { relay: WorkerThread; onrelay?: (data: Parameters[0]) => void; } interface SharedData { id: number; memory: SharedArrayBuffer; layout: ArrayBuffer; } type BYTES_PER_ELEMENT = number; type TypedArrayConstructor = | Uint8ClampedArrayConstructor | Uint8ArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor | BigUint64ArrayConstructor | Int8ArrayConstructor | Int16ArrayConstructor | Int32ArrayConstructor | BigInt64ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor;