Search code examples
javascriptc++arraysemscripten

How pass a large array from js to c++ using emscripten?


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

  • Import function from Emscripten generated file.
  • Create example data to test float_multiply_array.
  • Get data byte size, allocate memory on Emscripten heap, and get pointer.
  • Copy data to Emscripten heap (directly accessed from Module.HEAPU8)
  • Call function and get result.
  • Free memory.
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

  • loop the array from world.map and add it to arr

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;
    }
}

Solution

  • 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,