I'm trying to use emscripten and asm.js in order to speed up my Javascript code. I need to get data from an Int32Array
into my compiled C function. According to this Github site I can allocate a buffer, copy data into it, and call a function taking that buffer's pointer as input, like so:
var buf = Module._malloc(myTypedArray.length*myTypedArray.BYTES_PER_ELEMENT);
Module.HEAPU8.set(myTypedArray, buf);
Module.ccall('my_function', 'number', ['number'], [buf]);
Module._free(buf);
But it doesn't work for anything besides an Uint8Array
, because Uint8Array.set
"helpfully" converts the input array's data type to Uint8
instead of just doing a raw copy. In other words, if I try to copy Int32Array.of(1, -1)
into the heap at address 100 using this method, I'll get
{ ... 100:1, 101:255, 102:0, 103:0, 104:0, 105:0, 106:0, 107:0 ... }
instead of
{ ... 100:1, 101:0, 102:0, 103:0, 104:255, 105:255, 106:255, 107:255 ... }
(assuming little endian)
So how am I supposed to copy the data to and from the asm.js heap? I understand that an ArrayBuffer
object can be bitwise-casted to any typed array type, but it doesn't seem possible to do the reverse (correction: see Jaromanda X's comment). Also, I read, considered, and rejected the website's suggestion to prefer setValue
/ getValue
whenever possible because I have millions of things to copy and I'd like to avoid the overhead of one function call each if at all possible.
So it turns out I was wrong: it is possible to convert Int32Array
to an ArrayBuffer
and consequently to an Uint8Array
view of the original array. Here's a complete example of a function that sums up an array of int32_t
implemented as a JS function passing an array of int32_t
to a C function:
#include <stdint.h>
#include <stddef.h>
double sum_i32(const int32_t* ints, size_t count) {
double result = 0.0;
while (count-- > 0) {
result += *ints++;
}
return result;
}
"use strict";
const cModule = require("./sum.c.js");
module.exports.sum_i32 = function sum_i32(array) {
// Convert to array of int32_t if needed.
if (!(array instanceof Int32Array)) {
array = new Int32Array(array);
}
// Allocate a buffer to store a copy of the input array.
const ptr = cModule._malloc(array.length * 4);
let result = NaN;
if (ptr === 0) {
throw "Out of memory";
}
try {
// View int32_t array as uint8_t using the int array's ArrayBuffer.
const u8view = new Uint8Array(array.buffer);
// Copy it to the right position in the heap and call the C function.
cModule.HEAPU8.set(u8view, ptr);
result = cModule._sum_i32(ptr, array.length);
} finally {
cModule._free(ptr);
}
return result;
}