⑴FlatBuffers是一款非常专业且优秀的跨平台序列化库,旨在最大程度地提高内存效率,适用于多种不同的编程语言,包括C++,C#,C,Java,Python,PHP等,FlatBuffers是一个二进制缓冲区,与大多数内存数据结构不同,FlatBuffers使用严格的对齐和字节顺序规则来确保这些缓冲区是跨平台的,您可以在模式中定义对象类型,也可以将其编译为C++或Java以实现低至零的读写开销,FlatBuffers应用范围非常广,可使用它来序列化游戏数据,也可将其用于客户端与服务器之间的通信,需要的话就赶快下载吧!
⑵编写一个架构文件,该文件可让您定义可能要序列化的数据结构
⑶使用flatc(FlatBuffer编译器生成带有帮助程序类的C ++头文件(或Java / Kotlin / C#/ Go / Python
⑷..类,以访问和构造序列化数据。
⑸使用FlatBufferBuilder该类构造一个平面二进制缓冲区。生成的函数使您可以递归地将对象添加到此缓冲区中,通常就像进行单个函数调用一样简单。
⑹回读时,您可以从二进制缓冲区中获取指向根对象的指针
⑺可将缓冲区存储或发送到某个地方
⑻对于表对象,FlatBuffers提供了向前/向后兼容性以及字段的一般可选性,以支持大多数形式的格式演变。
⑼FlatBuffers还提供“裸”结构,该结构不提供向前/向后兼容性,但可以更小(对于不太可能更改的非常小的对象(例如坐标对或RGBA颜色很有用。
⑽可以将有关格式的大多数信息纳入生成的代码中,从而减少存储数据所需的内存以及访问数据的时间。
⑾在不解析/解包的情况下访问序列化数据
⑿FlatBuffers与众不同之处在于,它在平坦的二进制缓冲区中表示层次结构数据,使得即使不进行解析/解包也可以直接访问分层数据,同时还支持数据结构的演进(forward
⒀访问数据所需的唯一内存是缓冲区的内存。它需要个额外的分配(在C
⒁++中,其他语言可能会有所不同。FlatBuffers也非常适合与mmap(或流一起使用,仅要求将部分缓冲区存储在内存中。访问仅通过一个额外的间接调用(一种vtable即可接近原始结构访问的速度,以允许格式演变和可选字段。它针对那些不希望花费时间和空间(许多内存分配来访问或构造序列化数据的项目,例如在游戏或任何其他对性能敏感的应用程序中。有关详细信息,请参见基准。
⒂可选字段不仅意味着您具有很好的前后兼容性(对于长寿命游戏也越来越重要:不必使用每个新版本更新所有数据!。这也意味着您在写入哪些数据,不写入哪些数据以及如何设计数据结构方面有很多选择。
⒃微小的代码占用空间
⒄生成的代码很少,只有一个小的标头作为最小的依赖关系,非常易于集成。同样,请参阅基准测试部分以了解详细信息。
⒅错误发生在编译时,而不是手动编写重复且容易出错的运行时检查。可以为您生成有用的代码。
⒆++代码允许简洁的访问和构造代码。然后是可选功能,可以在需要时在运行时高效地解析模式和类似JSON的文本表示形式(比其他JSON解析器更快,更高效地使用内存。Java,Kotlin和Go代码支持对象重用。C#具有高效的基于结构的访问器。
⒇无需依赖项的跨平台代码
⒈C ++代码可与任何最新的g / clang和VS一起使用。随附用于测试和示例的构建文件(Android
⒉.mk文件,以及用于所有其他平台的cmake。
⒊编写FlatBuffer模式
⒋要开始使用FlatBuffers,首先需要创建一个schema文件,该文件定义要序列化的每个数据结构的格式。这是schema为我们的定义模板的模板:
⒌如您所见,schema 接口定义语言(IDL的语法与C系列语言和其他IDL语言的语法相似。让我们检查其中的每个部分schema以确定其作用。
⒍该schema带开始namespace申报。这将为生成的代码确定相应的包/名称空间。在我们的示例中,我们在Sample名称空间内部具有MyGame名称空间。
⒎接下来,我们有一个enum定义。在此示例中,我们有一个enum类型为byte,名为Color。我们在这三个值enum:Red,Green,和Blue。我们指定Red
⒏= 和Blue = ,但未指定的显式值Green。由于an的行为enum是在未指定的情况下递增,因此Green将收到的隐式值。
⒐紧随其后的enum是一个union。在union这个例子不是非常有用,因为它仅包含一个table(命名Weapon。如果我们创建了多个表,希望union它们能够被引用,则可以向中添加更多元素union
⒑Equipment。
⒒之后union是struct
⒓Vec,它表示具有尺寸的浮点向量。我们使用了struct这里,过了table,因为structs为理想的,不会改变,因为它们使用更少的内存,并具有更快的查找数据结构。
⒔该Monster表是FlatBuffer中的主要对象。这将用作存储我们的orc怪物的模板。我们为字段指定了一些默认值,例如mana:short =
⒕。如果未指定,则标量字段(如int,uint或float将默认设置为,而字符串和表格将默认设置为null。需要注意的另一件事是线路friendly:bool
⒖(deprecated);。由于您不能从中删除字段table(以支持向后兼容性,因此可以将字段设置为deprecated,这将防止在生成的代码中为此字段生成访问器。deprecated但是,使用时要小心,因为它可能会破坏使用此访问器的旧代码。
⒗该Weapon表是在FlatBuffer中使用的子表。它被使用两次:一次在Monster表中,一次在Equipment联合中。对于我们来说Monster,它用于在我们vector
⒘of tables的weapons字段中填充一个via字段Monster。它也是Equipment工会引用的唯一表。
⒙的最后一部分schema是root_type。根类型声明将是序列化数据的根表。在我们的例子中,根类型是我们的Monster表。
⒚标量类型还可以使用别名类型名称,例如int代替short和float代替float。因此,我们也可以将该Weapon表编写为:
⒛编写FlatBuffers模式后,下一步就是对其进行编译。
①如果您尚未这样做,请按照以下说明来构建flatcFlatBuffer编译器。
②一旦flatc构建成功,请为您的语言编译架构:
③读写Monster FlatBuffers
④现在,我们已经为编程语言编译了架构,我们可以开始创建一些怪物,然后从FlatBuffers对其进行序列化/反序列化。
⑤创建和编写Orc FlatBuffers
⑥第一步是导入/包括库,生成的文件等。
⑦现在我们准备开始构建一些缓冲区。为了开始,我们需要创建一个实例,该实例FlatBufferBuilder将包含缓冲区的增长。您可以传递缓冲区的初始大小(此处为字节,如果需要,该大小将自动增长:
⑧创建完之后builder,我们就可以开始序列化数据了。在制作orc怪物之前,让我们创建一些Weapon:aSword和an Axe。
⑨现在,让我们创建我们的怪物orc。为此orc,让他red发怒,定位于(., .,
⑩.),并给他大量的生命值。我们可以给他一个向量的武器(我们Sword和Axe以前。在这种情况下,我们将为他配备Axe,因为它是两者中功能最强大的。最后,让我们用一些潜在的宝藏来填补他的库存,一旦他被击败,这些宝藏就可以被拿走。
Ⅰ在序列化怪物之前,我们需要首先序列化包含在其中的所有对象,即,我们使用深度优先的预遍历序列化数据树。通常在任何树形结构上都很容易做到这一点。
Ⅱ我们序列化了两个内置数据类型(string和vector,并捕获了它们的返回值。这些值是序列化数据中的偏移量,指示它们的存储位置,以便在向怪物添加字段时可以在下面引用它们。
Ⅲ注意:要创建一个vector嵌套对象(例如tables,strings或other
Ⅳvector,请将其偏移量收集到一个临时数据结构中,然后创建一个vector包含其偏移量的附加对象。
Ⅴ如果不是从一个现有的数组创建向量,而是逐个序列化元素,请注意,这是相反的顺序,因为缓冲区是从头开始构建的。
Ⅵ例如,看一下Weapon我们先前创建的两个(Sword和Axe。它们都是FlatBuffer
Ⅶtable,它们的偏移量现在存储在内存中。因此,我们可以创建一个FlatBuffervector来包含这些偏移量。
Ⅷ请注意,还有其他的便利重载CreateVector,它允许您处理不在a中的数据,std::vector或者允许您通过调用lambda来生成元素。对于的常见情况,std::vector也有CreateVectorOfStrings。
Ⅸ请注意,结构的向量与表的序列化方式不同,因为结构以内联方式存储在向量中。例如,为path上面的字段创建一个向量:
Ⅹ现在我们已经序列化了兽人的非标量组件,因此我们可以序列化怪物本身:
㈠如果您不想在a中设置每个字段table,则可以更方便地手动设置怪物的每个字段,而不是调用CreateMonster()。以下代码段在功能上等同于上面的代码,但提供了更多的灵活性。
㈡在完成序列化之前,让我们快速看一下FlatBuffer union
㈢Equipped。每个FlatBuffer都有两部分union。第一个_type是生成的隐藏字段,用于保存所table引用的类型union。这使您可以在运行时知道要转换为哪种类型。其次是union的数据。
㈣在我们的示例中,我们添加到的最后两件事Monster是Equipped Type和Equipped本身。
㈤这是这些行的重复,以帮助更清楚地突出显示它们:
㈥创建缓冲区后,orc变量中的数据根将具有偏移量,因此可以通过调用适当的finish方法来完成缓冲区。
㈦现在可以准备将缓冲区存储在某个位置,通过网络发送,进行压缩或进行任何其他操作。您可以这样访问缓冲区: