export async function loadDriverWasm(bytes) {
const wasmModule = await WebAssembly.instantiate(bytes, { env: {} });
const exports = wasmModule.instance.exports;
const memory = exports.memory;
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
function allocAndWriteString(str) {
const bytes = textEncoder.encode(str);
const ptr = exports.alloc(bytes.length);
new Uint8Array(memory.buffer, ptr, bytes.length).set(bytes);
return [ptr, bytes.length];
}
function allocAndWriteBytes(bytes) {
const ptr = exports.alloc(bytes.length);
new Uint8Array(memory.buffer, ptr, bytes.length).set(bytes);
return [ptr, bytes.length];
}
function readBuffer(ptr, bufSize) {
const view = new DataView(memory.buffer, ptr, 4);
const actualSize = view.getUint32(0, true);
if (actualSize > bufSize - 4) {
exports.dealloc(ptr, bufSize);
throw new Error(`Invalid output size: ${actualSize}`);
}
const data = new Uint8Array(memory.buffer, ptr + 4, actualSize);
const result = data.slice(); return result;
}
function runWithOutputBuffer(fn) {
const outSize = 64 * 1024; const outPtr = exports.alloc(outSize);
try {
const code = fn(outPtr, outSize);
if (code !== 0) {
const errMsg = _internalGetLastError();
throw new Error(`FFI call failed (${code}): ${errMsg}`);
}
return readBuffer(outPtr, outSize);
} finally {
exports.dealloc(outPtr, outSize);
}
}
function _internalGetLastError() {
const outSize = 1024;
const outPtr = exports.alloc(outSize);
try {
exports.get_last_error(outPtr, outSize);
const result = readBuffer(outPtr, outSize);
return textDecoder.decode(result);
} finally {
exports.dealloc(outPtr, outSize);
}
}
return {
init: () => exports.init(),
encodeInputFn: (fnName, json) => {
const [fnPtr, fnLen] = allocAndWriteString(fnName);
const [jsonPtr, jsonLen] = allocAndWriteString(json);
const result = runWithOutputBuffer((outPtr, outSize) =>
exports.encode_input_fn(fnPtr, fnLen, jsonPtr, jsonLen, outPtr, outSize)
);
exports.dealloc(fnPtr, fnLen);
exports.dealloc(jsonPtr, jsonLen);
return result; },
decodeInputFn: (fnName, rkyvBytes) => {
const [fnPtr, fnLen] = allocAndWriteString(fnName);
const [rkyvPtr, rkyvLen] = allocAndWriteBytes(rkyvBytes);
const result = runWithOutputBuffer((outPtr, outSize) =>
exports.decode_input_fn(fnPtr, fnLen, rkyvPtr, rkyvLen, outPtr, outSize)
);
exports.dealloc(fnPtr, fnLen);
exports.dealloc(rkyvPtr, rkyvLen);
return JSON.parse(textDecoder.decode(result));
},
decodeOutputFn: (fnName, rkyvBytes) => {
const [fnPtr, fnLen] = allocAndWriteString(fnName);
const [rkyvPtr, rkyvLen] = allocAndWriteBytes(rkyvBytes);
const result = runWithOutputBuffer((outPtr, outSize) =>
exports.decode_output_fn(
fnPtr,
fnLen,
rkyvPtr,
rkyvLen,
outPtr,
outSize
)
);
exports.dealloc(fnPtr, fnLen);
exports.dealloc(rkyvPtr, rkyvLen);
return JSON.parse(textDecoder.decode(result));
},
decodeEvent: (eventName, rkyvBytes) => {
const [eventPtr, eventLen] = allocAndWriteString(eventName);
const [rkyvPtr, rkyvLen] = allocAndWriteBytes(rkyvBytes);
const result = runWithOutputBuffer((outPtr, outSize) =>
exports.decode_event(
eventPtr,
eventLen,
rkyvPtr,
rkyvLen,
outPtr,
outSize
)
);
exports.dealloc(eventPtr, eventLen);
exports.dealloc(rkyvPtr, rkyvLen);
return JSON.parse(textDecoder.decode(result));
},
getSchema: () => {
const result = runWithOutputBuffer((outPtr, outSize) =>
exports.get_schema(outPtr, outSize)
);
return JSON.parse(textDecoder.decode(result));
},
getVersion: () => {
const result = runWithOutputBuffer((outPtr, outSize) =>
exports.get_version(outPtr, outSize)
);
return textDecoder.decode(result);
},
};
}