Skip to content

Instantly share code, notes, and snippets.

@PhoenixIllusion
Created September 16, 2025 23:00
Show Gist options
  • Save PhoenixIllusion/00f4723f907140f6ba53cf639b1a5ae5 to your computer and use it in GitHub Desktop.
Save PhoenixIllusion/00f4723f907140f6ba53cf639b1a5ae5 to your computer and use it in GitHub Desktop.
Simple 'roll your own' binary inter-process communication protocol with a class + proxy + meta-data of how to encode/decode parameters limited to Float32 and Int32 arrays/values
type ParamType = [0|1, 1|2|3|4];
export const F4: ParamType = [0,4];
export const F3: ParamType = [0,3];
export const F2: ParamType = [0,2];
export const F1: ParamType = [0,1];
export const U4: ParamType = [1,4];
export const U3: ParamType = [1,3];
export const U2: ParamType = [1,2];
export const U1: ParamType = [1,1];
type FunctionMap = Record<string|symbol, Function>;
export type BufferData = [Float32Array, Int32Array, number];
type FunctionArgs = (number|number[]|Float32Array|Int32Array|Uint32Array)[];
function isNumber(obj: number|number[]|Float32Array|Int32Array|Uint32Array) {
return typeof obj == 'number';
}
function writeFunctionIndex(buffer: BufferData, idx: number) {
buffer[1][buffer[2]] = idx; buffer[2]+=1;
}
function writeToBuffer(buffer: BufferData, args: FunctionArgs, meta: MetaData): void {
meta.args.forEach((t,i) => {
const v = args[i];
const arr = isNumber(v) ? [v] : v;
const buf = buffer[t[0]];
buf.set(arr, buffer[2])
buffer[2] += t[1];
});
}
function readFunctionIndex(buffer: BufferData): number {
const idx = buffer[1][buffer[2]]
buffer[2]+=1;
return idx;
}
function readFromBuffer(buffer: BufferData, meta: MetaData): FunctionArgs {
const result: FunctionArgs = [];
meta.args.forEach((t) => {
const buf = buffer[t[0]];
if(t[1] == 1) {
result.push(buf[buffer[2]])
} else {
result.push(buf.slice(buffer[2], buffer[2]+t[1]))
}
buffer[2] += t[1];
});
return result;
}
interface MetaData {
args: ParamType[]
}
const MethodIndexRegistry = new WeakMap<any, Map<string|symbol,number>>();
const ServiceRegistry = new WeakMap<Function, MetaData>();
export function fields(meta: MetaData) {
return function <TMethod extends (...args: any[]) => any>(
originalMethod: TMethod,
context: ClassMethodDecoratorContext<ThisParameterType<TMethod>, TMethod>
) {
if (context.kind !== 'method') {
throw new Error(`@fields can only be applied to methods`);
}
context.addInitializer(function(this: ThisParameterType<TMethod>) {
if(!MethodIndexRegistry.has(this)){
MethodIndexRegistry.set(this, new Map());
}
const methodIndex = MethodIndexRegistry.get(this)!;
methodIndex.set(context.name, methodIndex.size)
ServiceRegistry.set(originalMethod, meta);
})
return originalMethod;
};
}
function getMethodIndex(target: any, method: string|symbol) {
return MethodIndexRegistry.get(target)?.get(method) ?? -1;
}
function getFields(fn: Function) {
return ServiceRegistry.get(fn);
}
function extractMethodMap<T>(instance: T): FunctionMap {
const proto = Object.getPrototypeOf(instance);
const keys = Object.getOwnPropertyNames(proto).filter(k => k !== 'constructor');
const map = {} as FunctionMap;
for (const key of keys) {
const fn = (instance as any)[key];
if (typeof fn === 'function') {
map[key] = fn
}
}
return map;
}
export function makeWrapper<T extends Object>(target: T, buffer: BufferData): T {
const targetMap = extractMethodMap(target);
const $this = new Proxy(target, {
get(_target, p, _receiver) {
const index = getMethodIndex(target, p);
const meta = getFields(targetMap[p]);
if(!meta) throw new Error('Attempted to read field meta data for functions not in registry')
if(index < 0) throw new Error(`Attempted to call method with negative index. Not in registry`)
return (... args: FunctionArgs): T => {
writeFunctionIndex(buffer, index);
writeToBuffer(buffer, args, meta);
return $this;
}
},
});
return $this;
}
export function makeDispatch<T>(target: T, buffer: BufferData, dispatch: T) {
const targetMap = extractMethodMap(target);
const dispatchMap = extractMethodMap(dispatch);
const indexMap = MethodIndexRegistry.get(target);
if(!indexMap) throw new Error(`Attempted to make dispatch for target with no method index registry.`)
const metas: MetaData[] = [];
const funcs: Function[] = [];
Array.from(indexMap.entries()).forEach(([k,v]) => {
const funcDispatch = dispatchMap[k];
if(!funcDispatch) throw new Error(`Failed to wire to dispatch for function ${k.toString()}. Not found`);
const meta = getFields(targetMap[k]);
if(!meta) throw new Error(`No meta data found in target for key ${k.toString()}`);
metas[v] = meta;
funcs[v] = funcDispatch;
});
return () => {
const index = readFunctionIndex(buffer);
if(index < 0 || !funcs[index]) throw new Error(`Attempted to call method with negative index or not in registry.`)
funcs[index].apply(dispatch, readFromBuffer(buffer, metas[index]));
}
}
import { quat, vec2 } from 'gl-matrix';
import { F1, F3, F4, fields, makeDispatch, makeWrapper, U1, U2, type BufferData } from './decorator';
import './style.css'
const out = document.getElementById('app')!;
function log(... args: any[]) {
out.innerText += JSON.stringify(args.map(x => typeof x == 'number' ? x : Array.from(x)))+'\n';
}
class TestService {
@fields({ args: [F4, F3]})
foo(_quat: [number,number,number,number], _position: [number,number,number]) { }
@fields({ args: [F4, U2, F3, F1, U2, U1]})
test1(_quat: Float32Array, _point: Uint32Array, _vec: [number, number, number], _weight: number, _point2: [number,number], _addr: number) {}
}
class WorkerService extends TestService {
constructor(){super();}
override foo(quat: [number, number, number, number], position: [number, number, number]): void {
log(quat, position);
}
override test1(quat: Float32Array, point: Uint32Array, vec: [number, number, number], weight: number, point2: [number, number], addr: number): void {
log(quat, point, vec, weight, point2, addr);
}
}
const buffer = new ArrayBuffer(400);
const writeBuffer: BufferData = [ new Float32Array(buffer), new Int32Array(buffer), 0];
const readBuffer: BufferData = [ new Float32Array(buffer), new Int32Array(buffer), 0];
const service = new TestService();
const writer = makeWrapper(service, writeBuffer);
const reader = new WorkerService();
const dispatch = makeDispatch(service, readBuffer, reader);
writer.foo([0,1,0,0],[3,4,5]);
dispatch();
writer.foo([0,0,0,1],[6,5,4]);
writer.foo([1,0,0,0],[9,5,2]);
dispatch();
writer.test1(
quat.set(quat.create(), 1,0,1,0) as Float32Array,
new Uint32Array([2,3]),
[0,1,0], 72, vec2.set(vec2.create(), 0.5, -1.75) as [number, number], 23.5
)
dispatch();
dispatch();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment