当数组的类型不是 Uint8 时,如何从 Javascript 代码访问 asm.js 堆上的数组?
How do I access arrays on the asm.js heap from Javascript code, when the array's type is not Uint8?
我正在尝试使用 emscripten 和 asm.js 来加速我的 Javascript 代码。我需要从 Int32Array
获取数据到我编译的 C 函数中。根据 this Github site,我可以分配一个缓冲区,将数据复制到其中,然后调用一个将该缓冲区的指针作为输入的函数,如下所示:
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);
但它对 Uint8Array
以外的任何东西都不起作用,因为 Uint8Array.set
"helpfully" 将输入数组的数据类型转换为 Uint8
而不是只做一个原始的复制。换句话说,如果我尝试使用此方法将 Int32Array.of(1, -1)
复制到地址 100 处的堆中,我将得到
{ ... 100:1, 101:255, 102:0, 103:0, 104:0, 105:0, 106:0, 107:0 ... }
而不是
{ ... 100:1, 101:0, 102:0, 103:0, 104:255, 105:255, 106:255, 107:255 ... }
(假设小端)
那么我应该如何将数据复制到 asm.js 堆或从堆中复制数据?我知道 ArrayBuffer
对象可以按位转换为任何类型的数组类型,但是 似乎不可能进行相反的操作 (更正:请参阅 Jaromanda X 的评论) .此外,我阅读、考虑并拒绝了该网站关于尽可能使用 setValue
/ getValue
的建议,因为我有数百万的东西要复制,我想避免每次调用一个函数的开销完全有可能。
所以事实证明我错了:可以将 Int32Array
转换为 ArrayBuffer
,然后转换为原始数组的 Uint8Array
视图。这是一个函数的完整示例,该函数总结了 int32_t
的数组,实现为将 int32_t
的数组传递给 C 函数的 JS 函数:
sum.c(编译为sum.c.js)
#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;
}
sum.js
"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;
}
我正在尝试使用 emscripten 和 asm.js 来加速我的 Javascript 代码。我需要从 Int32Array
获取数据到我编译的 C 函数中。根据 this Github site,我可以分配一个缓冲区,将数据复制到其中,然后调用一个将该缓冲区的指针作为输入的函数,如下所示:
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);
但它对 Uint8Array
以外的任何东西都不起作用,因为 Uint8Array.set
"helpfully" 将输入数组的数据类型转换为 Uint8
而不是只做一个原始的复制。换句话说,如果我尝试使用此方法将 Int32Array.of(1, -1)
复制到地址 100 处的堆中,我将得到
{ ... 100:1, 101:255, 102:0, 103:0, 104:0, 105:0, 106:0, 107:0 ... }
而不是
{ ... 100:1, 101:0, 102:0, 103:0, 104:255, 105:255, 106:255, 107:255 ... }
(假设小端)
那么我应该如何将数据复制到 asm.js 堆或从堆中复制数据?我知道 ArrayBuffer
对象可以按位转换为任何类型的数组类型,但是 似乎不可能进行相反的操作 (更正:请参阅 Jaromanda X 的评论) .此外,我阅读、考虑并拒绝了该网站关于尽可能使用 setValue
/ getValue
的建议,因为我有数百万的东西要复制,我想避免每次调用一个函数的开销完全有可能。
所以事实证明我错了:可以将 Int32Array
转换为 ArrayBuffer
,然后转换为原始数组的 Uint8Array
视图。这是一个函数的完整示例,该函数总结了 int32_t
的数组,实现为将 int32_t
的数组传递给 C 函数的 JS 函数:
sum.c(编译为sum.c.js)
#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;
}
sum.js
"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;
}