I want to pass a large array from js to c++ using emscripten. But am only able to pass a small array of sizes 3 or 4. I keep getting this error for large arrays Uncaught (in promise) RuntimeError: memory access out of bounds
How would I pass an array of length 160000?
.js
function __CPPModule__(size,iterations) {
const myModule = Module();
return myModule.then((Module) => {
// Import function from Emscripten generated file
var cppf= Module.cwrap('testFunction', 'number', ['number', 'number', 'number']);
// Create example data to test float_multiply_array
var data = new Float32Array(size*size);
// Get data byte size, allocate memory on Emscripten heap, and get pointer
var nDataBytes = data.length * data.BYTES_PER_ELEMENT;
var dataPtr = Module._malloc(nDataBytes);
// Copy data to Emscripten heap (directly accessed from Module.HEAPU8)
var dataHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr, nDataBytes);
dataHeap.set(new Uint8Array(data.buffer));
// Call function and get result
cppf(dataHeap.byteOffset, size, iterations);
var result = new Float32Array(dataHeap.buffer, dataHeap.byteOffset, data.length, iterations);
// Free memory
Module._free(dataHeap.byteOffset);
return result
})
}
async function jsTocpp(size,iterations){
let f = await __CPPModule__(size,iterations)
return f
}
var size = 400
var iterations = 0
console.log(jsTocpp(size,iterations));
.cpp
class World{
public:
//Constructor
World(int size) {
...
}
double map[400][400] ={0.0};
};
extern "C" {
EMSCRIPTEN_KEEPALIVE int testFunction(float *arr, int size, int iter) {
World world(size);
int row = size;
int col = size;
for(int i = 0; i < row; ++i)
for(int j = 0; j < col; ++j)
arr[i * col + j] = (float)world.map[i][j]; //copy the data from 2D array to 1D array
return 0;
}
}
There are many ways for Connecting C++ and JavaScript. The usual way is to use Embind. Use the emscripten::val
object to access the JavaScript object in the C++ code. Emscripten also offers the possibility to Access memory from JavaScript in C++ and to create a memory view.
A simple C++ function that can be called from JavaScript to get and decode a Float32Array
looks like this (file testArray.cpp):
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <iostream>
#include <vector>
void passArray(const emscripten::val &floatArrayObject) {
unsigned int length = floatArrayObject["length"].as<unsigned int>();
std::vector<float> floatArray;
floatArray.resize(length);
auto memory = emscripten::val::module_property("HEAPU8")["buffer"];
auto memoryView = floatArrayObject["constructor"].new_(memory, reinterpret_cast<uintptr_t>(floatArray.data()), length);
memoryView.call<void>("set", floatArrayObject);
for (auto &floatValue : floatArray) {
std::cout << floatValue << ", ";
}
std::cout << std::endl;
}
EMSCRIPTEN_BINDINGS(my_module) {
emscripten::function("passArray", &passArray);
}
Compile the file with the -lembind
option. e.g.:
emcc --no-entry --bind -O0 "testArray.cpp" -o "testArray.js" -s WASM=1
A simple HTML code that executes the function might look as follows
<script>
var Module = {
onRuntimeInitialized: function() {
const floatArray = new Float32Array([1.1, 2.2, 3.3, 4.4, 5.5]);
Module.passArray(floatArray);
}
};
</script>
<script src="testArray.js"></script>
and generates to following output:
1.1, 2.2, 3.3, 4.4, 5.5,