Skip to content

DefaultStorageFactory が FileStream を参照しているため、FileStream を使えない環境で DryDB を使えない #45

Closed

Description

@SheetTori

DatabaseLoadOptions.DefaultStorageFactory に stream is FileStream の分岐があります。DatabaseLoadOptions.StorageFactory の既定値は DefaultStorageFactory なので、MemoryStream を渡す場合や StorageFactory に独自のものを渡す場合でも、この分岐はバイナリに含まれます。

FileStream の処理に必要なものが無い環境では、ファイルを使わない場合でも、この分岐のために DryDB を使えません。

起きていること

godot-dn2cpp(Godot の C# を C++ に変換して Web(wasm)に書き出すフォーク)4.7.2-dn2cpp.14.0 で、次のコードを書き出すと止まります(DryDB 1.1.0 に Header の Pack = 1(#44)だけを足した状態)。

var stream = new MemoryStream();
using (var builder = new DatabaseBuilder())
{
    var table = builder.CreateTable("Item", KeyEncoding.Ascii);
    table.Append("key", new byte[] { 42 });
    await builder.BuildToStreamAsync(stream);
}

stream.Position = 0;
using var db = await ReadOnlyDatabase.OpenAsync(stream);
using var value = db.GetTable("Item").Get("key"u8);
ERROR: Export .NET Project: 'DryDbWebRepro.so' imports symbols nothing in the exported page defines ... Unsatisfied:
  SystemNative_PReadV
  SystemNative_PWriteV

DefaultStorageFactory から FileStream の分岐を外すと、このエラーは出なくなり、ブラウザでも動きました。

手元で回避に使った変更(参考)

FileStream の分岐を、OpenFileAsync だけが通る経路に移しました。

     public static readonly StorageFactory DefaultStorageFactory = (stream, pageSize) =>
     {
-        if (stream is FileStream fs)
-        {
-            return new PreadPageLoader(fs.SafeFileHandle);
-        }
-
         if (stream is MemoryStream ms)
         {
             return new InMemoryPageLoader(ms.ToArray());

+static class FileStorage
+{
+    public static readonly StorageFactory Factory = (stream, pageSize) => stream is FileStream fs
+        ? new PreadPageLoader(fs.SafeFileHandle)
+        : DatabaseLoadOptions.DefaultStorageFactory(stream, pageSize);
+}

     public static async ValueTask<ReadOnlyDatabase> OpenFileAsync(string path, DatabaseLoadOptions? options = null, CancellationToken cancellationToken = default)
     {
         options ??= DatabaseLoadOptions.Default;
+        if (options.StorageFactory == DatabaseLoadOptions.DefaultStorageFactory)
+        {
+            options = options with { StorageFactory = FileStorage.Factory };
+        }

この変更では、OpenAsync(fileStream) で FileStream を直接渡したときの挙動が変わります(PreadPageLoader ではなく NotSupportedException になる)。

dn2cpp はかなり特殊な環境なので、サポートの対象になるかは分かりません。もし差し支えなければ、ファイルを使わないときに FileStream の分岐がバイナリに含まれない形にしていただけると、とても助かります。形はもちろんお任せします。

環境

  • godot-dn2cpp 4.7.2-dn2cpp.14.0(Web、スレッドなし)
  • DryDB 1.1.0。main(6b17592)の DefaultStorageFactory も同じ形です
  • .NET SDK 10.0.403、Windows 11 x64

Activity

  1. hadashiA commented on Nov 8, 2026

    @hadashiA
    Owner

    ご報告ありがとうございます。迷いましたが、ご提案の形の対応を #47 で入れました。

    備考:

    • FileStream は BCL 標準の型なので、C#ライブラリ側としては「標準の型の実装がターゲットに無い」ことを通常は想定していないと思います。
      • 使えない機能はビルドを通したうえで実行時に PlatformNotSupportedException を投げるのが .NET の標準的な流儀だと思うので、本来はランタイム/変換器側で吸収してほしい類の問題だと思いました。
    • ただ、現状のDryDBが、ファイルを一切使わない環境でも PreadPageLoader がビルドに含まれていることは、 dn2cpp に限らず、NativeAOTやUnity IL2CPP環境等でもトリミングの無駄が生じると思われるので、避ける価値があると判断しました。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions