unsafe使って書いてみた。無駄なコピーはなくなった。
けどどうだろう。fixedしたことによってGCが回収できなくなっちゃった。ホストが読んだらもういらないんだけど。
でも、そもそもWASMだとまだGC動かなかった気もする。プロポーザルは出てる。
Blazorはそこらへんどうなってるんだろう。
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 bytes = Encoding.ASCII.GetBytes(str);
unsafe {
fixed(byte* addr = bytes) {
var ptr = Marshal.AllocHGlobal(sizeof(int) + sizeof(byte*));
Marshal.WriteInt32(ptr, bytes.Length);
Marshal.WriteIntPtr(ptr + sizeof(int), (nint)addr);
return ptr;
}
}
}
private static string ManagedString() {
return "This is managed string value";
}
}
ホスト側
using System.Text;
using Wasmtime;
var wasm = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "lib.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 = mem.ReadInt32(ptr);
var addr = mem.ReadIntPtr(ptr + sizeof(int));
var str = mem.ReadString(addr, len, Encoding.ASCII);
Console.WriteLine(str);
なんかやっぱりコピーしたほうがいい気がした。通信レイヤーとして大き目のバッファを用意しておいてリングバッファ的に使うとかそういう感じで。