Search code examples
javascriptrustwebassemblywasm-bindgen

How to pass an array of primitive element type from javascript to wasm in Rust fast?


Let me ask how to pass an array holding uint8 elements from javascript to wasm in Rust fast?

I found one method in: As JsValue.

Though I wonder if there is some clearly faster one when all element values are same primitive type and in a particular range.

For example, you encode a uint8 array to a string then pass the string to wasm not through JsValue.

Or I wonder if you can pass any array of primitive element type to wasm not through JsValues.

Edit: After reading the first answer, I tried to follow the answer with my understanding as follows. It seems to be successful. Is my understanding correct?


import * as wasm from "../pkg";
import * as bg_wasm from '../pkg/PACKAGE_NAME_bg.wasm';
const RUST_STRUCT = wasm.RUST_STRUCT.new();

const cellsPtr = RUST_STRUCT.cells();
const cells = new Uint8Array(bg_wasm.memory.buffer, cellsPtr, 100);
console.log( cells[50]);// 0
for(let i=0;i<100;i++){cells[i]=i;}
console.log( cells[50]);//50
console.log( "end");

Vec::leak


Solution

  • The WASM virtual machine has its own memory address space, that is seen from JS as a big ArrayBuffer usually named wasm.memory. Any value that you pass from JS to WASM has to be copied from its own variable to that memory.

    But there is a nice trick. If you allocate a buffer from Rust side, such a Vec<u8>, and then somehow pass a reference to it to JS, then the &mut [u8] will become a JS Uint8Array, that is actually a direct view of the Rust memory. Zero copy overhead.

    You just have to be careful not to use the reference from JS in a way that breaks any Rust invariant, such as passing it back as another reference, such that you get two &mut _ to the same value. Also you have to keep this memory alive in Rust for as long as JS holds the reference. It may even be a good idea to call Vec::leak() into your buffer so that its memory lives forever.