FlatBuffers介紹

轉自:https://www.jianshu.com/p/6eb04a149cd8

重要補充:

https://www.cnblogs.com/lizhenghn/p/3854244.html 

https://www.coder4.com/archives/4386?utm_source=tuicool&utm_medium=referral               

java型序列化反序列化:

https://www.jianshu.com/p/8df23cd182ec

---------------------

概述

Google在今年6月份發佈了跨平臺序列化工具FlatBuffers,提供了C++/Java/Go/C#接口支持,這是一個注重性能和資源使用的序列化類庫。相較於Protocol Buffers,其更適用於移動設備,FlatBuffers提供更高的性能以及更低的資源需求。

特點

  1. 不需要打包/解包。它的結構化數據都以二進制形式保存,不需要數據解析過程,數據也可以方便傳遞;
  2. 省內存、性能好;
  3. 強類型系統,在編譯階段就能預防一些bug的產生;
  4. 跨平臺(C++11/Java/Go/C#);

FlatBuffers和Protocol Buffers以及Json的比較:

  1. FlatBuffers的功能和Protocol Buffers很像,他們的最大不同點是在使用具體的數據之前,FlatBuffers不需要解析/解包的過程。同時,在工程中使用時,FlatBuffers的引用比Protocol Buffers方便很多,只需要包含兩三個頭文件即可;
  2. JSON作爲數據交換格式,被廣泛用戶各種動態語言之間(當然也包括靜態語言)。它的優點是易於理解(可讀性好),同時它的最大的缺點那就是解析時的性能問題了。而且因爲它的動態類型特點,你的代碼可能還需要多寫好多類型、數據檢查邏輯。

FlatBuffers的使用步驟:

 //flatbuffers test struct

    namespace Jason.Flat.Test;

    enum Color : byte { Red = 1, Green, Blue }

    union Any { TextureData, Texture }

    table TestAppend {
        test_num:int;
        test_num2:int;
    }

    table TextureData {
        image_size:int (id:0);
        image_data:[ubyte] (id:1);
        image_test:short(id:3);
        test_num2:int(id:2);
    }

    table Texture {
        num_textures:short;
        textures:[TextureData];
        num_test:short = 30;
        num_test1:short (deprecated);
            num_test2:short;
        test_append:TestAppend;
    }

    root_type Texture;

 

  1. 編寫一個用來定義數據結構的schema(IDL,接口定義)文件,如上所示;將上述代碼保存爲TestFlat.fbs文件之後,即可用flatc來編譯了;
  2. 使用FlatBuffer編譯器flatc生成數據結構源代碼(C++頭文件或者Java類)登錄 google/flatbuffers ,下載所需版本的源碼及工程文件,在build目錄下有VS2010的工程文件(當然,你也可以選擇利用CMake自己本地創建工程),打開配置好flatc工程的傳入參數,如:-c -o ./ ./TestFlat.fbs        運行flatc工程,即可在當前工程目錄下生成TestFlat_generated.h頭文件,這個頭文件中包含了我們所需的所有結構體、枚舉類型等以及相應的存取方法和驗證方法;
  3. 使用FlatBufferBuilder類創建flat的二進制buffer以下代碼展示瞭如何利用FlatBufferBuilder創建相應buffer:
//read serialized buffer
    flatbuffers::FlatBufferBuilder builder_data;

    int test_append = 300;
    auto name_test = builder_data.CreateString("TestAppend");
    auto testApp = CreateTestAppend(builder_data, test_append, test_append);

    int image_size = 12;
    unsigned char inv_data[] = { 11, 2, 4, 2, 10, 3, 5 ,7, 10, 39, 45, 23 };
    auto name = builder_data.CreateString("TextureData");
    auto image_data = builder_data.CreateVector(inv_data, image_size);

    int image_test = 900;
    auto texture_data = CreateTextureData(builder_data, image_size, image_data, image_test, image_test);

    //flatbuffers::FlatBufferBuilder builder_tex;
    int texture_num = 1;

    auto name_tex = builder_data.CreateString("Texture");

    vector<flatbuffers::Offset<TextureData>> tex_vec;
    tex_vec.push_back(texture_data);
    auto tex_data = builder_data.CreateVector(tex_vec);

    int num_text = 100, num_text2 = 200, num_text3 = 300;
    auto texture = CreateTexture(builder_data, texture_num, tex_data, num_text, num_text2, testApp);
    builder_data.Finish(texture);

要使上述正確運行,除了引用C++基本庫之外,需在文件頭部添加以下代碼:

#include "flatbuffers/flatbuffers.h"
#include "idl.h"
#include "util.h"
#include "TestFlat_generated.h"
using namespace Jason::Flat::Test;

上述代碼的編寫中規中矩,其中CreateString和CreateVector都是FlatBufferBuilder類的成員函數,分別用於創建適用於FlatBuffer內存結構的字符串數據以及向量數據。其餘的方法,如CreateTextureData、CreateTexture均是由flatc根據IDL文件(TestFlat.fbs)自動生成的頭文件中用於創建相應結構體的函數。最後一句builder_data.Finish(texture)用於優化對齊寫入builder_data的內存結構。

        4.保存buffer到本地或者直接通過網絡發送
           保存buffer到本地的代碼,如下: 

std::cout << builder_data.GetSize() << std::endl;
flatbuffers::SaveFile("texture.bin", reinterpret_cast<char *>(builder_data.GetBufferPointer()), builder_data.GetSize(), true);

將數據保存到名爲texture.bin的二進制文件中,其中通過builder_data.GetBufferPointer()獲取內存指針,builder_data.GetSize()獲取內存大小,最後一個參數用於制定是否生成二進制文件。

 

        5.接收並buffer並讀取數據內容
           讀取二進制文件的代碼如下:

string binaryfile;
    bool ok = flatbuffers::LoadFile("texture.bin", false, &binaryfile);

    flatbuffers::Verifier tex_verify(builderOut.GetBufferPointer(), builderOut.GetSize());
    bool verify_flag = VerifyTextureBuffer(tex_verify);

    flatbuffers::FlatBufferBuilder builderOut;
    TextureBuilder* texBuilder = new TextureBuilder(builderOut);
    builderOut.PushBytes(reinterpret_cast<unsigned char*>(const_cast<char *>(binaryfile.c_str())), binaryfile.size());
    std::cout << builderOut.GetSize() << std::endl;

    auto model = GetTexture(builderOut.GetBufferPointer());

    int outNum = model->num_textures();
    const flatbuffers::Vector<flatbuffers::Offset<TextureData>>* outTex = model->textures();
    TextureData* outTexData = (TextureData *)outTex->Get(0);
    int outSize = outTexData->image_size();
    const flatbuffers::Vector<unsigned char>* outData = outTexData->image_data();
    int x = outData->Get(5);
    int len = outData->Length();
    delete texBuilder;

上述代碼中VerifyTextureBuffer用於驗證讀取的內存是否爲FlatBuffers的內存塊,是則返回true,不是則返回false。通過GetTexture獲取指針之後,結構體中的變量均可以通過相應方法(各方法名請查看自動生成的頭文件)獲取。

總結

利用FlatBuffers來進行數據保存及傳輸的優點顯而易見,它利用自身特殊的編碼格式,能一定程度上減少內存的佔用,優化讀取的性能。更重要的是,對於數據結構的向前向後兼容提供了很好的擴展性,方便又高效:

  • 要想讓數據結構具有可擴展性,需將數據結構定義爲table,它是數據擴展的基礎,FlatBuffers中的struct類型不支持擴展;
  • 如果想在後續的版本中刪除數據結構中的某些字段,只要在將要刪除的字段後面添加(deprecated)即可,當然需要保證刪除的字段在之前版本的程序中不會引起程序崩潰(該刪掉的字段在上一版本的程序中獲取到的會是個空指針或空值,只需保證程序在獲取到空值或空指針之後不會出現異常即可);
  • 如果想在後續版本中向數據結構中添加某些字段,需添加到table中最後一個字段的後面,若是想table中隨意位置添加字段,需如上面TextureData 的定義,給每個字段指明添加id:n(n從0開始);

目前FlatBuffers還不是很完善,碰到問題可以到FlatBuffers Issues Tracker去提交或則尋找答案。

參考鏈接

FlatBuffers Documentation

github repository

Google FlatBuffers 跨平臺序列化工具

FlatBuffers與protobuf性能比較

作者:drybeans

鏈接:https://www.jianshu.com/p/6eb04a149cd8

來源:簡書

著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章