LoginSignup
27
11

More than 5 years have passed since last update.

RustでFlatBuffersのコンパイラを作り始めた話

Last updated at Posted at 2019-04-24
1 / 11

Who am I?

  • @termoshtt (Twitter/GitHub/Qiita)
  • Rustで数値計算するためのOSSとか作ってる
    • accel (GPGPU for Rust)
    • rust-numpy
    • ndarray-linalg (LAPACK)
  • rust-math
    • intel-mkl-src / rust-fftw3 / rust-sfmt

ぐだフェリス.png


FlatBuffersとは?

  • Cross-Platform serialization library
  • Serialize data without parsing/unpacking
  • Strongly Typed
  • Protocol Buffersのようにスキーマから各言語のコードを生成
    • 仮想テーブルを使うことであとから拡張可能に
  • Googleが2014年にOSS化 (Apache license)

Who uses FlatBuffers?

  • cocos2d-x (2D Game framework)
  • Facebook (for client-server communication in their Android app)
  • Apache Arrow (cross-language development platform for in-memory data)
  • TensorFlow Lite

FlatBuffers schema

electric.fbs
    namespace Eclectic;

    enum Fruit : byte { Banana = -1, Orange = 42 }
    table FooBar {
        meal      : Fruit = Banana;
        density   : long (deprecated);
        say       : string;
        height    : short;
    }
    file_identifier "NOOB";
    root_type FooBar;

flatccのサンプルより(後述)


公式でRustサポートしてないの?

  • FlatBuffers 1.10 (2018/10)からRust/Dart/Lua/Lobsterがサポート
  • 公式のコンパイラflatc (C++実装) からRustのコードが出力できる
  • Cには別実装がある
    • flatcc: FlatBuffers Compiler and Library in C for C
  • 今回はFlatBuffersの構成の理解とPure Rust実装が欲しかったので作ってみることに
    • 外部コマンドに依存するのは面倒

没案:flatc-gen

Protocol Buffersみたいにflatcの実行もコンパイル時にやってしまいたい

use flatc_gen::flatc_gen;
flatc_gen!("../fbs/addressbook.fbs");
  • proc-macroで実行すれば良いのでは!?
  • コンパイル時にflatcをGitHubからダウンロードしてコンパイルしてRustのコードを生成してマクロとして展開する
    • flatc-gen: Procedural macro for FlatBuffers compiler
    • proc-macro中で相対パスを取れないのであきらめる
  • build.rsで生成するやつはあるっぽい

rflatc

ゼロからコンパイラ作ることに
https://github.com/termoshtt/rflatc

  • rflatc
    • combine3でパーサーを作成
    • コード生成は実装中...
  • fbs: rflatc Runtime
    • バイナリのパーサーは完了
    • バイナリのビルダーは未着手...

FlatBuffers Binary format

image.png

  • データのパースとビルダーはfbsが担当
  • rflatcは*.fbsファイルからVTableの正しい位置にアクセスするためのコードを生成
  • std::allocが(部分的に)安定化されてるので32bit-alignedなバッファーも確保できる
#[repr(C, align(32))]
#[derive(Debug)]
struct Table {
    vtable_offset: i32,
    data: [u8],
}

#[repr(C, align(16))]
#[derive(Debug)]
struct VTable {
    vtable_length: u16,
    table_length: u16,
    offsets: [u16],
}

combine3:パーサコンビネーター

/// enum_decl = ( enum ident [ : type ] | union ident ) metadata { commasep( enumval_decl ) }
fn enum_<I>() -> impl Parser<Input = I, Output = Stmt>
where
    I: Stream<Item = char>,
    I::Error: ParseError<I::Item, I::Range, I::Position>,
{
    string("enum")
        .skip(spaces())
        .and(identifier())
        .skip(spaces())
        .and(optional(token(':').skip(spaces()).and(ty()).map(|x| x.1)))
        .skip(spaces())
        .and(paren(sep_by1(enumval(), token(',').skip(spaces()))))
        .skip(spaces())
        .map(|(((_, id), ty), values)| Stmt::Enum(Enum { id, ty, values }))
}

まとめ・感想

  • RustでFlatBuffersのコンパイラ作り始めて、バイナリパースまでは出来た
  • FlatBuffersは数値計算とも相性が良さそう
  • パーサコンビネータは人類の英知
27
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
11