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

    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で生成するやつはあるっぽい

https://github.com/frol/flatc-rust


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は数値計算とも相性が良さそう

 

    パーサコンビネータは人類の英知
广告
将在 10 秒后关闭
bannerAds