Flatbuffers はデータのparse が不要で、非常に読み込み書き込みがはやくて すばらしい。仕事なんかでリアルタイム性の要求が高いゲームサーバでの通信なんかでもつかって。

flatc が生成するコードがつらすぎる。

たとえば以下のような schema で

namespace vfs_schema;

union Index {
  File,
  Files
}

table File {
    id:uint64; // file inode number
    name:string;
    index_at:int64;
}

table Files {
    datas:[File];
}

table Root {
    version:int;
    index:Index;
}

root_type Root;

こいつらにflatc で生成したcode にgo でアクセスする場合.


vRoot := vfs_schema.GetRootAsRoot(buf, 0)

uTable := new(flatbuffers.Table)
vRoot.Index(uTable)
fbsFile := new(vfs_schema.File)
fbsFile.Init(uTable.Bytes, uTable.Pos)
  
fbsFile.Id()
fbsFile.Name()
fbsFile.IndexAt()

Flatbuffers のつらい点

まぁflatc でやるとき問題はいくつかってあって, 主に以下のような感じ

  1. flatc で生成したコードを利用したコードを後でみてもまったくわからない(全部調べ直し)
  2. streaming で流した場合にコンテンツのサイズがわからない。
  3. データの部分的な書き換えが固定長のデータの部分だけで、可変長のものはできない。
  4. データの作成の歳 []byte の領域で後ろから書いていって、そこからだんだん前につめていくのでmemcpy とかが走りまくり。

さすがにこれではつらすぎる。効率が悪いので

fbs-query というものをつくった。

Rust 方面でもやはりflatc コードが糞なのに耐えきれず自分でparser/generater つくってる人はいるぽい

fbs-query をつくってみた。

最終的には1-4を全部解決する予定ですが、まだ 3,4 あたりはこれから解決していく予定

上のようなアクセスコードは以下のような感じで。 これできっと忘れない。

//io  io.Reader

q := query.Open(io)
q.Len()
q.Index().File().Id()
q.Index().File().Name()
q.Index().File().IndexAt()

streaming できた場合とかは Next() を使うと次のメッセージがあつかえる

q := query.Open(io)
n := q.Next()

n.Index().File().Name()

Flatbuffers 的にいうVector データ(slice?) もイテレータ的にアクセスできるようにした。

fbs := query.Open(ioReader)
fbs.Files().First()
fbs.Files().Last()
fbs.Files().Len()
fbs.Files().At(1)
fbs.Files().All()
fbs.Files().Select(func(m query.FbsFile) bool {
    return m.Id() == 10
})
// for streaming data
fbs.Next().Files().First()

Marshal はまだだが Unmarshal はできる

f := struct{
    ID      uint64 `fbs:"Id"`
	Name    []byte `fbs:"Name"`
	IndexAt int64  `fbs:"IndexAt"`
}{}

fbs := query.OpenByBuf(buf)
fbs.Files().First().Unmarshal(&f)

書き込みとかMarshal とかはこれから…

References