В 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());
}
Я могу вызвать getLength () и getPtr () из JS, но я не знаю, как заставить JS рассматривать его как ArrayBuffer для загрузки как BLOB-объекта.
Как я могу получить данные буфера в JS в форме, где я могу затем загрузить их, используя код, подобный https://github.com/kennethjiang/js-file-download/blob/master/file-download.js.
В настоящее время WebAssembly определяет только основные типы номеров для связи между JS и WASM. Здесь нет ни типов объектов, ни типов массивов. Это цель дизайна WebAssembly. Emscripten сделал несколько хаков, чтобы сделать C ++ Class <=> JS привязки, но они не являются стандартом WASM.
НО есть способ получить массив. JS имеет прямой доступ к внутренней памяти модуля WASM, даже без API. WASM имеет линейную модель памяти, а линейная память соединена через WebAssembly.Memory()
. WebAssembly.Memory()
это один ArrayBuffer WebAssembly.Memory.buffer
где ваш модуль WASM использует в качестве области динамической памяти и где выделяется память (например, malloc()
) бывает.
Что это значит? Это означает, что указатель (целое число на стороне JS), который вы получаете от getPtr()
на самом деле смещение WebAssembly.Memory.buffer
,
Emscripten автоматически генерирует JS (это генерируется из шаблона под названием preamble.js) код, который создает WebAssembly.Memory()
, Вы можете искать код, сгенерированный Emscripten, самостоятельно и сможете найти строку, аналогичную к этой линии:
Module['wasmMemory'] = new WebAssembly.Memory({ 'initial': TOTAL_MEMORY / WASM_PAGE_SIZE, 'maximum': TOTAL_MEMORY / WASM_PAGE_SIZE });
Таким образом, вы можете получить доступ к ArrayBuffer, используемому вашим модулем WASM, через Module['wasmMemory'].buffer
:
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);
HEAPU8
В качестве альтернативы Emscripten предлагает официальный способ доступа к области памяти кучи в виде типизированных массивов: HEAPU8
,HEAPU16
, HEAPU32
и т. д., как определено Вот. Так что вы можете сделать так:
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']
является своего рода недокументированным и может быть изменено; но они делают то же самое.
emscripten::val
(Только C ++)Emscripten также предоставляет класс под названием emscripten::val
для разработчиков C ++ взаимодействовать между JS и C ++. Это абстрагирует любые типы JS / C ++ для удобства. Вы можете получить массив, используя это.
Это пример, взятый из документация и комментарий Гленна:
#include <emscripten/bind.h>
#include <emscripten/val.h>
emscripten::val getBytes() {
return emscripten::val(
emscripten::typed_memory_view(buffer->size(),
buffer->data()));
}
EMSCRIPTEN_BINDINGS() {
function("getInt8Array", &getInt8Array);
}
Тогда вы можете позвонить getInt8Array()
в сторону JS, чтобы получить типизированный массив.
Для получения массива из WASM предлагается 3 варианта. В любом случае, я думаю, что вы должны понимать понятия WebAssembly.Memory
и что стоит за вариантом 1, потому что это самый низкий уровень для получения массива из WASM, и, самое главное, это неуправляемый и небезопасный доступ к памяти, поэтому легко испортить данные когда объект освобождается или модифицируется на стороне C / C ++; Знание последствий низкого уровня требуется для этого конкретного случая.
Других решений пока нет …