
FlatBuffers 官方入门教程从 Schema 编写、flatc 编译到多语言序列化实战【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/gh_mirrors/flat/flatbuffers导读本篇文章完整讲解 FlatBuffers 内存高效序列化库的官方入门教程对应仓库中的 docs/source/Tutorial.md以一个英雄讨伐 Orc 怪物的游戏场景为线索带你走完从编写.fbsSchema、使用flatc编译器、利用 JSON 与二进制互相转换到在 C/Java/Kotlin/C#/Go/Python/TypeScript/Rust/Swift 等语言中序列化与反序列化的全过程。读完本文你将掌握 FlatBuffers 的核心工作流定义 Schema → 生成代码 → 构建写→ 访问读→ 原地修改 → JSON 互转并理解 struct/table/union/vector 的底层设计动机。文中所有命令、Schema 与代码均可对照仓库samples/目录下的真实示例直接运行。教程总览一个怪物的完整生命周期本教程以一个简单示例应用贯穿始终演示 FlatBuffers 的四大核心能力编写 FlatBufferschema即 IDL 文件使用flatcFlatBuffer 编译器生成各语言代码将符合 Schema 的 JSON 文件解析为 FlatBuffer 二进制文件在 C、Java 等多种受支持语言中使用生成的代码完成读写。教程的叙事设定是你正在开发一款游戏主角故事的英雄需要讨伐一些orc兽人。我们将逐步创建这个怪物类型。所有示例代码都位于仓库根目录下的 samples 目录中各语言对应的二进制构建示例文件包括语言示例文件Csample_binary.cppJavaSampleBinary.javaKotlinSampleBinary.ktC#SampleBinary.csGosample_binary.goPythonsample_binary.pyPHPSampleBinary.phpDartexample.dartLuasample_binary.luaLobstersample_binary.lobsterRustsample_binary.rsSwiftsample_binary.swift注意JavaScript 不单独提供二进制示例因为 JS 代码由 TypeScript 转译而来请直接参考 TypeScript 支持而 C 语言需要的是独立项目 FlatCCschema 编译器 C 运行时库注意flatc与flatcc是两套不同的工具。编写怪物的 FlatBuffer Schema使用 FlatBuffers 的第一步是创建schema文件它定义了你想要序列化的每个数据结构的格式。下面是定义我们怪物模板的完整 Schema与仓库中的 samples/monster.fbs 完全一致// Example IDL file for our monsters schema. namespace MyGame.Sample; enum Color:byte { Red 0, Green, Blue 2 } union Equipment { Weapon } // Optionally add more tables. struct Vec3 { x:float; y:float; z:float; } table Monster { pos:Vec3; // Struct. mana:short 150; hp:short 100; name:string; friendly:bool false (deprecated); inventory:[ubyte]; // Vector of scalars. color:Color Blue; // Enum. weapons:[Weapon]; // Vector of tables. equipped:Equipment; // Union. path:[Vec3]; // Vector of structs. } table Weapon { name:string; damage:short; } root_type Monster;Schema 的 接口定义语言IDL 语法与 C 系语言及其他 IDL 语言相似。逐段剖析如下namespace控制生成代码的包/命名空间Schema 以namespace声明开头它决定生成代码对应的包/命名空间。本示例中在MyGame命名空间内部还有一个Sample命名空间namespace MyGame.Sample;。生成后C 中可通过using namespace MyGame::Sample;引入Java/Kotlin 中为import MyGame.Sample.*;。enum枚举定义enum Color:byte { Red 0, Green, Blue 2 }定义了一个底层类型为byte、名为Color的枚举包含三个值。显式指定了Red 0和Blue 2而Green未显式赋值——由于枚举未赋值时自动递增的行为Green会隐式取值为1。你可以在 docs/source/Grammar.md 查看 Schema 语言的正式语法在 docs/source/Schemas.md 查看完整的 Schema 编写指南。union联合体本例中的union Equipment { Weapon }目前只包含一个tableWeapon实用性有限如果想让union能引用多个表只需在其中追加更多元素例如union Equipment { Weapon, Shield, Potion }。struct适合固定不变的数据结构struct Vec3表示一个三维浮点向量。这里选择struct而非table是因为struct非常适合不会变化的数据结构它们占用更少内存、查找更快。struct 是标量的简单组合与标量一样始终以内联inline方式存储。table可扩展的主对象Monster表是 FlatBuffer 中的主对象将作为存储orc怪物的模板。注意其中为部分字段指定了默认值如mana:short 150。默认值规则未显式指定默认值的标量字段如int、uint、float默认为0字符串和表默认为null。特别要注意friendly:bool false (deprecated);这一行为了保持向后兼容table中的字段不能删除但可以标记为deprecated这样生成代码时就不会为这个字段生成访问器accessor。不过使用deprecated要格外小心它可能会破坏仍在使用该访问器的遗留代码。Weapon表是 FlatBuffer 内部使用的子表在本例中被使用了两次一次通过weapons字段填充Monster中的vector of tables表向量另一次被Equipmentunion 引用。root_type声明根表Schema 的最后一部分是root_type Monster;。根类型声明了序列化数据中作为根表的类型。读取二进制时GetRootAsMonster/GetMonster之类的入口就是从根表开始的。标量类型别名标量类型可以使用别名写法例如int16代替short、float32代替float。因此Weapon表也可以写成table Weapon { name:string; damage:int16; }编译怪物的 Schemaflatc 使用入门写完 Schema 后的下一步是编译它。首先需要构建flatcFlatBuffer 编译器完整构建说明见 docs/source/Building.md。核心流程是使用项目根目录的 CMakeLists.txt 生成构建文件例如cmake -G Unix Makefiles -DCMAKE_BUILD_TYPERelease然后按平台常规方式构建得到一个flatc可执行文件可选地运行flattests验证环境。flatc的完整命令行参数说明见 docs/source/Compiler.md。进入samples目录为你的目标语言编译 Schema# 进入示例目录本仓库已位于 flatbuffers 根目录直接进入 samples 即可 cd samples # C ./../flatc --cpp monster.fbs # Java ./../flatc --java monster.fbs # Kotlin ./../flatc --kotlin monster.fbs # C# ./../flatc --csharp monster.fbs # Go ./../flatc --go monster.fbs # Python ./../flatc --python monster.fbs # TypeScript ./../flatc --ts monster.fbs # JavaScript生成 ts/js 模块以及 js bundle monster_generated.js ./../flatc --ts-flat-files --ts monster.fbs # PHP ./../flatc --php monster.fbs # Dart ./../flatc --dart monster.fbs # Lua ./../flatc --lua monster.fbs # Lobster ./../flatc --lobster monster.fbs # Rust ./../flatc --rust monster.fbs # Swift ./../flatc --swift monster.fbs以 C 为例编译会生成monster_generated.h仓库中已有生成结果可对照samples/monster_generated.hRust 生成monster_generated.rsSwift 生成monster_generated.swift等。如果你使用的是 C则需使用独立的 FlatCC 项目bin/flatcc -a -o build/tmp/samples/monster samples/monster/monster.fbs请务必区分flatc与flatcc两个工具。编写序列化怪物 FlatBuffers编译 Schema 之后就可以开始创建怪物并进行序列化/反序列化。本教程采用深度优先、先序pre-order遍历的原则先序列化所有被 Monster 引用的子对象字符串、向量、表最后再序列化 Monster 本身。第一步导入库与生成文件以 C 为例其余语言见下表#include monster_generated.h // This was generated by flatc. using namespace MyGame::Sample; // Specified in the schema.语言导入方式Java/Kotlinimport MyGame.Sample.*;import com.google.flatbuffers.FlatBufferBuilder;C#using Google.FlatBuffers;using MyGame.Sample;Goimport ( flatbuffers github.com/google/flatbuffers/go; sample MyGame/Sample )Pythonimport flatbuffersimport MyGame.Sample.Monster等生成模块TypeScriptimport { MyGame } from ./monster_generated;Rustextern crate flatbuffers;#[path ./monster_generated.rs] mod monster_generated;Swiftimport Flatbuffers并可用typealias Monster MyGame1_Sample_Monster简化第二步创建 FlatBufferBuilder序列化需要一个FlatBufferBuilder实例它持有不断增长的缓冲区。构造时可传入初始大小这里是 1024 字节缓冲区需要时会自动增长flatbuffers::FlatBufferBuilder builder(1024);各语言对应Java/Kotlin/C#new FlatBufferBuilder(1024)、Goflatbuffers.NewBuilder(1024)、Pythonflatbuffers.Builder(1024)、Rustflatbuffers::FlatBufferBuilder::with_capacity(1024)、Dartfb.Builder(initialSize: 1024)等。Dart 中如果你只打算立即取回字节数组还可以直接使用生成 Builder 的toBytes()便捷方法。第三步创建武器子表先创建两件武器Sword攻击 3和Axe攻击 5。C 可以使用CreateWeapon快捷函数一步创建因为所有字段都已设置auto weapon_one_name builder.CreateString(Sword); short weapon_one_damage 3; auto weapon_two_name builder.CreateString(Axe); short weapon_two_damage 5; // Use the CreateWeapon shortcut to create Weapons with all the fields set. auto sword CreateWeapon(builder, weapon_one_name, weapon_one_damage); auto axe CreateWeapon(builder, weapon_two_name, weapon_two_damage);若字段较多且不想全部设置也可以使用Start/Add/End三段式Go、Python、JavaScript、TypeScript、Lua 等语言基本都采用这种形式weaponOne : builder.CreateString(Sword) sample.WeaponStart(builder) sample.WeaponAddName(builder, weaponOne) sample.WeaponAddDamage(builder, 3) sword : sample.WeaponEnd(builder)Rust 使用带命名字段的WeaponArgslet sword Weapon::create(mut builder, WeaponArgs{ name: Some(weapon_one_name), damage: 3, });第四步序列化字符串与标量向量为怪物序列化名字Orc并创建一个长度为 10 的ubyte向量作为战利品清单击败 Orc 后可领取的物品编号auto name builder.CreateString(Orc); unsigned char treasure[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; auto inventory builder.CreateVector(treasure, 10);字符串和向量的返回值都是序列化数据中的偏移量offset标明它们存储的位置以便下面给怪物添加字段时引用它们。关键陷阱——反向构建如果不用现有数组、而是逐个元素序列化C#、Go、Python、Lua 的StartXxxVectorPrependByte循环方式必须注意按逆序进行因为缓冲区是从后往前构建的。以 C# 为例Monster.StartInventoryVector(builder, 10); for (int i 9; i 0; i--) { builder.AddByte((byte)i); } var inv builder.EndVector();第五步创建表向量vector of tables之前创建的Sword和Axe都是 FlatBuffertable其偏移量暂存在内存中。若要创建一个包含这些偏移量的 FlatBuffer 向量需要先把它们收集进临时数据结构再一次性创建向量。C 借助std::vectorflatbuffers::OffsetWeaponstd::vectorflatbuffers::OffsetWeapon weapons_vector; weapons_vector.push_back(sword); weapons_vector.push_back(axe); auto weapons builder.CreateVector(weapons_vector);C 的CreateVector还有额外重载可处理非std::vector数据或通过 lambda 生成元素对于常见的std::vectorstd::string还有专门的CreateVectorOfStrings。用StartWeaponsVector/PrependUOffsetT/EndVector手动逐个前插Go/Python/Lua时同样要逆序push。第六步创建结构体向量vector of structs注意struct 向量与 table 向量的序列化方式不同——struct 直接内联存储在向量中。例如为path字段创建两个Vec3Vec3 points[] { Vec3(1.0f, 2.0f, 3.0f), Vec3(4.0f, 5.0f, 6.0f) }; auto path builder.CreateVectorOfStructs(points, 2);第七步序列化 Monster 本体所有非标量部件就绪后即可创建位置为(1.0, 2.0, 3.0)、红色、hp300、mana150的 Orc装备两件武器中最强的Axe并设置路径// Create the position struct auto position Vec3(1.0f, 2.0f, 3.0f); int hp 300; int mana 150; // Finally, create the monster using the CreateMonster helper function // to set all fields. auto orc CreateMonster(builder, position, mana, hp, name, inventory, Color_Red, weapons, Equipment_Weapon, axe.Union(), path);如果不想设置表中的每个字段也可以逐字段手动构建MonsterBuilder方式功能等价且更灵活MonsterBuilder monster_builder(builder); monster_builder.add_pos(position); monster_builder.add_hp(hp); monster_builder.add_name(name); monster_builder.add_inventory(inventory); monster_builder.add_color(Color_Red); monster_builder.add_weapons(weapons); monster_builder.add_equipped_type(Equipment_Weapon); monster_builder.add_equipped(axe.Union()); auto orc monster_builder.Finish();重要约束与 struct 不同不应在start与end之间嵌套创建表或其他对象——所以本怪物引用的所有字符串/向量/表都必须在start之前创建完毕。如果你试图在start与end之间创建它们会触发断言/异常/panic取决于语言。空间优化原理由于我们传入的mana150恰好等于默认值该字段实际上不会写入缓冲区——因为查询时默认值同样会被返回。这是一项很好的空间节省机制如果数据中默认值常见缓冲区会显著变小也不必担心为少量实例才使用的字段增加负担未使用的字段不会撑大缓冲区。第八步理解 union 的双字段结构FlatBufferunion在序列化时由两部分组成第一部分是隐藏字段_type保存 union 所引用table的类型便于运行时知道该转换成什么类型第二部分是 union 的数据本身。所以添加Equipped时通常连续调用两个 Addmonster_builder.add_equipped_type(Equipment_Weapon); // Union type monster_builder.add_equipped(axe.Union()); // Union data第九步Finish 收尾并取回字节创建完缓冲区后orc变量持有数据根部的偏移量调用Finish()完成缓冲区builder.Finish(orc); // You could also call FinishMonsterBuffer(builder, orc);完成后的缓冲区即可存储、压缩或网络传输。取回字节// This must be called after Finish(). uint8_t *buf builder.GetBufferPointer(); int size builder.GetSize(); // Returns the size of the buffer that // GetBufferPointer() points to.各语言取字节的方式Javabuilder.sizedByteArray()或dataBuffer()注意数据不始于 0而始于buf.position()长度为buf.remaining()Gobuilder.FinishedBytes()Pythonbuilder.Output()TypeScript/JavaScriptbuilder.asUint8Array()Rustbuilder.finished_data()返回[u8]Swiftbuilder.sizedByteArrayDart 的finish直接返回Uint8List。务必使用二进制模式将字节写入文件或网络时文件模式或传输协议必须设为 BINARY 而非文本。如果以文本模式传输 FlatBuffer缓冲区会被破坏导致读取时出现极难排查的问题。例如 Node 中writeFileSync(monster.bin, buf, binary);读取反序列化怪物 FlatBuffers创建成功后数据可以被保存、传输。现在反向操作访问 FlatBuffer。读取需要与写入相同的 import/include 与命名空间设置且必须以二进制模式读取字节。获取根对象访问器各语言获取根对象的方式注意 Go/Python 中传入偏移量0的约定见下方注释// C uint8_t *buffer_pointer /* the data you just read */; auto monster GetMonster(buffer_pointer); // monster is of type Monster *. // Note: root object pointers are NOT the same as buffer_pointer. // GetMonster is a convenience function that calls GetRootMonster.// Java byte[] bytes /* the data you just read */; java.nio.ByteBuffer buf java.nio.ByteBuffer.wrap(bytes); Monster monster Monster.getRootAsMonster(buf);// Go var buf []byte /* the data you just read */; monster : sample.GetRootAsMonster(buf, 0) // Note: We use 0 for the offset here, which is typical for most buffers. // If reading from builder.Bytes directly, you would need to pass the offset // of builder.Head(), as the builder constructs the buffer backwards.# Python monster MyGame.Sample.Monster.Monster.GetRootAs(buf, 0)// Rust let monster root_as_monster(buf).unwrap();读取标量字段查看生成的代码会发现所有非deprecated字段都有访问器。C 访问hp、mana、nameauto hp monster-hp(); auto mana monster-mana(); auto name monster-name()-c_str();这些应分别得到300、150和Orc。注意默认值150并未真正存储于mana字段中但我们依然能读出它——这正是默认值机制的体现。语言风格差异C# 与 Dart 中大部分值以属性property而非访问器方法暴露如monster.Hp、monster.hpGo 中monster.Name()返回byte[]需要转字符串Swift 的name返回可选字符串。读取结构体子对象访问posVec3结构体auto pos monster-pos(); auto x pos-x(); auto y pos-y(); auto z pos-z();x、y、z分别为1.0、2.0、3.0。若序列化时未设置pos此处将是null。Go 语言有个性能提示每次访问新对象如Pos()都会创建新的临时访问器对象若代码对性能敏感可以传入一个已存在的Vec3指针而非nil以复用对象、减少分配和 GC 压力。读取标量向量通过索引访问inventory向量元素也可以遍历整个向量auto inv monster-inventory(); // A pointer to a flatbuffers::Vector. auto inv_len inv-size(); auto third_item inv-Get(2);Lua 中注意索引是 1 基的mon:Inventory(3)才是第三个元素。读取表向量weapons是vector of tables访问方式与普通向量类似只是每个元素需要按 FlatBuffertable处理auto weapons monster-weapons(); // A pointer to a flatbuffers::Vector. auto weapon_len weapons-size(); auto second_weapon_name weapons-Get(1)-name()-str(); auto second_weapon_damage weapons-Get(1)-damage();读取 union访问Equippedunion 时需要同时取得两个部分类型与数据。先读类型再按类型动态转换数据auto union_type monster-equipped_type(); if (union_type Equipment_Weapon) { auto weapon static_castconst Weapon*(monster-equipped()); // Requires static_cast auto weapon_name weapon-name()-str(); // Axe auto weapon_damage weapon-damage(); // 5 }Java 需要显式向下转型(Weapon)monster.equipped(new Weapon())Go/Python 中equipped()返回flatbuffers.Table需要用它初始化一个Weapon访问器对象Rust 提供类型安全的monster.equipped_as_weapon()若不是该类型则返回NoneLobster 也提供monster.equipped_as_Weapon。原地修改 FlatBuffers--gen-mutable通常 FlatBuffer 一旦创建即为只读。但有时你刚收到一个缓冲区、希望在转发给下一个接收者之前修改其中的某些内容——若按常规方式你需要重新生成整个 FlatBuffer 并自行追踪修改。FlatBuffers 为此支持原地修改mutation。虽然它很适合对已有缓冲区做小幅修补但只要可能仍应尽量从头创建缓冲区因为那样更高效、API 更通用。要获得非 const 访问器调用flatc时需要加--gen-mutable参数该选项在 src/flatc.cpp 中解析。然后即可使用mutate_*风格的方法C 使用GetMutableMonsterauto monster GetMutableMonster(buffer_pointer); // non-const monster-mutate_hp(10); // Set the table hp field. monster-mutable_pos()-mutate_z(4); // Set struct field. monster-mutable_inventory()-Mutate(0, 1); // Set vector element.Java/Kotlin/C# 对应为mutateHp(10)、pos().mutateZ(4)、mutateInventory(0, 1)。语言支持现状以本仓库文档为准Go、Python、Dart、Lua、Lobster、Rust 目前尚无修改 APIJavaScript/TypeScript/PHP 暂不支持C 语言不支持原地修改但支持原地向量排序。为什么叫mutate而不是set这是为了强调它是特殊用途避免与常规的 FlatBuffer 构建方式混淆。有两个关键行为需要了解mutate函数返回布尔值如果目标字段不在缓冲区中返回false。字段不在缓冲区有两种情况从未设置过或恰好等于默认值。例如前面创建代码中mana150恰为默认值从未写入缓冲区对此数据调用mana的mutate会返回false值也不会被真正修改ForceDefaults强制写入解决办法之一是在FlatBufferBuilder上调用ForceDefaults强制所有设置过的字段都真正写入缓冲区。这当然会略微增大缓冲区但对于可变缓冲区而言可能是可接受的代价。如果这仍不满足需求部分语言还支持通过基于对象的 API--gen-object-api或反射机制来修改 FlatBuffers详见各语言文档。把 flatc 当作 JSON 转换工具C、C、Lobster 可以在运行时直接解析 JSON。如果你的语言当前不支持 JSON 运行时解析flatc提供了替代方案。使用flatc通常是更受青睐的做法无需向程序添加任何新代码且高效——因为你可以直接发布二进制数据。缺点是用户/开发者需要多执行一步不过可以自动化到编译流程中。JSON → 二进制假设有一个描述怪物的 JSON 文件即仓库中的 samples/monsterdata.json{ pos: { x: 1.0, y: 2.0, z: 3.0 }, hp: 300, name: Orc, weapons: [ { name: axe, damage: 100 }, { name: bow, damage: 90 } ], equipped_type: Weapon, equipped: { name: bow, damage: 90 } }用flatc加-b标志、配合monster.fbsSchema 即可把它转换成 FlatBuffer 二进制文件./../flatc --binary monster.fbs monsterdata.json输出文件为monsterdata.bin包含 JSON 内容的 FlatBuffer 二进制表示。C 还可在运行时解析 JSON详见 docs/source/CppUsage.mdLobster 同理见 docs/source/LobsterUsage.md。C 中flatcc --json会生成 Schema 专用的高性能 JSON 解析器/打印器flatc则仍适合通用离线 JSON↔FlatBuffer 转换。二进制 → JSON反向转换同样支持./../flatc --json --raw-binary monster.fbs -- monsterdata.bin该命令把monsterdata.bin转回 JSON 表示。需要传入对应的 FlatBuffers Schema让flatc知道如何解释二进制缓冲区。由于monster.fbs没有为二进制缓冲区指定显式的file_identifierflatc必须用--raw-binary强制读取该.bin文件src/flatc.cpp 中对该场景有专门提示。三个重要选项--defaults-jsonFlatBuffer 二进制表示不显式编码默认值因此默认值不会出现在结果 JSON 中除非指定该选项--strict-json如果打算用其他工具处理 JSON建议开启使标识符被正确加引号浮点精度生成的 JSON 未必与原 JSON 完全一致——若二进制含浮点数float 和 double 会分别四舍五入到 6 位与 12 位小数以便用十进制表示。高级特性向量中的 UnionVector of Unions部分语言支持直接在向量中存储 union。相关 Schema 位于 tests/union_vector/union_vector.fbsnamespace Example.VectorOfUnions; table Attacker { sword_attack_damage: int; } struct Rapunzel { hair_length: int; } struct BookReader { books_read: int; } union Character { MuLan: Attacker, // Can have name be different from type. Rapunzel, // Or just both the same, as before. Belle: BookReader, BookFan: BookReader, Other: string, Unused: string } table Movie { main_character: Character; characters: [Character]; }创建正如单个 union 会为 table 增加两个字段向量中的 union 会创建两个向量——一个存 union 数据一个存数据类型。以 C# 为例using Google.FlatBuffers; using Example.VectorOfUnions; var fbb new FlatBufferBuilder(100); var characterTypes new[] { Character.MuLan, Character.Belle, Character.Other, }; var characterTypesOffset Movie.CreateCharactersTypeVector(fbb, characterTypes); var characters new[] { Attacker.CreateAttacker(fbb, 10).Value, BookReader.CreateBookReader(fbb, 20).Value, fbb.CreateSharedString(Chip).Value, }; var charactersOffset Movie.CreateCharactersVector(fbb, characters); var movieOffset Movie.CreateMovie( fbb, Character.Rapunzel, rapunzel, characterTypesOffset, charactersOffset); Movie.FinishMovieBuffer(fbb, movieOffset);读取按索引遍历通过类型判断后分别转换var movie Movie.GetRootAsMovie(fbb.DataBuffer); for (var i 0; i movie.CharactersLength; i) { if (movie.CharactersType(i) Character.MuLan) { var mulanSwordDamage movie.CharactersAttacker(i).Value.SwordAttackDamage; } else if (movie.CharactersType(i) Character.Belle) { var belleBooksRead movie.CharactersBookReader(i).Value.BooksRead; } else if (movie.CharactersType(i) Character.Other) { var otherStr movie.CharactersAsString(i); } }C、Java、Kotlin、TypeScript、PHP、Swift 也支持向量中的 union但当前文档未展开说明。各语言深入阅读每种语言在 Programmers Guide 中都有专门的 Use in XXX 页面介绍该语言下 FlatBuffers 的细微差别对应仓库文档如下Cdocs/source/CppUsage.mdJavadocs/source/JavaUsage.mdKotlindocs/source/KotlinUsage.mdC#docs/source/CsharpUsage.mdGodocs/source/GoUsage.mdPythondocs/source/PythonUsage.mdJavaScriptdocs/source/JavaScriptUsage.mdTypeScriptdocs/source/TypeScriptUsage.mdPHPdocs/source/PHPUsage.mdDartdocs/source/DartUsage.mdLuadocs/source/LuaUsage.mdLobsterdocs/source/LobsterUsage.mdRustdocs/source/RustUsage.mdSwiftdocs/source/SwiftUsage.md如需进一步了解 Schema 语法与 flatc 全量参数可继续阅读 docs/source/Grammar.md、docs/source/Schemas.md 与 docs/source/Compiler.md。完整的可运行 C 示例含构建与断言验证见 samples/sample_binary.cpp。【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/gh_mirrors/flat/flatbuffers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考