FlatBuffers:flatc源碼簡析

FlatBuffers 是Google推出的一個跨平臺、跨語言的序列化和反序列化庫仙畦,主要用于游戲以及對性能要求較高的系統(tǒng)中输涕,例如RPC框架、保存端測推理的模型文件等(如TFLite)慨畸。端測不同于服務(wù)器莱坎,內(nèi)存和算力等資源相對于服務(wù)器十分有限,想要縮短整個推理的時(shí)間和內(nèi)存消耗寸士,模型加載的階段也需要考慮檐什。FlatBuffers可以只使用一塊內(nèi)存進(jìn)行解析,恰好滿足這些要求弱卡。其使用步驟如下:

  1. 下載源碼編譯得到一個編譯該庫指定的IDL(Interface Definition Language)所定義的Schema的編譯器flatc;
  2. 按照IDL的語法編寫Schema乃正;
  3. 使用第一步編譯出的flatc編譯第二步寫出的Schema,得到對應(yīng)語言的序列化和反序列化接口婶博;
  4. 使用第三步得到的接口進(jìn)行序列化和反序列化瓮具。

具體使用方法參考官方文檔即可。一般情況下凡蜻,我們只需要知道FlatBuffers這個庫是怎么使用的就夠了搭综,并不需要知道我們編寫的Schema是如何被編譯生成對應(yīng)語言的接口的。

但是有意思的是划栓,F(xiàn)latBuffers包含了兩個我感興趣的東西:一個是它序列化數(shù)據(jù)的時(shí)候的思想兑巾,之前在FlatBuffer內(nèi)部解析原理簡介一文中有做過總結(jié);另一個就是它的編譯器忠荞。

俗話說麻雀雖小五臟俱全蒋歌,作為一個編譯器,雖然相比于GCC委煤、LLVM等它非常簡單堂油,但是它的代碼中對于詞法分析、語法分析以及代碼生成等都有體現(xiàn)碧绞。

1. 工作流程

flatc的入口位于flatbuffers/src/flatc_main.cpp中府框,其具體工作流程如圖1所示。整個工作流程可以分為三部分:

  1. 解析命令行讥邻、初始化迫靖;
  2. 對源文件進(jìn)行解析院峡,涉及詞法分析和語法分析,這兩個階段是合并在一起的系宜;
  3. 目標(biāo)語言的代碼生成照激。

圖1 flatbuffers workflow sequence

首先,flatc開辟了一個結(jié)構(gòu)體Generator的數(shù)組空間盹牧,該結(jié)構(gòu)體如下所示俩垃。

  struct Generator {
    typedef bool (*GenerateFn)(const flatbuffers::Parser &parser,
                               const std::string &path,
                               const std::string &file_name);
    typedef std::string (*MakeRuleFn)(const flatbuffers::Parser &parser,
                                      const std::string &path,
                                      const std::string &file_name);

    GenerateFn generate;
    const char *generator_opt_short;
    const char *generator_opt_long;
    const char *lang_name;
    bool schema_only;
    GenerateFn generateGRPC;
    flatbuffers::IDLOptions::Language lang;
    const char *generator_help;
    MakeRuleFn make_rule;
  };

后續(xù)通過匹配用戶命令行的參數(shù)選生成哪些語言的API,例如下面的結(jié)構(gòu)體實(shí)例是用于生成C++ API的汰寓,當(dāng)用戶的命令中存在-c或者--cpp口柳,最終就會有C++的API生成。

    { flatbuffers::GenerateCPP, "-c", "--cpp", "C++", true,
      flatbuffers::GenerateCppGRPC, flatbuffers::IDLOptions::kCpp,
      "Generate C++ headers for tables/structs", flatbuffers::CPPMakeRule     
    }

緊接著踩寇,flatc解析命令行參數(shù)啄清,解析完成后便開始編譯。FlatCompiler對源文件進(jìn)行加載俺孙,之后委托Parser進(jìn)行解析辣卒,DoParse()就是整個解析的核心。

源文件解析完成后睛榄,通過查看Generator數(shù)組荣茫,再相應(yīng)的委托BaseGenerator對應(yīng)的子類進(jìn)行代碼生成,例如要生成C++代碼就委托CppGenerator场靴。

2. 詞法分析

詞法分析是每個編譯器進(jìn)行編譯的第一個階段啡莉,詞法分析的目的就是掃描從源碼文件中讀入的字符串,并將它們分成一個一個的Token旨剥,以便后面做語法分析咧欣。
雖然詞法分析和語法分析是編譯過程中的兩個階段,但通常情況下轨帜,它們之間并不是完全獨(dú)立的魄咕。語法分析并不會等待詞法分析將整個源文件都分成一個個Token才開始工作,語法分析會以命令的方式要求詞法分析器提供一個一個的Token蚌父。

在FlatBuffers flatc中哮兰,詞法分析和語法分析的代碼都是在類Parser中完成的,其中Next()方法負(fù)責(zé)詞法分析苟弛,每一次調(diào)用喝滞,它就會從當(dāng)前光標(biāo)開始掃描,然后返回下一個Token膏秫。Parser中有一塊用于存放從文件中讀入的字符串的緩存source_右遭,它是一塊連續(xù)的內(nèi)存區(qū)域,可以看做是一個存放字符的數(shù)組;還有一個光標(biāo)cursor_用于表示當(dāng)前掃描位置狸演。

