Learning WASM #10
November 12, 2023•200 words
Managedな値をホストとの共有メモリに書き出してみる。
とは言っても、バイト列に変換して、共有メモリにコピーするだけ。
WASM側
using System.Runtime.InteropServices;
using System.Text;
namespace classlib;
public static class Class1
{
[UnmanagedCallersOnly(EntryPoint = "Read")]
public static IntPtr Read()
{
var str = ManagedString();
return WriteToMemory(str);
}
private static IntPtr WriteToMemory(string str) {
var prefix = sizeof(int);
var bytes = Encoding.ASCII.GetBytes(str);
var ptr = Marshal.AllocHGlobal(prefix + bytes.Length);
Marshal.WriteInt32(ptr, bytes.Length);
for (var i = 0; i < bytes.Length; i++) {
Marshal.WriteByte(ptr + prefix + i, bytes[i]);
}
return ptr;
}
private static string ManagedString() {
return "This is managed string value";
}
}
最初の4バイトはペイロードの長さをintで入れる。C#のエンディアンって何だっけかな。
まあどっちもC#だから問題なし。
ホスト側
using System.Text;
using Wasmtime;
var wasm = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "lib.wasm");
Console.WriteLine($"Load wasm file: {wasm}");
using var engine = new Engine();
using var module = Module.FromFile(engine, wasm);
using var linker = new Linker(engine);
using var store = new Store(engine);
linker.DefineWasi();
store.SetWasiConfiguration(new WasiConfiguration());
var instance = linker.Instantiate(store, module);
var init = instance.GetAction("_initialize");
if (init is null)
{
Console.WriteLine("error: MyAdd export is missing");
return;
}
init();
var read = instance.GetFunction<int>("Read");
if (read is null)
{
Console.WriteLine("error: Read export is missing");
return;
}
var ptr = read();
var mem = instance.GetMemory("memory")!;
var len = BitConverter.ToInt32(mem.GetSpan(ptr, sizeof(int)));
var str = Encoding.ASCII.GetString(mem!.GetSpan(ptr + sizeof(int), len));
Console.WriteLine(str);
実行結果
This is managed string value
コピーしてるのでそこがボトルネック。バイト列とるときに共有メモリに置きたい。
そもそも、ゲスト側のメモリ自体がmemoryなわけだからbytesのポインタさえ取れればホストでも取れそうだ。unsafeでコンパイルしないとだめだけど。