Reputation: 79238
The examples I've seen show essentially this:
fetch('simple.wasm').then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, {})
).then(result =>
result.instance.exports...
)
But I would like to do it without making that extra HTTP request. Wondering if the only way is this (or some variation of it, which would be helpful to know):
var binary = '...mywasmbinary...'
var buffer = new ArrayBuffer(binary.length)
var view = new DataView(buffer)
for (var i = 0, n = binary.length; i < n; i++) {
var x = binary[i]
view.setInt8(i * 8, x)
}
Wondering if I have to worry about endianess or anything like that.
Or perhaps doing something with URL
and blobs might be better, I'm not sure.
Upvotes: 8
Views: 3650
Reputation: 61
Thank you ColinE. This solved my problem trying to upload a WASM module to NPM. For that, I had to split your answer into two file: 'export' and 'import.' Ignoring browser compatibility and focusing only on Node, my solution ended up like this:
const readFileSync = require('fs').readFileSync
const writeFile = require('fs').writeFileSync;
const wasmCode = readFileSync("./public/wasm/main.wasm");
const encoded = Buffer.from(wasmCode, 'binary').toString('base64');
function exportToJson(encoded){
json = "\""+encoded+"\""
writeFile("b64wasm.json", json, err => {
if (err) {
console.error(err);
}
// file written successfully
});
}
exportToJson(encoded)
and
wasmBin = require("./b64wasm.json")
const crypto = require("crypto").webcrypto;
globalThis.crypto = crypto;
require('./public/wasm/wasm_exec.js');
function decode(encoded) {
var binaryString = Buffer.from(encoded, 'base64').toString('binary');
var bytes = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
function loadWebAssembly() {
const go = new Go();
const importObject = go.importObject;
WebAssembly.instantiate(decode(wasmBin), importObject).then((results) => {
const instance = results.instance
go.run(instance);
console.log("arrival One")
});
}
loadWebAssembly()
setTimeout(()=>{
let a = Math.floor(Math.random()*100)
let b = Math.floor(Math.random()*100)
const sum = addTwoNumbers(a,b);
console.log("arrived two: ", sum)
},100)
Upvotes: 0
Reputation: 70132
Yes, you are correct, in order to inline wasm modules and avoid the HTTP request, you'll have to perform some sort of encoding. I'd recommend using Base64 encoded strings as they are the most compact form.
You can encode as follows:
const readFileSync = require('fs').readFileSync;
const wasmCode = readFileSync(id);
const encoded = Buffer.from(wasmCode, 'binary').toString('base64');
You can then load the module as follows:
var encoded = "... contents of encoded from above ...";
function asciiToBinary(str) {
if (typeof atob === 'function') {
// this works in the browser
return atob(str)
} else {
// this works in node
return new Buffer(str, 'base64').toString('binary');
}
}
function decode(encoded) {
var binaryString = asciiToBinary(encoded);
var bytes = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
var module = WebAssembly.instantiate(decode(encoded), {});
Upvotes: 12