Parser的語法分析器(其實(shí)也就是一個函數(shù)Parse())通過調(diào)用Next()獲得一個一個的Token進(jìn)行語法分析言蛇。在Next()方法中光標(biāo)cursor_source_上從左向右滑動,并返回一個一個Token宵距。Parse()負(fù)責(zé)分析。例如在下面的例子吨拗,例子所示為一個名為Monster的結(jié)構(gòu)體的定義满哪。

table Monster {
  pos:Vec3;
  mana:short = 150;
  hp:short = 100;
  name:string;
  friendly:bool = false (deprecated, priority: 1);
  inventory:[ubyte];
  color:Color = Blue;
  test:Any;
}

一開始,光標(biāo)位于最開始得字符t劝篷,然后開始滑動哨鸭,直到劃過table這個詞,遇見第一個空格娇妓,根據(jù)規(guī)則像鸡,此時(shí)table被識別成一個Token,因此Next()函數(shù)便將這個Token返回給調(diào)用者Parse()哈恰。Parse()在得到該Token后只估,識別到它是一個關(guān)鍵字,它后面應(yīng)該需要跟上的是一個標(biāo)識符着绷,因此它再次調(diào)用Next()去獲取下一個Token蛔钙,并判斷這個Token是不是所期望的標(biāo)識符。如果得到的并不是一個標(biāo)識符荠医,那么說明語法有誤吁脱,終止編譯并報(bào)錯。如果此時(shí)得到的Token是標(biāo)識符彬向,那么根據(jù)要求兼贡,需要緊接著的是又花括號包含的成員定義,因此Parse()在此調(diào)用Next()去獲取下一個Token娃胆。語法分析器和詞法分析器就是這樣反復(fù)交互遍希,直到整個文件掃描分析結(jié)束或者出錯終止。

這個Next()的邏輯如圖2所示(狀態(tài)圖更合適缕棵,但是奈何手頭沒有適合畫狀態(tài)圖的工具)孵班。

圖2 Parser::Next()

3. 語法分析

通常情況下,一般編譯器的語法分析器會構(gòu)造一顆解析樹招驴,并將這顆解析樹傳遞給后續(xù)的編譯階段進(jìn)行進(jìn)一步處理篙程。但是由于flatc編譯的是接口描述語言,語言本身并不復(fù)雜也不包含計(jì)算别厘,并且最終生成的是其他語言的代碼虱饿,并不是直接運(yùn)行的機(jī)器碼,因此它只需要解析的同時(shí)提取到每個定義的結(jié)構(gòu)的名字、初始值等信息即可氮发。
還是以上面的代碼為例渴肉,當(dāng)解析Monster的時(shí)候,Parser會將Monster的信息保存在一個名叫struct_的數(shù)組中爽冕。后續(xù)讀取此數(shù)組便可以獲取到用戶定義的信息進(jìn)行代碼生成仇祭。

整個解析過程如圖3所示。


圖3 Parser::DoParse()

4. 總結(jié)

看這部分的代碼最大的收獲就是對于如何解析一個文件豁然開朗颈畸,很多需要文本處理的軟件中都有著編譯器前端的部分影子乌奇。甚至是正則表達(dá),其實(shí)仔細(xì)想想眯娱,不就是一個詞法分析器么礁苗?

本文首發(fā)于個人微信公眾號TensorBoy,微信掃描上方二維碼或者微信搜索TensorBoy并關(guān)注徙缴,及時(shí)獲取最新文章试伙。C++ | Python | Linux | 原理 | 源碼,有一起玩耍的么于样?

5. References

[1] http://google.github.io/flatbuffers/index.html
[2] https://github.com/google/flatbuffers

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疏叨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子百宇,更是在濱河造成了極大的恐慌考廉,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件携御,死亡現(xiàn)場離奇詭異昌粤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)啄刹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門涮坐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人誓军,你說我怎么就攤上這事袱讹。” “怎么了昵时?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵捷雕,是天一觀的道長。 經(jīng)常有香客問我壹甥,道長救巷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任句柠,我火速辦了婚禮浦译,結(jié)果婚禮上棒假,老公的妹妹穿的比我還像新娘。我一直安慰自己精盅,他們只是感情好帽哑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著叹俏,像睡著了一般妻枕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上她肯,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天佳头,我揣著相機(jī)與錄音,去河邊找鬼晴氨。 笑死,一個胖子當(dāng)著我的面吹牛碉输,可吹牛的內(nèi)容都是我干的籽前。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼敷钾,長吁一口氣:“原來是場噩夢啊……” “哼枝哄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起阻荒,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤挠锥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后侨赡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蓖租,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年羊壹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蓖宦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡油猫,死狀恐怖稠茂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情情妖,我是刑警寧澤睬关,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站毡证,受9級特大地震影響电爹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜情竹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一藐不、第九天 我趴在偏房一處隱蔽的房頂上張望匀哄。 院中可真熱鬧,春花似錦雏蛮、人聲如沸涎嚼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽法梯。三九已至,卻和暖如春犀概,著一層夾襖步出監(jiān)牢的瞬間立哑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工姻灶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留铛绰,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓产喉,卻偏偏與公主長得像捂掰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子曾沈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355