Learning WASM #2

Protocol Bufferフォーマットを使用して、dotnetとwasmの間でオブジェクトをやり取りするコードを書いてみた。

https://github.com/iwate/learn-wasm-with-dotnet/tree/main/challenge01


こんな感じのプロトファイルを用意して、



dotent側で生成した Hello をバイナリに書き出して、wasmのshared memoryに書き込んでから wasmモジュールの`run` を実行する。

shared memoryから Helloをパースするとwasmで加工された情報が受け取れる。


using System;
using System.IO;
using Google.Protobuf;
using Iwate.WasmtimeChallenge;
using Wasmtime;

var bytes = new byte[32];
using (var stream = new MemoryStream(bytes))
{
    new Hello { Message = "Hello, " }.WriteDelimitedTo(stream);
    stream.Flush();
}


using var engine = new Engine();
using var module = Module.FromTextFile(engine, "memory.wat");
using var host = new Host(engine);
using var mem = host.DefineMemory("", "mem");

for (var i = 0; i < bytes.Length; i++)
{
    mem.WriteByte(100 + i, bytes[i]);
}

using dynamic instance = host.Instantiate(module);
instance.run();

var result = mem.Span.Slice(100, 32).ToArray();
using var stream1 = new MemoryStream(result);
var hello = Hello.Parser.ParseDelimitedFrom(stream1);
Console.WriteLine(hello.Message);
Console.WriteLine(Helpers.HexDump(result));


出力結果はこんな感じ。


wasmに渡す前は Hello, だったのが Hello,WASM! になって返ってきてる。


Hello,WASM!
00000000   0E 0A 0C 48 65 6C 6C 6F  2C 57 41 53 4D 21 00 00   ・・・Hello,WASM!・・
00000010   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ・・・・・・・・・・・・・・・・


wasmはこんな感じ


(module
	(import "" "mem" (memory 1))
	(data (i32.const 0) "WASM!")
	(func $run
		(memory.copy (i32.add (i32.load8_u (i32.const 102)) (i32.const 102)) (i32.const 0) (i32.const 5))
		(i32.store8 (i32.const 102) (i32.add (i32.load8_u (i32.const 102)) (i32.const 5)))
		(i32.store8  (i32.const 100) (i32.add (i32.load8_u (i32.const 102)) (i32.const 2)))
	)
	(export "run" (func $run))
)



うまいこといった。shared memoryにコピーするのなんかバカっぽいなあ。最初からそっちのバイナリに書き込めればいいんだけど、`Span<byte>`を`Stream`にする方法がよくわからない。


次は、wasmをwasファイルからじゃなくて、rustとかで生成したやつで試そう。


rust用のprotocol bufferからシリアライズ、デシリアライズしたバイナリでdotnetと通信させてみたい。