How to Directly Instantiate WebAssembly Module in JavaScript - javascript

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.

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), {});

Related

How to chunk string using javascript

I have a string which is more than 32kb it needs to be chunked, with every chunk having a size limit of 32kb.is it possible ? using JavaScript , I can only find codes like cutting the string or splitting the string in which, I think is not related to my task
stringChop = function(str, size){
if (str == null)
return [];
str = String(str);
return size > 0 ? str.match(new RegExp('.{1,' + size + '}', 'g')) : [str];
}
I also have code the check the bytes
const byteSize = str => new Blob([str]).size;
const result = byteSize("sample")
You really don't want to "spend time" splitting large strings in Node.
If you have to use vanilla
This is entirely possible with JavaScript (and you're pretty close). Though this is more elegant without regular expressions and with generators:
function* chunk(str, size = 3) {
for(let i = 0; i < str.length; i+= size ) yield str.slice(i, i + size);
}
[...chunk('hello world')]; // ["hel", "lo ", "wor", "ld"];
If you can use Node.js
I'd read the file you want to split with a createReadStream and then write it to different files when it reaches the limit. This is much more effective since you don't create many small strings or keep all the data in memory:
(async () => {
let currentFileIndex = 0, currentBytes = 0;
let currentFile = fs.createWriteStream(`${currentFileIndex}.csv`);
for await(const chunk of fs.createReadStream('input.csv') {
currentBytes += chunk.length;
if (currentBytes > 32000) { // or whatever limit you want
currentFile.end(); // probably wait for the allback here
currentBytes = 0;
currentFile = fs.createWriteStream(`${++currentFileIndex}.csv`)
}
await util.promisify(cb => currentFile.write(chunk, cb)();
}
})();

Convert Base64 String to Javascript Uint16Array?

I'm working in an Javascript application that receives a base64 array. This array encodes a 16 bits per pixel raw image.
Thus, I would like to do some calculations in it. For this, I need to unpack this Base64 string in a Uint16Array, so I can iterate over pixels and perform the required calculations.
What are my options for doing that?
After some hours looking for a solution, I found a way for doing this:
function getData()
{
fetch("test_data/img_base64.txt")
.then(res => res.text())
.then((out) => {
rawString = window.atob(out);
uint8Array = new Uint8Array(rawString.length);
for(var i = 0; i < rawString.length; i++)
{
uint8Array[i] = rawString.charCodeAt(i);
}
uint16Array = new Uint16Array(uint8Array.buffer);
console.log(uint16Array);
})
.catch(err => { throw err });
}
First I fetch my base64 string from a file. Then using window.atob it is converted to a JavaScript string. After this, I need to fill a Uint8Array with every byte loaded from the string. Finally, I had to convert this Uint8Array, into the final Uint16Array.
That was tough to achieve exactly what I was looking. But I have found it.
You can use this function that converts a base64 string into a binary Uint16 Array
var BASE64_MARKER = ';base64,';
function convertDataURIToBinary(dataURI) {
var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
var base64 = dataURI.substring(base64Index);
var raw = window.atob(base64);
var rawLength = raw.length;
var array = new Uint16Array(new ArrayBuffer(rawLength));
for(i = 0; i < rawLength; i++) {
array[i] = raw.charCodeAt(i);
}
return array;
}
If you're targeting Firefox and feeling adventurous you can shorten the function down to this:
var BASE64_MARKER = ';base64,';
function convertDataURIToBinaryFF(dataURI) {
var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
var raw = window.atob(dataURI.substring(base64Index));
return Uint8Array.from(Array.prototype.map.call(raw, function(x) {
return x.charCodeAt(0);
}));
};

Recursive function Javascript concat array result - ImageJ

I try to make a javascript code for ImageJ, wich have to goal to allow me to select a directory, and return me all jpg and jpeg file in the directory and sub-directory.
I have do this code, it's work well for direct children, he call well getJpeg on subdirectories (I have verify it with some IJ.log), but the jpeg Array is not concat with result of recursives call
Have you an idea?
importClass(Packages.ij.IJ);
importClass(Packages.ij.plugin.frame.RoiManager);
importClass(Packages.ij.gui.GenericDialog);
importClass(Packages.ij.io.OpenDialog);
importClass(Packages.java.io.File);
var dirstr = IJ.getDirectory("Choose the stacks folder");
var jpeg = getJpeg(dirstr);
for (var i = 0; i < jpeg.length; i++) {
IJ.log(jpeg[i]);
}
function getJpeg(dirst) {
var dir = new java.io.File(dirst);
var names = dir.listFiles();
var jpeg = new Array();
for (var i = 0 ; i < names.length; i++) {
if(names[i].isDirectory()){
jpeg.concat(getJpeg(names[i].getPath()));
continue;
}
var namest = names[i].getName();
if (namest.match(/.*\.jpg|jpeg/)) {
var path = names[i].getPath();
jpeg.push(path);
}
}
return jpeg;
}
Thank you :)
The problem come from a missread from concat documentation ( w3schools.com/jsreF/jsref_concat_array.asp ). Have to put
jpeg = jpeg.concat(getJpeg(names[i].getPath()));

javascript bzip2 stream as Uint8Array

From a previous answer on Stackoverflow I found the bzip2 lib for javascript: https://github.com/antimatter15/bzip2.js
It does pretty much what I need, except that I need the output as a Uint8Array instead of a string.
I use this function from the bzip2 lib:
bzip2.simple = function(bits){
var size = bzip2.header(bits);
var all = '', chunk = '';
do{
all += chunk;
chunk = bzip2.decompress(bits, size);
}while(chunk != -1);
return all;
}
I found a way of transforming the string output into a Uint8Array :
function str2ab(str)
{
var buf = new ArrayBuffer(str.length);
var bufView = new Uint8Array(buf);
for (var i=0, strLen=str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
};
But for speed reasons I was hoping there was a good way to re-write the original bzip2 function so it directly outputs as a Uint8Array.
One of the main problems I’m having is that I don’t know how to dynamically increase the array.

Javascript: Uint16Array(length) returns Invalid argument

I'm trying to read a file in order to perform certain actions on the binary data before sending it to a server.
At a certain point I'm trying to convert the data returned by FileReader.readAsArrayBuffer() to an Uint16Array(). However upon doing so the code allocating the array fails with: 'Error: invalid arguments'. I need the data to be a hex string representing the entire binary.
This is the code I'm using:
function HexToHexString(ByteBuffer)
{
//Similar constructs like: 'var Array = new Uint16Array(ByteBuffer);' also fail
var View = new DataView(ByteBuffer);
var Array = new Uint16Array((ByteBuffer.byteLength / 2)); // <- this line fails
for(var i = 0; i < Array.length; i++)
{
Array[i] = View.getUint16(i*2);
}
return String.fromCharCode.apply(null, Array);
}
function OnReadFileCompletion(FileReadEvent)
{
if(FileReadEvent.target.readyState == FileReader.DONE)
{
// Debug code, will be replaced:
document.getElementById('byte_content').textContent = HexToHexString(FileReadEvent.target.result);
//FileReadEvent.target.result;
}
}
function ReadFile(File, ResultFunction)
{
var Reader = new FileReader();
Reader.onloadend = ResultFunction;
Reader.readAsArrayBuffer(File.slice(0, File.size - 1));
}
File Is a file object, ResultFunction is OnReadFileCompletion(), ByteBuffer is an '[object ArrayBuffer]'.
When I output the size of the ArrayBuffer it matches the size of the file (82kb). I'm on firefox 32 with no plugins installed.
I'm not a javascript programmer, does anyone know what I'm doing wrong?
Edit1:
It appears to have something to-do with the size of the file I'm trying to read, using a 1kb text file appears to work while a 82kb binary file does not.
Edit2
I spoke too soon, perhaps it has something to do with file types. An image file of 200kb works, while an executable of 82 does not.
It appears that javascript does not allow executable files to be accesed this way, does anybody know of any way where I could possibly access the data in hex form?
try using .length instead of byteLength
I've hacked together code that works for me, I don't know why this works or what I did wrong the other time. But it works.
function ApplyPadding(Number, PaddingLength)
{
var s = Number + "";
while (s.length < PaddingLength)
s = "0" + s;
return s;
}
function HexToHexString(ByteBuffers)
{
var AnArray = new Uint8Array(ByteBuffers);
var Result = "";
for(var i = 0; i < AnArray.length; i++)
{
if(i%2==0)
Result += ApplyPadding(AnArray[i].toString(16), 2);
}
return Result;
}
function HexStringToHex(aString)
{
var Buffer = new ArrayBuffer(aString.length*2); // 2 bytes for each char
var BufferView = new Uint16Array(Buffer);
for (var i = 0;i < aString.length; i++)
{
BufferView[i] = aString.charCodeAt(i);
}
return Buffer;
}
function OnReadFileCompletion(FileReadEvent)
{
if(FileReadEvent.target.readyState == FileReader.DONE)
{
//document.getElementById('byte_content').textContent =FileReadEvent.target.result;
var DataOfFile = HexStringToHex(FileReadEvent.target.result);
var FinalData = HexToHexString(DataOfFile);
document.getElementById('byte_content').textContent = FinalData;
//FileReadEvent.target.result;
}
}
function ReadFile(File, ResultFunction)
{
var Reader = new FileReader();
Reader.onloadend = ResultFunction;
Reader.readAsBinaryString(File.slice(0, File.size - 1));
}

Categories