FlatBuffers初识

FlatBuffers初识

概述

FlatBuffers一开始是由Google开发用来支持性能高要求的游戏应用的一个开源的序列化工具库,由于其高效性、跨平台,同时支持多语言开发,而后经常被应用在各种传输性能高要求的互联网、物联网等应用场景。

本文仅为个人理解所作,详细请参考官方文档https://google.github.io/flatbuffers/index.html

为什么使用FlatBuffers

  • 无需解析即可访问经序列化的数据:FlatBuffers是通过在一个扁平的buffer(一维数组)中存储二进制数据,数据无需经解析即可访问;
  • 内存使用高效:访问序列化数据仅需序列化数据时所申请的buffer,经常性与共享内存IPC一起使用实现高效的性能需求;
  • 灵活性:序列化数据"table“结构向前与向后同时兼容;
  • 强类型:错误发生在编译期,无需在运行期通过代码断言;
  • 引用方便:只需要添加一组头文件和一个自定义的生成头文件即可
  • 跨平台

对标ProcotolBuffers、JSON

待学习,后补充

用法概述

  1. 自定义schema文件,定义序列化数据的数据结构(其中包括root对象,具体在schema章节展开);
  2. 通过FlatBuffers编译器flatc根据编译选项将schema文件生成相应开发语言的依赖文件,如C++的--cpp,生成.h头文件;
  3. 使用FlatBufferBuilder类去构造一个二进制buffer,然后引用上一步生成的头文件中的API在buffer中序列化数据对象并存储;
  4. 数据传输;
  5. 接收方通过调用相应API在buffer中获取到root对象的指针,通过类似于object->field()的方式直接访问数据;

编译构建

FlatBuffers官方提供了多种编译构建的方法,此处仅介绍基于Linux平台的CMake编译构建,其余请参考官方文档:

  • cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release
  • make

编译完成,工程根目录下生成flatc可执行文件,即FlatBuffers编译器

定义数据结构

FlatBuffers在scheme文件使用IDL语言(Interface Definition Languange)自定义数据结构,从本节起我将由一个具体的FlatBuffers使用示例展开用法介绍:

schema示例文件:

//jocker.fbs
namespace StudySample;

table Weapon
{
    name : string;
    hurt : short;
}
table Corselet
{
    name : string;
    damage : short =100;
}
union Equiment
{
    weapon : Weapon,
    corselet : Corselet
}
struct Vec3
{
    x : float;
    y : float;
    z : float;
}

table Jocker
{
    name : string;
    hp : short =100;
    friends : bool =true;
    equiment : Equiment;
    weapons : [Weapon];
    pos : Vec3;
    path : [Vec3];
    scope : [short];
}
root_type Jocker;

IDL 语法

  • 定义: 变量 :类型 默认值 (可选字段);
  • root_type指定序列化的根数据对象

数据类型概述

  • 整型
  • 字符串:string
  • 布尔型
  • 结构体
  • 联合体
  • 数组:由方括号[]定义数据类型,固定的内存存储消耗,相比于table内存占用低访问快
  • table:FlatBuffers所提供的同时向前向后兼容的数据结构,可末尾字段增加字段,亦可通过关键字deprecated禁用字段

编译schema文件

以c++为例:./${FLATBUFFERS_DIR}/flatc --cpp ${EXAMPLE_DIR}/jocker.fbs

  • ${FLATBUFFERS_DIR}:FlatBuffers工程目录
  • ${EXAMPLE_DIR}:示例工程目录

编译成功后会在当前目录下生成FlatBuffers序列化数据所依赖的.h头文件

序列化与访问

示例文件:

#include "jocker.h"
using namespace StudySample;

uint8_t* BuildFlatbuffers(void)
{
    flatbuffers::FlatBufferBuilder builder(1024);

    auto sword_name = builder.CreateString("sword");
    auto sword = CreateWeapon(builder, sword_name, 20);
    auto axe_name = builder.CreateString("axe");
    auto axe = CreateWeapon(builder, axe_name, 30);

    auto cuff_name = builder.CreateString("icon-cuff");
    auto cuff = CreateCorselet(builder, cuff_name, 30);

    Vec3 position = {3.0f, 4.0f, 5.0f};

    //Jocker buffer build
    auto name = builder.CreateString("Yiff");
    
    std::vector<flatbuffers::Offset<Weapon>> offset_weapons;
    offset_weapons.push_back(sword);
    offset_weapons.push_back(axe);
    auto weapons = builder.CreateVector(offset_weapons);

    std::vector<Vec3> vec_Vec3 = {Vec3(1.0f, 0.0f, 0.0f), Vec3(1.0f, 2.0f, 0.0f), Vec3(1.0f, 2.0f, 3.0f)};
    auto path = builder.CreateVectorOfStructs(vec_Vec3);

    short scope_array[] = {69, 75, 77};
    auto scope = builder.CreateVector(scope_array, 3); 

    //Method1
    // auto jocker = CreateJocker(builder, name, 100, true, Equiment_weapon, sword.Union(), weapons, &position, path, scope);
    // builder.Finish(jocker);

    //Method2
    JockerBuilder jocker_builder(builder);
    jocker_builder.add_name(name);
    jocker_builder.add_hp(100);
    jocker_builder.add_friends(true);
    jocker_builder.add_equiment_type(Equiment_weapon);
    jocker_builder.add_equiment(sword.Union());
    jocker_builder.add_weapons(weapons);
    jocker_builder.add_pos(&position);
    jocker_builder.add_path(path);
    jocker_builder.add_scope(scope);
    auto jocker = jocker_builder.Finish();
    builder.Finish(jocker);

    return builder.GetBufferPointer();
}


void ParseFlatbuffers(uint8_t* pbuff)
{
    auto jocker = GetJocker(pbuff);
    std::cout << "Jocker name: " << jocker->name()->str() << std::endl;
    std::cout << "Jocker hp: " << jocker->hp() << std::endl;
    std::cout << "Jocker attr(1-friend, 0-enemy): " << jocker->friends() << std::endl;
    std::cout << "Jocker equnimet: " << (static_cast<const Weapon*>(jocker->equiment()))->name()->str() << " and its data: " <<  (static_cast<const Weapon*>(jocker->equiment()))->hurt() << std::endl;
    std::cout << "Jocker position: " << jocker->pos()->x() << "-" << jocker->pos()->y() << "-" << jocker->pos()->z() << std::endl;
    std::cout << "Jocker path position2-y:" << jocker->path()->Get(1)->y() << std::endl;
    std::cout << "Jocker scope2:" << jocker->scope()->Get(1) << std::endl; 
}

上述展示了两种构建buffer的API:Method1与Method2

Object based API (C++)

由FlatBuffers官方针对C++编程语言特点而编写的一套API

  • 编译选项--gen-object-api
  • 效率性能不如原生用法,但是序列化与反序列化适用C++ STL,构建buffer方便

mutable 变量

应用需要进行学习后再补充

实现原理

待了解补充

性能比较

待了解补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值