WebAssemblyでちょっと分かったこと
それは、基本内部表現が整数か小数かなので、文字列やバイト列の扱いが結構大変と言うことです。
例えば以下は適当に作った、単にバイト列となるデータを受取り、新しくコピーして返すだけの関数です。
#[wasm_bindgen]
pub fn get_buffer(buf: &[u8]) -> Vec<u8> {
let mut new_buf = Vec::new();
for i in buf.iter() {
new_buf.push(*i);
}
new_buf
}
これ、Node側でどういう操作をしなきゃいけないかというと、こうなるんですよ。 もちろん、関数の中で使ってる関数も実装が必要です。
module.exports.get_buffer = function(buf) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passArray8ToWasm0(buf, wasm.__wbindgen_malloc);
const len0 = WASM_VECTOR_LEN;
wasm.get_buffer(retptr, ptr0, len0);
var r0 = getInt32Memory0()[retptr / 4 + 0];
var r1 = getInt32Memory0()[retptr / 4 + 1];
var v2 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1, 1);
return v2;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
};
端的に言うとC的なメモリ確保と解放の操作をやってるんですよね。実に原始的です。これ、javascriptですよ。書く気起きます?(笑) 起きませんね!こういうことを考えると、Rustは良い自動化ツールが揃っているようです。
wasm-packとwasm-bindgenでNode・ブラウザ連携が圧倒的に楽になる
RustがWebAssemblyに強い、ということを間違いなく感じたのがこのツールです。
wasm-pack
はビルドラッパーみたいなツールで、ブラウザ・Node.jsに対応した、いわゆるnode_modules
に入れるパッケージを作ってくれるツールです。
cargo install wasm-pack
wasm-bindgen
はCargo.toml
で導入。
[package]
name = "hello-wasm"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2.92"
使う時はこんな感じ
wasm-pack build --target nodejs
# Nodeプロジェクトへの紐付け(node_modulesにインストール)
npm link ./pkg
たったこれだけで超〜簡単にRustでの処理をwasm化し、更にはjavascriptパッケージ化してくれます。すごい。
Nodeのスクリプトはとてもラクです。以下は適当なjpegファイルを置いておき、wasmで読んでコピーを取得、それを別ファイルに書き込む処理です。
const wasm = require("hello-wasm");
const fs = require("fs");
const PATH = "test.jpg";
const buf = fs.readFileSync(PATH);
const res = wasm.get_buffer(buf);
fs.writeFileSync("output.jpg", res);
console.log("Wrote output.jpg");
素晴らしい簡潔さ。夢が広がりますね。
ちょっと個人的にはWebAssembly結構面白い技術だと思うので、もうちょっと深ぼっていきたいです。