@hashgraphonline/standards-sdk
Version:
The Hashgraph Online Standards SDK provides a complete implementation of the Hashgraph Consensus Standards (HCS), giving developers all the tools needed to build applications on Hedera.
205 lines (204 loc) • 6.46 kB
JavaScript
import { Logger } from "./standards-sdk.es22.js";
class WasmBridge {
constructor() {
this.wasm = null;
this.WASM_VECTOR_LEN = 0;
this.cachedUint8Memory = null;
this.cachedDataViewMemory = null;
this.textEncoder = new TextEncoder();
this.textDecoder = new TextDecoder("utf-8", {
ignoreBOM: true,
fatal: true
});
this.textDecoder.decode();
this.logger = Logger.getInstance({ module: "WasmBridge" });
}
setLogLevel(level) {
this.logger.setLogLevel(level);
}
get wasmInstance() {
if (!this.wasm) {
throw new Error("WASM not initialized");
}
return this.wasm;
}
getUint8Memory() {
if (!this.wasm) {
throw new Error("WASM not initialized");
}
if (this.cachedUint8Memory === null || this.cachedUint8Memory.byteLength === 0) {
this.cachedUint8Memory = new Uint8Array(this.wasm.memory.buffer);
}
return this.cachedUint8Memory;
}
getDataViewMemory() {
if (!this.wasm) {
throw new Error("WASM not initialized");
}
if (this.cachedDataViewMemory === null || this.cachedDataViewMemory.buffer !== this.wasm.memory.buffer) {
this.cachedDataViewMemory = new DataView(this.wasm.memory.buffer);
}
return this.cachedDataViewMemory;
}
encodeString(arg, view) {
if (arg.length === 0) {
return { read: 0, written: 0 };
}
const buf = this.textEncoder.encode(arg);
view.set(buf);
return { read: arg.length, written: buf.length };
}
passStringToWasm(arg, malloc, realloc) {
if (realloc === void 0) {
const buf = this.textEncoder.encode(arg);
const ptr2 = malloc(buf.length, 1);
const view = this.getUint8Memory();
view.set(buf, ptr2);
this.WASM_VECTOR_LEN = buf.length;
return ptr2;
}
let len = this.textEncoder.encode(arg).length;
let ptr = malloc(len, 1);
const mem = this.getUint8Memory();
let offset = 0;
for (; offset < len; offset++) {
const code = arg.charCodeAt(offset);
if (code > 127) break;
mem[ptr + offset] = code;
}
if (offset !== len) {
if (offset !== 0) {
arg = arg.slice(offset);
}
ptr = realloc(
ptr,
len,
len = offset + this.textEncoder.encode(arg).length * 3,
1
);
const view = this.getUint8Memory().subarray(ptr + offset, ptr + len);
const ret = this.encodeString(arg, view);
offset += ret.written;
}
this.WASM_VECTOR_LEN = offset;
return ptr;
}
getStringFromWasm(ptr, len) {
ptr = ptr >>> 0;
return this.textDecoder.decode(
this.getUint8Memory().subarray(ptr, ptr + len)
);
}
createWasmFunction(wasmFn) {
if (!this.wasm) {
throw new Error("WASM not initialized");
}
return (...args) => {
const retptr = this.wasm.__wbindgen_add_to_stack_pointer(-16);
let deferred = [0, 0];
try {
const ptrLenPairs = args.map((arg) => {
const ptr = this.passStringToWasm(
arg,
this.wasm.__wbindgen_malloc,
this.wasm.__wbindgen_realloc
);
return [ptr, this.WASM_VECTOR_LEN];
});
const wasmArgs = [retptr, ...ptrLenPairs.flat()];
wasmFn.apply(this.wasm, wasmArgs);
const r0 = this.getDataViewMemory().getInt32(retptr + 4 * 0, true);
const r1 = this.getDataViewMemory().getInt32(retptr + 4 * 1, true);
deferred = [r0, r1];
return this.getStringFromWasm(r0, r1);
} finally {
this.wasm.__wbindgen_add_to_stack_pointer(16);
this.wasm.__wbindgen_free(deferred[0], deferred[1], 1);
}
};
}
async initWasm(wasmBytes) {
const bridge = this;
const imports = {
__wbindgen_placeholder__: {
__wbindgen_throw: function(ptr, len) {
const message = bridge.getStringFromWasm(ptr, len);
bridge.logger.error(`WASM error: ${message}`);
throw new Error(message);
}
}
};
try {
this.logger.debug("Compiling WASM module");
const wasmModule = await WebAssembly.compile(wasmBytes);
this.logger.debug("Instantiating WASM module");
const wasmInstance = await WebAssembly.instantiate(wasmModule, imports);
this.wasm = wasmInstance.exports;
this.logger.info("WASM module initialized successfully");
return this.wasm;
} catch (error) {
this.logger.error("Failed to initialize WASM module", error);
throw error;
}
}
createStateData(wasmConfig, stateData = {}) {
let dynamicStateData = {};
if (wasmConfig?.c?.inputType?.stateData) {
if (stateData.latestRoundData && Object.keys(wasmConfig.c.inputType.stateData).every(
(key) => key in stateData.latestRoundData
)) {
dynamicStateData.latestRoundData = {};
Object.entries(wasmConfig.c.inputType.stateData).forEach(([key, _]) => {
dynamicStateData.latestRoundData[key] = String(
stateData.latestRoundData[key]
);
});
} else {
Object.entries(wasmConfig.c.inputType.stateData).forEach(
([key, type]) => {
const result = stateData[key];
if (result && typeof result === "object" && "values" in result && result.values.length > 0) {
dynamicStateData[key] = String(result.values[0]);
} else {
dynamicStateData[key] = this.getDefaultValueForType(
type
);
}
}
);
}
}
return dynamicStateData;
}
getDefaultValueForType(type) {
if (type.startsWith("uint") || type.startsWith("int") || type === "number") {
return "0";
} else if (type === "bool") {
return "false";
} else {
return "";
}
}
executeWasm(stateData, messages) {
if (!this.wasm) {
this.logger.error("WASM not initialized");
throw new Error("WASM not initialized");
}
try {
this.logger.debug("Executing WASM with stateData", stateData);
const fn = this.createWasmFunction(this.wasmInstance.process_state);
return fn(JSON.stringify(stateData), JSON.stringify(messages));
} catch (error) {
this.logger.error("Error executing WASM", error);
throw error;
}
}
getParams() {
const fn = this.createWasmFunction(this.wasmInstance.get_params);
return fn();
}
}
export {
WasmBridge
};
//# sourceMappingURL=standards-sdk.es3.js.map