使用 emscripten 如何将 C++ uint8_t 数组获取到 JS Blob 或 UInt8Array
Using emscripten how to get C++ uint8_t array to JS Blob or UInt8Array
在 emscripten C++ 中,我有
class MyClass {
public:
MyClass() {}
std::shared_ptr<std::vector<uint8_t>> buffer;
int getPtr() {
return (int)(buffer->data());
}
int getLength() {
return buffer->size();
}
};
EMSCRIPTEN_BINDINGS() {
class_<MyClass>("MyClass").constructor()
.function("getLength",&MyClass::getLength)
.function("getPtr",&MyClass::getPtr,
allow_raw_pointers());
}
我可以从 JS 调用 getLength() 和 getPtr() 但我不知道如何让 JS 将其视为 ArrayBuffer 以作为 Blob 下载。
如何将缓冲区数据以某种形式导入 JS,然后我可以使用类似于 https://github.com/kennethjiang/js-file-download/blob/master/file-download.js.
的代码下载它
目前 WebAssembly 只定义了基本的数字类型,用于 JS 和 WASM 之间的通信。没有对象类型也没有数组类型。 This is the WebAssembly's design goal. Emscripten 做了一些 hack 来制作 C++ Class <=> JS 绑定,但它们不是 WASM 标准。
WebAssembly.Memory()
但是有一种方法可以获取数组。 JS 可以直接访问 WASM 模块的内部内存,即使没有 API。 WASM 有一个线性内存模型,线性内存通过 WebAssembly.Memory()
. WebAssembly.Memory()
is a single ArrayBuffer WebAssembly.Memory.buffer
连接,您的 WASM 模块用作堆内存区域,内存分配(例如 malloc()
)发生。
1。以 UInt8Array
访问它
这是什么意思?这意味着 你从 getPtr()
得到的指针(JS 端的整数)实际上是 WebAssembly.Memory.buffer
的偏移量。
Emscripten 自动生成 JS(这是从名为 preamble.js) code that create WebAssembly.Memory()
. You can search Emscripten-generated code yourself and should be able find out a line similar to this line:
的模板生成的
Module['wasmMemory'] = new WebAssembly.Memory({ 'initial': TOTAL_MEMORY / WASM_PAGE_SIZE, 'maximum': TOTAL_MEMORY / WASM_PAGE_SIZE });
所以你可以通过Module['wasmMemory'].buffer
访问你的WASM模块使用的ArrayBuffer:
let instance = new Module.MyClass();
// ... Do something
let ptr = instance.getPtr();
let size = instance.getLength();
// You can use Module['env']['memory'].buffer instead. They are the same.
let my_uint8_buffer = new Uint8Array(Module['wasmMemory'].buffer, ptr, size);
2。 Emscripten HEAPU8
或者,Emscripten 提供 an official way to access the heap memory region as typed arrays: HEAPU8
,HEAPU16
, HEAPU32
, and etc. as defined here。所以你可以这样做:
let instance = new Module.MyClass();
// ... Do something
let ptr = instance.getPtr();
let size = instance.getLength();
let my_uint8_buffer = new Uint8Array(Module.HEAPU8.buffer, ptr, size);
使用 HEAPU8
会更安全,因为 HEAPU8
已记录,而 Module['wasmMemory']
的属性名称有点未记录并且可能会更改;但他们做同样的事情。
3。使用 emscripten::val
(仅限 C++)
Emscripten 还提供了一个class 叫做emscripten::val
供C++ 开发人员在JS 和C++ 之间进行交互。为了方便起见,这抽象了任何 JS/C++ 类型。您可以使用此获取数组。
这是取自 the documentation 和 Glenn 评论的示例:
#include <emscripten/bind.h>
#include <emscripten/val.h>
emscripten::val getInt8Array() {
return emscripten::val(
emscripten::typed_memory_view(buffer->size(),
buffer->data()));
}
EMSCRIPTEN_BINDINGS() {
function("getInt8Array", &getInt8Array);
}
然后你可以在JS端调用getInt8Array()
来获取类型数组。
结论
此处建议使用 3 个选项从 WASM 获取数组。无论如何,我认为你应该理解 WebAssembly.Memory
的概念和选项 1 背后的东西,因为这是从 WASM 获取数组的最低级别,而且最重要的是,这是非托管的并且不安全的内存访问,因此在 C/C++ 端释放或修改对象时很容易损坏数据 。此特定案例需要了解低级含义。
实际上,我只是通过一个 hacky workaround 解决了这个问题。定义了自定义 Module.print
以通过 printf
语句捕获您的数据。我的例子:
C++
bool first = true;
for (auto i : settings)
{
if (!first)
{
printf(",");
}
first = false;
printf("%u", i);
}
printf("\n");
(这将打印出类似于 1,255,76,31
的内容)
JS:(下面需要在包含emscripten输出.js
的<script>
标签之前定义)
let arrayFromC;
var Module = {
preRun: [],
postRun: [],
print: function (printOutput) {
arrayFromC = printOutput.split(",");
},
};
在 emscripten C++ 中,我有
class MyClass {
public:
MyClass() {}
std::shared_ptr<std::vector<uint8_t>> buffer;
int getPtr() {
return (int)(buffer->data());
}
int getLength() {
return buffer->size();
}
};
EMSCRIPTEN_BINDINGS() {
class_<MyClass>("MyClass").constructor()
.function("getLength",&MyClass::getLength)
.function("getPtr",&MyClass::getPtr,
allow_raw_pointers());
}
我可以从 JS 调用 getLength() 和 getPtr() 但我不知道如何让 JS 将其视为 ArrayBuffer 以作为 Blob 下载。
如何将缓冲区数据以某种形式导入 JS,然后我可以使用类似于 https://github.com/kennethjiang/js-file-download/blob/master/file-download.js.
的代码下载它目前 WebAssembly 只定义了基本的数字类型,用于 JS 和 WASM 之间的通信。没有对象类型也没有数组类型。 This is the WebAssembly's design goal. Emscripten 做了一些 hack 来制作 C++ Class <=> JS 绑定,但它们不是 WASM 标准。
WebAssembly.Memory()
但是有一种方法可以获取数组。 JS 可以直接访问 WASM 模块的内部内存,即使没有 API。 WASM 有一个线性内存模型,线性内存通过 WebAssembly.Memory()
. WebAssembly.Memory()
is a single ArrayBuffer WebAssembly.Memory.buffer
连接,您的 WASM 模块用作堆内存区域,内存分配(例如 malloc()
)发生。
1。以 UInt8Array
访问它这是什么意思?这意味着 你从 getPtr()
得到的指针(JS 端的整数)实际上是 WebAssembly.Memory.buffer
的偏移量。
Emscripten 自动生成 JS(这是从名为 preamble.js) code that create WebAssembly.Memory()
. You can search Emscripten-generated code yourself and should be able find out a line similar to this line:
Module['wasmMemory'] = new WebAssembly.Memory({ 'initial': TOTAL_MEMORY / WASM_PAGE_SIZE, 'maximum': TOTAL_MEMORY / WASM_PAGE_SIZE });
所以你可以通过Module['wasmMemory'].buffer
访问你的WASM模块使用的ArrayBuffer:
let instance = new Module.MyClass();
// ... Do something
let ptr = instance.getPtr();
let size = instance.getLength();
// You can use Module['env']['memory'].buffer instead. They are the same.
let my_uint8_buffer = new Uint8Array(Module['wasmMemory'].buffer, ptr, size);
2。 Emscripten HEAPU8
或者,Emscripten 提供 an official way to access the heap memory region as typed arrays: HEAPU8
,HEAPU16
, HEAPU32
, and etc. as defined here。所以你可以这样做:
let instance = new Module.MyClass();
// ... Do something
let ptr = instance.getPtr();
let size = instance.getLength();
let my_uint8_buffer = new Uint8Array(Module.HEAPU8.buffer, ptr, size);
使用 HEAPU8
会更安全,因为 HEAPU8
已记录,而 Module['wasmMemory']
的属性名称有点未记录并且可能会更改;但他们做同样的事情。
3。使用 emscripten::val
(仅限 C++)
Emscripten 还提供了一个class 叫做emscripten::val
供C++ 开发人员在JS 和C++ 之间进行交互。为了方便起见,这抽象了任何 JS/C++ 类型。您可以使用此获取数组。
这是取自 the documentation 和 Glenn 评论的示例:
#include <emscripten/bind.h>
#include <emscripten/val.h>
emscripten::val getInt8Array() {
return emscripten::val(
emscripten::typed_memory_view(buffer->size(),
buffer->data()));
}
EMSCRIPTEN_BINDINGS() {
function("getInt8Array", &getInt8Array);
}
然后你可以在JS端调用getInt8Array()
来获取类型数组。
结论
此处建议使用 3 个选项从 WASM 获取数组。无论如何,我认为你应该理解 WebAssembly.Memory
的概念和选项 1 背后的东西,因为这是从 WASM 获取数组的最低级别,而且最重要的是,这是非托管的并且不安全的内存访问,因此在 C/C++ 端释放或修改对象时很容易损坏数据 。此特定案例需要了解低级含义。
实际上,我只是通过一个 hacky workaround 解决了这个问题。定义了自定义 Module.print
以通过 printf
语句捕获您的数据。我的例子:
C++
bool first = true;
for (auto i : settings)
{
if (!first)
{
printf(",");
}
first = false;
printf("%u", i);
}
printf("\n");
(这将打印出类似于 1,255,76,31
的内容)
JS:(下面需要在包含emscripten输出.js
的<script>
标签之前定义)
let arrayFromC;
var Module = {
preRun: [],
postRun: [],
print: function (printOutput) {
arrayFromC = printOutput.split(",");
},
};