Working with Arrays in WebAssembly
Working with arrays in emscripten can be hard for new and even for some experienced developer so lets learn how to do it!!
Introduction
In my previous blog, we have seen how to set up Emscripten and write out the first WebAssembly program. But working with integers is not enough for real-life scenarios so In this blog, we are going to learn how we can pass an array from javascript to C++. So let's start
Writing C++ code
In the code snippet below you can see a function called alter, which takes an array and multiply its value with num.
#include<emscripten.h>
#include<iostream>
extern "C"{
EMSCRIPTEN_KEEPALIVE
void alter(long int arr[],int num,int length){
for(int i = 0 ;i < length;i++){
arr[i] = arr[i] * num;
}
}
}
Here the keyword EMSCRIPTEN_KEEPALIVE is used to tell the compiler to include this function in the output code and extern "C" {} blocks tell the compiler not to change the name of any function i.e; preserve their names.
The most important thing to take note of is that the type of arr which is passed to the function alter should match with the typed array you create on the javascript side, which we will see later in the blog. So for example if we have an Int16Array
on the javascript side then we will write short int arr[]
in C++ side.
Compiling the code
To compile the code we will use the following command.
em++ array.cpp -o function.js -sEXPORTED_FUNCTIONS=['_malloc','_free'] -sEXPORTED_RUNTIME_METHODS=['ccall'] -sMODULARIZE
The keywords of the command are explained below
em++ - triggers the emscripten
array.cpp - the name of the C++ file
-o function.js - the name of the output file
-sEXPORTED_FUNCTIONS=['_malloc','_free'] - tells the compiler to include the specified methods in the output code
-sEXPORTED_RUNTIME_METHODS=['ccall'] - tells the compiler to include the specified runtime methods in the output code
-sMODULARIZE - tells the compiler to modularize the code so we can get the WASM module as a promise
This command will give us 2 files function.js(contains the necessary code to import and use wasm code) and function.wasm.
Writing JavaScript
So now we have our wasm file ready and we can start writing our javascript code. So first of all create an HTML file and add the following script tag pointing to the function.js file
<script src="./function.js"></script>
Then inside another script
tag write the following code
Module().then((mod) => {
let array = [1, 2, 3, 4, 5];
let num = 10;
let typedArray = new Int32Array(array);
let pointer = mod._malloc(
typedArray.length * typedArray.BYTES_PER_ELEMENT
);
mod.HEAP32.set(
typedArray, pointer/typedArray.BYTES_PER_ELEMENT
);
mod.ccall(
"alter",
null,
["number", "number", "number"],
[pointer, num, typedArray.length]
);
let newArray = mod.HEAP32.subarray(
pointer / typedArray.BYTES_PER_ELEMENT,
pointer / typedArray.BYTES_PER_ELEMENT + typedArray.length
);
console.log(newArray);
});
Now let's break down the code stepwise and try to understand what's going on.
Module() - this
Module
gets resolved to the wasm object which contains all of the methods which we have exported during compilation.After resolving the
Module
we have created an array and a number.Then we convert that array to desired typed array in this case, it is
Int16Array
After that, we allocated the memory for the array using
_malloc
method, which returns the starting address of the allocated memory._malloc
method takes an argument which is the total length of the typed array.Then we set the typed array in the respective HEAP as the type of the array in this case it is HEAP16 using the
set
method.set
method takes 2 arguments, the first one is the typed array itself and the second one is the offset which is always equal topointer/typedArray.BYTES_PER_ELEMENT
.After that, we called the alter method using
ccall
which is exported during the compilation, which takes 4 arguments first one is the name of the function, the second one is the return type, the third one is the types of arguments and the last one is the list of arguments.And now we are done calling our function and we want the manipulated values which we can be retrieved using the
subarray
method of the same heap on which we set the typed array. Thissubarray
method takes 2 arguments, the first one is the starting address of the array which is the pointer address returned by themalloc
method divided by the byte length of each element of the array and the second one is the ending address which is the pointeraddress/typedArray.BYTES_PER_ELEMENT + length
of the typed array.Now finally we have our altered array!!.
Output
Thanks for reading the blog, if you have any questions or find some mistakes in the blog comment below👇.