| # Tutorial | |
| This tutorial provides an example of how to work with FlatBuffers in a variety | |
| of languages. The following topics will cover all the steps of using FlatBuffers | |
| in your application. | |
| 1. Writing a FlatBuffers schema file (`.fbs`). | |
| 2. Using the `flatc` compiler to transform the schema into language-specific | |
| code. | |
| 3. Importing the generated code and libraries into your application. | |
| 4. Serializing data into a flatbuffer. | |
| 5. Deserializing a flatbuffer. | |
| The tutorial is structured to be language agnostic, with language specifics in | |
| code blocks providing more context. Additionally, this tries to cover the major | |
| parts and type system of flatbuffers to give a general overview. It's not | |
| expected to be an exhaustive list of all features, or provide the best way to do | |
| things. | |
| ## FlatBuffers Schema (`.fbs`) | |
| To start working with FlatBuffers, you first need to create a | |
| [schema](schema.md) file which defines the format of the data structures you | |
| wish to serialize. The schema is processed by the `flatc` compiler to generate | |
| language-specific code that you use in your projects. | |
| The following | |
| [`monster.fbs`](https://github.com/google/flatbuffers/blob/master/samples/monster.fbs) | |
| schema will be used for this tutorial. This is part of the FlatBuffers | |
| [sample code](https://github.com/google/flatbuffers/tree/master/samples) to give | |
| complete sample binaries demonstrations. | |
| FlatBuffers schema is a Interface Definition Language (IDL) that has a couple | |
| data structures, see the [schema](schema.md) documentation for a detail | |
| description. Use the inline code annotations to get a brief synopsis of each | |
| part of the schema. | |
| ```proto title="monster.fbs" linenums="1" | |
| // Example IDL file for our monster's schema. | |
| namespace MyGame.Sample; //(1)! | |
| enum Color:byte { Red = 0, Green, Blue = 2 } //(2)! | |
| // Optionally add more tables. | |
| union Equipment { Weapon } //(3)! | |
| struct Vec3 { //(4)! | |
| x:float; //(5)! | |
| y:float; | |
| z:float; | |
| } | |
| table Monster { //(6)! | |
| pos:Vec3; //(7)! | |
| mana:short = 150; //(8)! | |
| hp:short = 100; | |
| name:string; //(9)! | |
| friendly:bool = false (deprecated); //(10)! | |
| inventory:[ubyte]; //(11)! | |
| color:Color = Blue; | |
| weapons:[Weapon]; //(12)! | |
| equipped:Equipment; //(13)! | |
| path:[Vec3]; | |
| } | |
| table Weapon { | |
| name:string; | |
| damage:short; | |
| } | |
| root_type Monster; //(14)! | |
| ``` | |
| 1. FlatBuffers has support for namespaces to place the generated code into. | |
| There is mixed level of support for namespaces (some languages don't have | |
| namespaces), but for the C family of languages, it is fully supported. | |
| 2. Enums definitions can be defined with the backing numerical type. Implicit | |
| numbering is supported, so that `Green` would have a value of 1. | |
| 3. A union represents a single value from a set of possible values. Its | |
| effectively an enum (to represent the type actually store) and a value, | |
| combined into one. In this example, the union is not very useful, since it | |
| only has a single type. | |
| 4. A struct is a collection of scalar fields with names. It is itself a scalar | |
| type, which uses less memory and has faster lookup. However, once a struct is | |
| defined, it cannot be changed. Use tables for data structures that can evolve | |
| over time. | |
| 5. FlatBuffers has the standard set of scalar numerical types (`int8`, `int16`, | |
| `int32`, `int64`, `uint8`, `uint16`, `uint32`, `uint64`, `float`, `double`), | |
| as well as `bool`. Note, scalars are fixed width, `varints` are not | |
| supported. | |
| 6. Tables are the main data structure for grouping data together. It can evolve | |
| by adding and deprecating fields over time, while preserving forward and | |
| backwards compatibility. | |
| 7. A field that happens to be a `struct`. This means the data of the `Vec3` | |
| struct will be serialized inline in the table without any need for offset. | |
| 8. Fields can be provided a default value. Default values can be configured to | |
| not be serialized at all while still providing the default value while | |
| deserializing. However, once set, a default value cannot be changed. | |
| 9. A `string` field which points to a serialized string external to the table. | |
| 10. A deprecated field that is no longer being used. This is used instead of | |
| removing the field outright. | |
| 11. A `vector` field that points to a vector of bytes. Like `strings`, the | |
| vector data is serialized elsewhere and this field just stores an offset to | |
| the vector. | |
| 12. Vector of `tables` and `structs` are also possible. | |
| 13. A field to a `union` type. | |
| 14. The root of the flatbuffer is always a `table`. This indicates the type of | |
| `table` the "entry" point of the flatbuffer will point to. | |
| ## Compiling Schema to Code (`flatc`) | |
| After a schema file is written, you compile it to code in the languages you wish | |
| to work with. This compilation is done by the [FlatBuffers Compiler](flatc.md) | |
| (`flatc`) which is one of the binaries built in the repo. | |
| ### Building `flatc` | |
| FlatBuffers uses [`cmake`](https://cmake.org/) to build projects files for your | |
| environment. | |
| === "Unix" | |
| ```sh | |
| cmake -G "Unix Makefiles" | |
| make flatc | |
| ``` | |
| === "Windows" | |
| ```sh | |
| cmake -G "Visual Studio 17 2022" | |
| msbuild.exe FlatBuffers.sln | |
| ``` | |
| See the documentation on [building](building.md) for more details and other | |
| environments. Some languages also include a prebuilt `flatc` via their package | |
| manager. | |
| ### Compiling Schema | |
| To compile the schema, invoke `flatc` with the schema file and the language | |
| flags you wish to generate code for. This compilation will generate files that | |
| you include in your application code. These files provide convenient APIs for | |
| serializing and deserializing the flatbuffer binary data. | |
| === "C++" | |
| ```sh | |
| flatc --cpp monster.fbs | |
| ``` | |
| === "C" | |
| !!! Note | |
| If you're working in C, you need to use the separate project | |
| [FlatCC](https://github.com/dvidelabs/flatcc) which contains a schema | |
| compiler and runtime library in C for C. See | |
| [flatcc build instructions](https://github.com/dvidelabs/flatcc#building). | |
| Please be aware of the difference between `flatc` and `flatcc` tools. | |
| ```sh | |
| cd flatcc | |
| mkdir -p build/tmp/samples/monster | |
| bin/flatcc -a -o build/tmp/samples/monster samples/monster/monster.fbs | |
| # or just | |
| flatcc/samples/monster/build.sh | |
| ``` | |
| === "C#" | |
| ```sh | |
| flatc --csharp monster.fbs | |
| ``` | |
| === "Dart" | |
| ```sh | |
| flatc --dart monster.fbs | |
| ``` | |
| === "Go" | |
| ```sh | |
| flatc --go monster.fbs | |
| ``` | |
| === "Java" | |
| ```sh | |
| flatc --java monster.fbs | |
| ``` | |
| === "JavaScript" | |
| ```sh | |
| flatc --js monster.fbs | |
| ``` | |
| === "Kotlin" | |
| ```sh | |
| flatc --kotlin monster.fbs | |
| ``` | |
| === "Lobster" | |
| ```sh | |
| flatc --lobster monster.fbs | |
| ``` | |
| === "Lua" | |
| ```sh | |
| flatc --lua monster.fbs | |
| ``` | |
| === "PHP" | |
| ```sh | |
| flatc --php monster.fbs | |
| ``` | |
| === "Python" | |
| ```sh | |
| flatc --python monster.fbs | |
| ``` | |
| === "Rust" | |
| ```sh | |
| flatc --rust monster.fbs | |
| ``` | |
| === "Swift" | |
| ```sh | |
| flatc --swift monster.fbs | |
| ``` | |
| === "TypeScript" | |
| ```sh | |
| flatc --ts monster.fbs | |
| ``` | |
| You can deserialize flatbuffers in languages that differ from the language that | |
| serialized it. For purpose of this tutorial, we assume one language is used for | |
| both serializing and deserializing. | |
| ## Application Integration | |
| The generated files are then included in your project to be built into your | |
| application. This is heavily dependent on your build system and language, but | |
| generally involves two things: | |
| 1. Importing the generated code. | |
| 2. Importing the "runtime" libraries. | |
| === "C++" | |
| ```c++ | |
| #include "monster_generated.h" // This was generated by `flatc` | |
| #include "flatbuffers.h" // The runtime library for C++ | |
| // Simplifies naming in the following examples. | |
| using namespace MyGame::Sample; // Specified in the schema. | |
| ``` | |
| === "C" | |
| ```c | |
| #include "monster_builder.h" // Generated by `flatcc`. | |
| // Convenient namespace macro to manage long namespace prefix. | |
| #undef ns | |
| // Specified in the schema. | |
| #define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) | |
| // A helper to simplify creating vectors from C-arrays. | |
| #define c_vec_len(V) (sizeof(V)/sizeof((V)[0])) | |
| ``` | |
| === "C#" | |
| ```c# | |
| using Google.FlatBuffers; // The runtime library for C# | |
| using MyGame.Sample; // The generated files from `flatc` | |
| ``` | |
| === "Dart" | |
| ```dart | |
| import 'package:flat_buffers/flat_buffers.dart' as fb; | |
| // Generated by `flatc`. | |
| import 'monster_my_game.sample_generated.dart' as myGame; | |
| ``` | |
| === "Go" | |
| ```go | |
| import ( | |
| flatbuffers "github.com/google/flatbuffers/go" | |
| sample "MyGame/Sample" | |
| ) | |
| ``` | |
| === "Java" | |
| ```java | |
| import MyGame.Sample.*; //The `flatc` generated files. (Monster, Vec3, etc.) | |
| import com.google.flatbuffers.FlatBufferBuilder; | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| // The following code is an example - use your desired module flavor by | |
| // transpiling from TS. | |
| var flatbuffers = require('/js/flatbuffers').flatbuffers; | |
| var MyGame = require('./monster_generated').MyGame; // Generated by `flatc`. | |
| //------------------------------------------------------------------------// | |
| // The following code is for browser-based HTML/JavaScript. Use the above | |
| // code for JavaScript module loaders (e.g. Node.js). | |
| <script src="../js/flatbuffers.js"></script> | |
| <script src="monster_generated.js"></script> // Generated by `flatc`. | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| import MyGame.Sample.* //The `flatc` generated files. (Monster, Vec3, etc.) | |
| import com.google.flatbuffers.FlatBufferBuilder | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| import from "../lobster/" // Where to find flatbuffers.lobster | |
| import monster_generated | |
| ``` | |
| === "Lua" | |
| ```lua | |
| -- require the flatbuffers module | |
| local flatbuffers = require("flatbuffers") | |
| -- require the generated files from `flatc`. | |
| local color = require("MyGame.Sample.Color") | |
| local equipment = require("MyGame.Sample.Equipment") | |
| local monster = require("MyGame.Sample.Monster") | |
| local vec3 = require("MyGame.Sample.Vec3") | |
| local weapon = require("MyGame.Sample.Weapon") | |
| ``` | |
| === "PHP" | |
| ```php | |
| // It is recommended that your use PSR autoload when using FlatBuffers in | |
| // PHP. Here is an example from `SampleBinary.php`: | |
| function __autoload($class_name) { | |
| // The last segment of the class name matches the file name. | |
| $class = substr($class_name, strrpos($class_name, "\\") + 1); | |
| // `flatbuffers` root. | |
| $root_dir = join(DIRECTORY_SEPARATOR, array(dirname(dirname(__FILE__)))); | |
| // Contains the `*.php` files for the FlatBuffers library and the `flatc` | |
| // generated files. | |
| $paths = array(join(DIRECTORY_SEPARATOR, array($root_dir, "php")), | |
| join(DIRECTORY_SEPARATOR, | |
| array($root_dir, "samples", "MyGame", "Sample"))); | |
| foreach ($paths as $path) { | |
| $file = join(DIRECTORY_SEPARATOR, array($path, $class . ".php")); | |
| if (file_exists($file)) { | |
| require($file); | |
| break; | |
| } | |
| } | |
| } | |
| ``` | |
| === "Python" | |
| ```py | |
| import flatbuffers | |
| # Generated by `flatc`. | |
| import MyGame.Sample.Color | |
| import MyGame.Sample.Equipment | |
| import MyGame.Sample.Monster | |
| import MyGame.Sample.Vec3 | |
| import MyGame.Sample.Weapon | |
| ``` | |
| === "Rust" | |
| ```rust | |
| // import the flatbuffers runtime library | |
| extern crate flatbuffers; | |
| // import the generated code | |
| #[allow(dead_code, unused_imports)] | |
| #[path = "./monster_generated.rs"] | |
| mod monster_generated; | |
| pub use monster_generated::my_game::sample::{root_as_monster, | |
| Color, Equipment, | |
| Monster, MonsterArgs, | |
| Vec3, | |
| Weapon, WeaponArgs}; | |
| ``` | |
| === "Swift" | |
| ```swift | |
| /** | |
| // make sure that monster_generated.swift is included in your project | |
| */ | |
| import Flatbuffers | |
| // typealiases for convenience | |
| typealias Monster = MyGame1_Sample_Monster | |
| typealias Weapon = MyGame1_Sample_Weapon | |
| typealias Color = MyGame1_Sample_Color | |
| typealias Vec3 = MyGame1_Sample_Vec3 | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| // note: import flatbuffers with your desired import method | |
| import { MyGame } from './monster_generated'; | |
| ``` | |
| For some languages the runtime libraries are just code files you compile into | |
| your application. While other languages provide packaged libraries via their | |
| package managers. | |
| The generated files include APIs for both serializing and deserializing | |
| FlatBuffers. So these steps are identical for both the consumer and producer. | |
| ## Serialization | |
| Once all the files are included into your application, it's time to start | |
| serializing some data! | |
| With FlatBuffers, serialization can be a bit verbose, since each piece of data | |
| must be serialized separately and in a particular order (depth-first, pre-order | |
| traversal). The verbosity allows efficient serialization without heap | |
| allocations, at the cost of more complex serialization APIs. | |
| For example, any reference type (e.g. `table`, `vector`, `string`) must be | |
| serialized before it can be referred to by other structures. So its typical to | |
| serialize the data from leaf to root node, as will be shown below. | |
| ### FlatBufferBuilder | |
| Most languages use a Builder object for managing the binary array that the data | |
| is serialized into. It provides an API for serializing data, as well as keeps | |
| track of some internal state. The generated code wraps methods on the Builder | |
| object to provide an API tailored to the schema. | |
| First instantiate a Builder (or reuse an existing one) and specify some memory | |
| for it. The builder will automatically resize the backing buffer when necessary. | |
| === "C++" | |
| ```c++ | |
| // Construct a Builder with 1024 byte backing array. | |
| flatbuffers::FlatBufferBuilder builder(1024); | |
| ``` | |
| === "C" | |
| ```c | |
| flatcc_builder_t builder, *B; | |
| B = &builder; | |
| // Initialize the builder object. | |
| flatcc_builder_init(B); | |
| ``` | |
| === "C#" | |
| ```c# | |
| // Construct a Builder with 1024 byte backing array. | |
| FlatBufferBuilder builder = new FlatBufferBuilder(1024); | |
| ``` | |
| === "Dart" | |
| ```dart | |
| // Construct a Builder with 1024 byte backing array. | |
| var builder = new fb.Builder(initialSize: 1024); | |
| ``` | |
| === "Go" | |
| ```go | |
| // Construct a Builder with 1024 byte backing array. | |
| builder := flatbuffers.NewBuilder(1024) | |
| ``` | |
| === "Java" | |
| ```java | |
| // Construct a Builder with 1024 byte backing array. | |
| FlatBufferBuilder builder = new FlatBufferBuilder(1024); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| // Construct a Builder with 1024 byte backing array. | |
| var builder = new flatbuffers.Builder(1024); | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| // Construct a Builder with 1024 byte backing array. | |
| val builder = FlatBufferBuilder(1024) | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| // Construct a Builder with 1024 byte backing array. | |
| let builder = flatbuffers_builder {} | |
| ``` | |
| === "Lua" | |
| ```lua | |
| -- Construct a Builder with 1024 byte backing array. | |
| local builder = flatbuffers.Builder(1024) | |
| ``` | |
| === "PHP" | |
| ```php | |
| // Construct a Builder with 1024 byte backing array. | |
| $builder = new Google\FlatBuffers\FlatbufferBuilder(1024); | |
| ``` | |
| === "Python" | |
| ```py | |
| # Construct a Builder with 1024 byte backing array. | |
| builder = flatbuffers.Builder(1024) | |
| ``` | |
| === "Rust" | |
| ```rust | |
| // Construct a Builder with 1024 byte backing array. | |
| let mut builder = flatbuffers::FlatBufferBuilder::with_capacity(1024); | |
| ``` | |
| === "Swift" | |
| ```swift | |
| // Construct a Builder with 1024 byte backing array. | |
| let builder = FlatBufferBuilder(initialSize: 1024) | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| // Construct a Builder with 1024 byte backing array. | |
| let builder = new flatbuffers.Builder(1024); | |
| ``` | |
| Once a Builder is available, data can be serialized to it via the Builder APIs | |
| and the generated code. | |
| ### Serializing Data | |
| In this tutorial, we are building `Monsters` and `Weapons` for a computer game. | |
| A `Weapon` is represented by a flatbuffer `table` with some fields. One field is | |
| the `name` field, which is type `string` and the other `damage` field is a | |
| numerical scalar. | |
| ```c title="monster.fbs" linenums="28" | |
| table Weapon { | |
| name:string; | |
| damage:short; | |
| } | |
| ``` | |
| #### Strings | |
| Since `string` is a reference type, we first need to serialize it before | |
| assigning it to the `name` field of the `Weapon` table. This is done through the | |
| Builder `CreateString` method. | |
| Let's serialize two weapon strings. | |
| === "C++" | |
| ```c++ | |
| flatbuffers::Offset<String> weapon_one_name = builder.CreateString("Sword"); | |
| flatbuffers::Offset<String> weapon_two_name = builder.CreateString("Axe"); | |
| ``` | |
| `flatbuffers::Offset<>` is a just a "typed" integer tied to a particular | |
| type. It helps make the numerical offset more strongly typed. | |
| === "C" | |
| ```c | |
| flatbuffers_string_ref_t weapon_one_name | |
| = flatbuffers_string_create_str(B, "Sword"); | |
| flatbuffers_string_ref_t weapon_two_name | |
| = flatbuffers_string_create_str(B, "Axe"); | |
| ``` | |
| === "C#" | |
| ```c# | |
| Offset<String> weaponOneName = builder.CreateString("Sword"); | |
| Offset<String> weaponTwoName = builder.CreateString("Axe"); | |
| ``` | |
| === "Dart" | |
| ```dart | |
| final int weaponOneName = builder.writeString("Sword"); | |
| final int weaponTwoName = builder.writeString("Axe"); | |
| ``` | |
| === "Go" | |
| ```go | |
| weaponOne := builder.CreateString("Sword") | |
| weaponTwo := builder.CreateString("Axe") | |
| ``` | |
| === "Java" | |
| ```java | |
| int weaponOneName = builder.createString("Sword") | |
| int weaponTwoName = builder.createString("Axe"); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| var weaponOne = builder.createString('Sword'); | |
| var weaponTwo = builder.createString('Axe'); | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| val weaponOneName = builder.createString("Sword") | |
| val weaponTwoName = builder.createString("Axe") | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| let weapon_one = builder.CreateString("Sword") | |
| let weapon_two = builder.CreateString("Axe") | |
| ``` | |
| === "Lua" | |
| ```lua | |
| local weaponOne = builder:CreateString("Sword") | |
| local weaponTwo = builder:CreateString("Axe") | |
| ``` | |
| === "PHP" | |
| ```php | |
| $weapon_one_name = $builder->createString("Sword") | |
| $weapon_two_name = $builder->createString("Axe"); | |
| ``` | |
| === "Python" | |
| ```py | |
| weapon_one = builder.CreateString('Sword') | |
| weapon_two = builder.CreateString('Axe') | |
| ``` | |
| === "Rust" | |
| ```rust | |
| let weapon_one_name = builder.create_string("Sword"); | |
| let weapon_two_name = builder.create_string("Axe"); | |
| ``` | |
| === "Swift" | |
| ```swift | |
| let weapon1Name = builder.create(string: "Sword") | |
| let weapon2Name = builder.create(string: "Axe") | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| let weaponOne = builder.createString('Sword'); | |
| let weaponTwo = builder.createString('Axe'); | |
| ``` | |
| This performs the actual serialization (the string data is copied into the | |
| backing array) and returns an offset. Think of the offset as a handle to that | |
| reference. It's just a "typed" numerical offset to where that data resides in | |
| the buffer. | |
| #### Tables | |
| Now that we have some names serialized, we can serialize the `Weapon` tables. | |
| Here we will use one of the generated helper functions that was emitted by | |
| `flatc`. The `CreateWeapon` function takes in the Builder object, as well as the | |
| offset to the weapon's name and a numerical value for the damage field. | |
| === "C++" | |
| ```c++ | |
| short weapon_one_damage = 3; | |
| short weapon_two_damage = 5; | |
| // Use the `CreateWeapon()` shortcut to create Weapons with all the fields | |
| // set. | |
| flatbuffers::Offset<Weapon> sword = | |
| CreateWeapon(builder, weapon_one_name, weapon_one_damage); | |
| flatbuffers::Offset<Weapon> axe = | |
| CreateWeapon(builder, weapon_two_name, weapon_two_damage); | |
| ``` | |
| === "C" | |
| ```c | |
| uint16_t weapon_one_damage = 3; | |
| uint16_t weapon_two_damage = 5; | |
| ns(Weapon_ref_t) sword | |
| = ns(Weapon_create(B, weapon_one_name, weapon_one_damage)); | |
| ns(Weapon_ref_t) axe | |
| = ns(Weapon_create(B, weapon_two_name, weapon_two_damage)); | |
| ``` | |
| === "C#" | |
| ```c# | |
| short weaponOneDamage = 3; | |
| short weaponTwoDamage = 5; | |
| // Use the `CreateWeapon()` helper function to create the weapons, since we | |
| // set every field. | |
| Offset<Weapon> sword = | |
| Weapon.CreateWeapon(builder, weaponOneName, weaponOneDamage); | |
| Offset<Weapon> axe = | |
| Weapon.CreateWeapon(builder, weaponTwoName, weaponTwoDamage); | |
| ``` | |
| === "Dart" | |
| ```dart | |
| final int weaponOneDamage = 3; | |
| final int weaponTwoDamage = 5; | |
| final swordBuilder = new myGame.WeaponBuilder(builder) | |
| ..begin() | |
| ..addNameOffset(weaponOneName) | |
| ..addDamage(weaponOneDamage); | |
| final int sword = swordBuilder.finish(); | |
| final axeBuilder = new myGame.WeaponBuilder(builder) | |
| ..begin() | |
| ..addNameOffset(weaponTwoName) | |
| ..addDamage(weaponTwoDamage); | |
| final int axe = axeBuilder.finish(); | |
| ``` | |
| Note, as an alternative, the previous steps can be combined using the | |
| generative Builder classes. | |
| ```dart | |
| final myGame.WeaponBuilder sword = new myGame.WeaponObjectBuilder( | |
| name: "Sword", | |
| damage: 3, | |
| ); | |
| final myGame.WeaponBuilder axe = new myGame.WeaponObjectBuilder( | |
| name: "Axe", | |
| damage: 5, | |
| ); | |
| ``` | |
| === "Go" | |
| ```go | |
| // Create the first `Weapon` ("Sword"). | |
| sample.WeaponStart(builder) | |
| sample.WeaponAddName(builder, weaponOne) | |
| sample.WeaponAddDamage(builder, 3) | |
| sword := sample.WeaponEnd(builder) | |
| // Create the second `Weapon` ("Axe"). | |
| sample.WeaponStart(builder) | |
| sample.WeaponAddName(builder, weaponTwo) | |
| sample.WeaponAddDamage(builder, 5) | |
| axe := sample.WeaponEnd(builder) | |
| ``` | |
| === "Java" | |
| ```java | |
| short weaponOneDamage = 3; | |
| short weaponTwoDamage = 5; | |
| // Use the `createWeapon()` helper function to create the weapons, since we | |
| // set every field. | |
| int sword = Weapon.createWeapon(builder, weaponOneName, weaponOneDamage); | |
| int axe = Weapon.createWeapon(builder, weaponTwoName, weaponTwoDamage); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| // Create the first `Weapon` ('Sword'). | |
| MyGame.Sample.Weapon.startWeapon(builder); | |
| MyGame.Sample.Weapon.addName(builder, weaponOne); | |
| MyGame.Sample.Weapon.addDamage(builder, 3); | |
| var sword = MyGame.Sample.Weapon.endWeapon(builder); | |
| // Create the second `Weapon` ('Axe'). | |
| MyGame.Sample.Weapon.startWeapon(builder); | |
| MyGame.Sample.Weapon.addName(builder, weaponTwo); | |
| MyGame.Sample.Weapon.addDamage(builder, 5); | |
| var axe = MyGame.Sample.Weapon.endWeapon(builder); | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| val weaponOneDamage: Short = 3; | |
| val weaponTwoDamage: Short = 5; | |
| // Use the `createWeapon()` helper function to create the weapons, since we | |
| // set every field. | |
| val sword = Weapon.createWeapon(builder, weaponOneName, weaponOneDamage) | |
| val axe = Weapon.createWeapon(builder, weaponTwoName, weaponTwoDamage) | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| let sword = MyGame_Sample_WeaponBuilder { b } | |
| .start() | |
| .add_name(weapon_one) | |
| .add_damage(3) | |
| .end() | |
| let axe = MyGame_Sample_WeaponBuilder { b } | |
| .start() | |
| .add_name(weapon_two) | |
| .add_damage(5) | |
| .end() | |
| ``` | |
| === "Lua" | |
| ```lua | |
| -- Create the first 'Weapon' | |
| weapon.Start(builder) | |
| weapon.AddName(builder, weaponOne) | |
| weapon.AddDamage(builder, 3) | |
| local sword = weapon.End(builder) | |
| -- Create the second 'Weapon' | |
| weapon.Start(builder) | |
| weapon.AddName(builder, weaponTwo) | |
| weapon.AddDamage(builder, 5) | |
| local axe = weapon.End(builder) | |
| ``` | |
| === "PHP" | |
| ```php | |
| $sword = \MyGame\Sample\Weapon::CreateWeapon($builder, $weapon_one_name, 3); | |
| $axe = \MyGame\Sample\Weapon::CreateWeapon($builder, $weapon_two_name, 5); | |
| ``` | |
| === "Python" | |
| ```py | |
| # Create the first `Weapon` ('Sword'). | |
| MyGame.Sample.Weapon.Start(builder) | |
| MyGame.Sample.Weapon.AddName(builder, weapon_one) | |
| MyGame.Sample.Weapon.AddDamage(builder, 3) | |
| sword = MyGame.Sample.Weapon.End(builder) | |
| # Create the second `Weapon` ('Axe'). | |
| MyGame.Sample.Weapon.Start(builder) | |
| MyGame.Sample.Weapon.AddName(builder, weapon_two) | |
| MyGame.Sample.Weapon.AddDamage(builder, 5) | |
| axe = MyGame.Sample.Weapon.End(builder) | |
| ``` | |
| === "Rust" | |
| ```rust | |
| // Use the `Weapon::create` shortcut to create Weapons with named field | |
| // arguments. | |
| let sword = Weapon::create(&mut builder, &WeaponArgs{ | |
| name: Some(weapon_one_name), | |
| damage: 3, | |
| }); | |
| let axe = Weapon::create(&mut builder, &WeaponArgs{ | |
| name: Some(weapon_two_name), | |
| damage: 5, | |
| }); | |
| ``` | |
| === "Swift" | |
| ```swift | |
| // start creating the weapon by calling startWeapon | |
| let weapon1Start = Weapon.startWeapon(&builder) | |
| Weapon.add(name: weapon1Name, &builder) | |
| Weapon.add(damage: 3, &builder) | |
| // end the object by passing the start point for the weapon 1 | |
| let sword = Weapon.endWeapon(&builder, start: weapon1Start) | |
| let weapon2Start = Weapon.startWeapon(&builder) | |
| Weapon.add(name: weapon2Name, &builder) | |
| Weapon.add(damage: 5, &builder) | |
| let axe = Weapon.endWeapon(&builder, start: weapon2Start) | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| // Create the first `Weapon` ('Sword'). | |
| MyGame.Sample.Weapon.startWeapon(builder); | |
| MyGame.Sample.Weapon.addName(builder, weaponOne); | |
| MyGame.Sample.Weapon.addDamage(builder, 3); | |
| let sword = MyGame.Sample.Weapon.endWeapon(builder); | |
| // Create the second `Weapon` ('Axe'). | |
| MyGame.Sample.Weapon.startWeapon(builder); | |
| MyGame.Sample.Weapon.addName(builder, weaponTwo); | |
| MyGame.Sample.Weapon.addDamage(builder, 5); | |
| let axe = MyGame.Sample.Weapon.endWeapon(builder); | |
| ``` | |
| The generated functions from `flatc`, like `CreateWeapon`, are just composed of | |
| various Builder API methods. So its not required to use the generated code, but | |
| it does make things much simpler and compact. | |
| Just like the `CreateString` methods, the table serialization functions return | |
| an offset to the location of the serialized `Weapon` table. | |
| Now that we have some `Weapons` serialized, we can serialize a `Monster`. | |
| Looking at the schema again, this table has a lot more fields of various types. | |
| Some of these need to be serialized beforehand, for the same reason we | |
| serialized the name string before the weapon table. | |
| !!! note inline end | |
| There is no prescribed ordering of which table fields must be serialized | |
| first, you could serialize in any order you want. You can also not serialize | |
| a field to provide a `null` value, this is done by using an 0 offset value. | |
| ```c title="monster.fbs" linenums="15" | |
| table Monster { | |
| pos:Vec3; | |
| mana:short = 150; | |
| hp:short = 100; | |
| name:string; | |
| friendly:bool = false (deprecated); | |
| inventory:[ubyte]; | |
| color:Color = Blue; | |
| weapons:[Weapon]; | |
| equipped:Equipment; | |
| path:[Vec3]; | |
| } | |
| ``` | |
| #### Vectors | |
| The `weapons` field is a `vector` of `Weapon` tables. We already have two | |
| `Weapons` serialized, so we just need to serialize a `vector` of those offsets. | |
| The Builder provides multiple ways to create `vectors`. | |
| === "C++" | |
| ```c++ | |
| // Create a std::vector of the offsets we had previous made. | |
| std::vector<flatbuffers::Offset<Weapon>> weapons_vector; | |
| weapons_vector.push_back(sword); | |
| weapons_vector.push_back(axe); | |
| // Then serialize that std::vector into the buffer and again get an Offset | |
| // to that vector. Use `auto` here since the full type is long, and it just | |
| // a "typed" number. | |
| auto weapons = builder.CreateVector(weapons_vector); | |
| ``` | |
| === "C" | |
| ```c | |
| // We use the internal builder stack to implement a dynamic vector. | |
| ns(Weapon_vec_start(B)); | |
| ns(Weapon_vec_push(B, sword)); | |
| ns(Weapon_vec_push(B, axe)); | |
| ns(Weapon_vec_ref_t) weapons = ns(Weapon_vec_end(B)); | |
| ``` | |
| === "C#" | |
| ```c# | |
| // Create an array of the two weapon offsets. | |
| var weaps = new Offset<Weapon>[2]; | |
| weaps[0] = sword; | |
| weaps[1] = axe; | |
| // Pass the `weaps` array into the `CreateWeaponsVector()` method to create | |
| // a FlatBuffer vector. | |
| var weapons = Monster.CreateWeaponsVector(builder, weaps); | |
| ``` | |
| === "Dart" | |
| ```dart | |
| // If using the Builder classes, serialize the `[sword,axe]` | |
| final weapons = builder.writeList([sword, axe]); | |
| // If using the ObjectBuilders, just create an array from the two `Weapon`s | |
| final List<myGame.WeaponBuilder> weaps = [sword, axe]; | |
| ``` | |
| === "Go" | |
| ```go | |
| // Create a FlatBuffer vector and prepend the weapons. | |
| // Note: Since we prepend the data, prepend them in reverse order. | |
| sample.MonsterStartWeaponsVector(builder, 2) | |
| builder.PrependUOffsetT(axe) | |
| builder.PrependUOffsetT(sword) | |
| weapons := builder.EndVector(2) | |
| ``` | |
| === "Java" | |
| ```java | |
| // Place the two weapons into an array, and pass it to the | |
| // `createWeaponsVector()` method to create a FlatBuffer vector. | |
| int[] weaps = new int[2]; | |
| weaps[0] = sword; | |
| weaps[1] = axe; | |
| // Pass the `weaps` array into the `createWeaponsVector()` method to create | |
| // a FlatBuffer vector. | |
| int weapons = Monster.createWeaponsVector(builder, weaps); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| // Create an array from the two `Weapon`s and pass it to the | |
| // `createWeaponsVector()` method to create a FlatBuffer vector. | |
| var weaps = [sword, axe]; | |
| var weapons = MyGame.Sample.Monster.createWeaponsVector(builder, weaps); | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| // Place the two weapons into an array, and pass it to the | |
| // `createWeaponsVector()` method to create a FlatBuffer vector. | |
| val weaps = intArrayOf(sword, axe) | |
| // Pass the `weaps` array into the `createWeaponsVector()` method to create | |
| // a FlatBuffer vector. | |
| val weapons = Monster.createWeaponsVector(builder, weaps) | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| let weapons = builder.MyGame_Sample_MonsterCreateWeaponsVector([sword, axe]) | |
| ``` | |
| === "Lua" | |
| ```lua | |
| -- Create a FlatBuffer vector and prepend the weapons. | |
| -- Note: Since we prepend the data, prepend them in reverse order. | |
| monster.StartWeaponsVector(builder, 2) | |
| builder:PrependUOffsetTRelative(axe) | |
| builder:PrependUOffsetTRelative(sword) | |
| local weapons = builder:EndVector(2) | |
| ``` | |
| === "PHP" | |
| ```php | |
| // Create an array from the two `Weapon`s and pass it to the | |
| // `CreateWeaponsVector()` method to create a FlatBuffer vector. | |
| $weaps = array($sword, $axe); | |
| $weapons = \MyGame\Sample\Monster::CreateWeaponsVector($builder, $weaps); | |
| ``` | |
| === "Python" | |
| ```py | |
| # Use the generated helper to create the weapons vector from offsets. | |
| weapons = MyGame.Sample.Monster.CreateWeaponsVector(builder, [sword, axe]) | |
| ``` | |
| === "Rust" | |
| ```rust | |
| // Create a FlatBuffer `vector` that contains offsets to the sword and axe | |
| // we created above. | |
| let weapons = builder.create_vector(&[sword, axe]); | |
| ``` | |
| === "Swift" | |
| ```swift | |
| // Create a FlatBuffer `vector` that contains offsets to the sword and axe | |
| // we created above. | |
| let weaponsOffset = builder.createVector(ofOffsets: [sword, axe]) | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| // Create an array from the two `Weapon`s and pass it to the | |
| // `createWeaponsVector()` method to create a FlatBuffer vector. | |
| let weaps = [sword, axe]; | |
| let weapons = MyGame.Sample.Monster.createWeaponsVector(builder, weaps); | |
| ``` | |
| While we are at it, let us serialize the other two vector fields: the | |
| `inventory` field is just a vector of scalars, and the `path` field is a vector | |
| of structs (which are scalar data as well). So these vectors can be serialized a | |
| bit more directly. | |
| === "C++" | |
| ```c++ | |
| // Create a `vector` representing the inventory of the Orc. Each number | |
| // could correspond to an item that can be claimed after he is slain. | |
| unsigned char treasure[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; | |
| flatbuffers::Offset<flatbuffers::Vector<unsigned char>> inventory = | |
| builder.CreateVector(treasure, 10); | |
| // Construct an array of two `Vec3` structs. | |
| Vec3 points[] = { Vec3(1.0f, 2.0f, 3.0f), Vec3(4.0f, 5.0f, 6.0f) }; | |
| // Serialize it as a vector of structs. | |
| flatbuffers::Offset<flatbuffers::Vector<Vec3>> path = | |
| builder.CreateVectorOfStructs(points, 2); | |
| ``` | |
| === "C" | |
| ```c | |
| // Create a `vector` representing the inventory of the Orc. Each number | |
| // could correspond to an item that can be claimed after he is slain. | |
| uint8_t treasure[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; | |
| flatbuffers_uint8_vec_ref_t inventory; | |
| // `c_vec_len` is the convenience macro we defined earlier. | |
| inventory = flatbuffers_uint8_vec_create(B, treasure, c_vec_len(treasure)); | |
| ``` | |
| === "C#" | |
| ```c# | |
| // Create a `vector` representing the inventory of the Orc. Each number | |
| // could correspond to an item that can be claimed after he is slain. | |
| // Note: Since we prepend the bytes, this loop iterates in reverse order. | |
| Monster.StartInventoryVector(builder, 10); | |
| for (int i = 9; i >= 0; i--) | |
| { | |
| builder.AddByte((byte)i); | |
| } | |
| Offset<Vector<byte>> inventory = builder.EndVector(); | |
| // Start building a path vector of length 2. | |
| Monster.StartPathVector(fbb, 2); | |
| // Serialize the individual Vec3 structs | |
| Vec3.CreateVec3(builder, 1.0f, 2.0f, 3.0f); | |
| Vec3.CreateVec3(builder, 4.0f, 5.0f, 6.0f); | |
| // End the vector to get the offset | |
| Offset<Vector<Vec3>> path = fbb.EndVector(); | |
| ``` | |
| === "Dart" | |
| ```dart | |
| // Create a list representing the inventory of the Orc. Each number | |
| // could correspond to an item that can be claimed after he is slain. | |
| final List<int> treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; | |
| final inventory = builder.writeListUint8(treasure); | |
| // Using the Builder classes, you can write a list of structs like so: | |
| // Note that the intended order should be reversed if order is important. | |
| final vec3Builder = new myGame.Vec3Builder(builder); | |
| vec3Builder.finish(4.0, 5.0, 6.0); | |
| vec3Builder.finish(1.0, 2.0, 3.0); | |
| final int path = builder.endStructVector(2); // the length of the vector | |
| ``` | |
| === "Go" | |
| ```go | |
| // Create a `vector` representing the inventory of the Orc. Each number | |
| // could correspond to an item that can be claimed after he is slain. | |
| // Note: Since we prepend the bytes, this loop iterates in reverse. | |
| sample.MonsterStartInventoryVector(builder, 10) | |
| for i := 9; i >= 0; i-- { | |
| builder.PrependByte(byte(i)) | |
| } | |
| inv := builder.EndVector(10) | |
| sample.MonsterStartPathVector(builder, 2) | |
| sample.CreateVec3(builder, 1.0, 2.0, 3.0) | |
| sample.CreateVec3(builder, 4.0, 5.0, 6.0) | |
| path := builder.EndVector(2) | |
| ``` | |
| === "Java" | |
| ```java | |
| // Create a `vector` representing the inventory of the Orc. Each number | |
| // could correspond to an item that can be claimed after he is slain. | |
| byte[] treasure = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; | |
| int inv = Monster.createInventoryVector(builder, treasure); | |
| Monster.startPathVector(fbb, 2); | |
| Vec3.createVec3(builder, 1.0f, 2.0f, 3.0f); | |
| Vec3.createVec3(builder, 4.0f, 5.0f, 6.0f); | |
| int path = fbb.endVector(); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| // Create a `vector` representing the inventory of the Orc. Each number | |
| // could correspond to an item that can be claimed after he is slain. | |
| var treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; | |
| var inv = MyGame.Sample.Monster.createInventoryVector(builder, treasure); | |
| MyGame.Sample.Monster.startPathVector(builder, 2); | |
| MyGame.Sample.Vec3.createVec3(builder, 1.0, 2.0, 3.0); | |
| MyGame.Sample.Vec3.createVec3(builder, 4.0, 5.0, 6.0); | |
| var path = builder.endVector(); | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| // Create a `vector` representing the inventory of the Orc. Each number | |
| // could correspond to an item that can be claimed after he is slain. | |
| val treasure = byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) | |
| val inv = Monster.createInventoryVector(builder, treasure) | |
| Monster.startPathVector(fbb, 2) | |
| Vec3.createVec3(builder, 1.0f, 2.0f, 3.0f) | |
| Vec3.createVec3(builder, 4.0f, 5.0f, 6.0f) | |
| val path = fbb.endVector() | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| // Inventory. | |
| let inv = builder.MyGame_Sample_MonsterCreateInventoryVector(map(10): _) | |
| builder.MyGame_Sample_MonsterStartPathVector(2) | |
| builder.MyGame_Sample_CreateVec3(1.0, 2.0, 3.0) | |
| builder.MyGame_Sample_CreateVec3(4.0, 5.0, 6.0) | |
| let path = builder.EndVector(2) | |
| ``` | |
| === "Lua" | |
| ```lua | |
| -- Create a `vector` representing the inventory of the Orc. Each number | |
| -- could correspond to an item that can be claimed after he is slain. | |
| -- Note: Since we prepend the bytes, this loop iterates in reverse. | |
| monster.StartInventoryVector(builder, 10) | |
| for i=10,1,-1 do | |
| builder:PrependByte(i) | |
| end | |
| local inv = builder:EndVector(10) | |
| -- Create a FlatBuffer vector and prepend the path locations. | |
| -- Note: Since we prepend the data, prepend them in reverse order. | |
| monster.StartPathVector(builder, 2) | |
| vec3.CreateVec3(builder, 1.0, 2.0, 3.0) | |
| vec3.CreateVec3(builder, 4.0, 5.0, 6.0) | |
| local path = builder:EndVector(2) | |
| ``` | |
| === "PHP" | |
| ```php | |
| // Create a `vector` representing the inventory of the Orc. Each number | |
| // could correspond to an item that can be claimed after he is slain. | |
| $treasure = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); | |
| $inv = \MyGame\Sample\Monster::CreateInventoryVector($builder, $treasure); | |
| \MyGame\Example\Monster::StartPathVector($builder, 2); | |
| \MyGame\Sample\Vec3::CreateVec3($builder, 1.0, 2.0, 3.0); | |
| \MyGame\Sample\Vec3::CreateVec3($builder, 1.0, 2.0, 3.0); | |
| $path = $builder->endVector(); | |
| ``` | |
| === "Python" | |
| ```py | |
| # Create a `vector` representing the inventory of the Orc. Each number | |
| # could correspond to an item that can be claimed after he is slain. | |
| inv = MyGame.Sample.Monster.CreateInventoryVector(builder, range(0, 10)) | |
| path_points = [ | |
| MyGame.Sample.Vec3T(x=1.0, y=2.0, z=3.0), | |
| MyGame.Sample.Vec3T(x=4.0, y=5.0, z=6.0), | |
| ] | |
| path = MyGame.Sample.Monster.CreatePathVector(builder, path_points) | |
| ``` | |
| === "Rust" | |
| ```rust | |
| // Inventory. | |
| let inventory = builder.create_vector(&[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9]); | |
| // Create the path vector of Vec3 objects. | |
| let x = Vec3::new(1.0, 2.0, 3.0); | |
| let y = Vec3::new(4.0, 5.0, 6.0); | |
| let path = builder.create_vector(&[x, y]); | |
| ``` | |
| === "Swift" | |
| ```swift | |
| // create inventory | |
| let inventory: [Byte] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | |
| let inventoryOffset = builder.createVector(inventory) | |
| let path = fbb.createVector(ofStructs: [ | |
| Vec3(x: 1, y: 2, z: 3), | |
| Vec3(x: 4, y: 5, z: 6) | |
| ]) | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| // Create a `vector` representing the inventory of the Orc. Each number | |
| // could correspond to an item that can be claimed after he is slain. | |
| let treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; | |
| let inv = MyGame.Sample.Monster.createInventoryVector(builder, treasure); | |
| MyGame.Sample.Monster.startPathVector(builder, 2); | |
| MyGame.Sample.Vec3.createVec3(builder, 1.0, 2.0, 3.0); | |
| MyGame.Sample.Vec3.createVec3(builder, 4.0, 5.0, 6.0); | |
| let path = builder.endVector(); | |
| ``` | |
| #### Unions | |
| The last non-scalar data field for the `Monster` table is the `equipped` `union` | |
| field. For this case, we will reuse an already serialized `Weapon` (the only | |
| type in the union), without needing to reserialize it. Union fields implicitly | |
| add a hidden `_type` field that stores the type of value stored in the union. | |
| When serializing a union, you must explicitly set this type field, along with | |
| providing the union value. | |
| We will also serialize the other scalar data at the same time, since we have all | |
| the necessary values and Offsets to make a `Monster`. | |
| === "C++" | |
| ```c++ | |
| // Create the remaining data needed for the Monster. | |
| auto name = builder.CreateString("Orc"); | |
| // Create the position struct | |
| auto position = Vec3(1.0f, 2.0f, 3.0f); | |
| // Set his hit points to 300 and his mana to 150. | |
| int hp = 300; | |
| int mana = 150; | |
| // Finally, create the monster using the `CreateMonster` helper function | |
| // to set all fields. | |
| // | |
| // Here we set the union field by using the `.Union()` method of the | |
| // `Offset<Weapon>` axe we already serialized above. We just have to specify | |
| // which type of object we put in the union, and do that with the | |
| // auto-generated `Equipment_Weapon` enum. | |
| flatbuffers::Offset<Monster> orc = | |
| CreateMonster(builder, &position, mana, hp, name, inventory, | |
| Color_Red, weapons, Equipment_Weapon, axe.Union(), | |
| path); | |
| ``` | |
| === "C" | |
| ```c | |
| // Serialize a name for our monster, called "Orc". | |
| // The _str suffix indicates the source is an ascii-z string. | |
| flatbuffers_string_ref_t name = flatbuffers_string_create_str(B, "Orc"); | |
| // Set his hit points to 300 and his mana to 150. | |
| uint16_t hp = 300; | |
| uint16_t mana = 150; | |
| // Define an equipment union. `create` calls in C has a single | |
| // argument for unions where C++ has both a type and a data argument. | |
| ns(Equipment_union_ref_t) equipped = ns(Equipment_as_Weapon(axe)); | |
| ns(Vec3_t) pos = { 1.0f, 2.0f, 3.0f }; | |
| ns(Monster_create_as_root(B, &pos, mana, hp, name, inventory, ns(Color_Red), | |
| weapons, equipped, path)); | |
| ``` | |
| === "C#" | |
| ```c# | |
| // Create the remaining data needed for the Monster. | |
| var name = builder.CreateString("Orc"); | |
| // Create our monster using `StartMonster()` and `EndMonster()`. | |
| Monster.StartMonster(builder); | |
| Monster.AddPos(builder, Vec3.CreateVec3(builder, 1.0f, 2.0f, 3.0f)); | |
| Monster.AddHp(builder, (short)300); | |
| Monster.AddName(builder, name); | |
| Monster.AddInventory(builder, inv); | |
| Monster.AddColor(builder, Color.Red); | |
| Monster.AddWeapons(builder, weapons); | |
| // For union fields, we explicitly add the auto-generated enum for the type | |
| // of value stored in the union. | |
| Monster.AddEquippedType(builder, Equipment.Weapon); | |
| // And we just use the `.Value` property of the already serialized axe. | |
| Monster.AddEquipped(builder, axe.Value); // Axe | |
| Monster.AddPath(builder, path); | |
| Offset<Monster> orc = Monster.EndMonster(builder); | |
| ``` | |
| === "Dart" | |
| ```dart | |
| // Serialize a name for our monster, called "Orc". | |
| final int name = builder.writeString('Orc'); | |
| // Using the Builder API: | |
| // Set his hit points to 300 and his mana to 150. | |
| final int hp = 300; | |
| final int mana = 150; | |
| final monster = new myGame.MonsterBuilder(builder) | |
| ..begin() | |
| ..addNameOffset(name) | |
| ..addInventoryOffset(inventory) | |
| ..addWeaponsOffset(weapons) | |
| ..addEquippedType(myGame.EquipmentTypeId.Weapon) | |
| ..addEquippedOffset(axe) | |
| ..addHp(hp) | |
| ..addMana(mana) | |
| ..addPos(vec3Builder.finish(1.0, 2.0, 3.0)) | |
| ..addPathOffset(path) | |
| ..addColor(myGame.Color.Red); | |
| final int orc = monster.finish(); | |
| ``` | |
| === "Go" | |
| ```go | |
| // Serialize a name for our monster, called "Orc". | |
| name := builder.CreateString("Orc") | |
| // Create our monster using `MonsterStart()` and `MonsterEnd()`. | |
| sample.MonsterStart(builder) | |
| sample.MonsterAddPos(builder, sample.CreateVec3(builder, 1.0, 2.0, 3.0)) | |
| sample.MonsterAddHp(builder, 300) | |
| sample.MonsterAddName(builder, name) | |
| sample.MonsterAddInventory(builder, inv) | |
| sample.MonsterAddColor(builder, sample.ColorRed) | |
| sample.MonsterAddWeapons(builder, weapons) | |
| sample.MonsterAddEquippedType(builder, sample.EquipmentWeapon) | |
| sample.MonsterAddEquipped(builder, axe) | |
| sample.MonsterAddPath(builder, path) | |
| orc := sample.MonsterEnd(builder) | |
| ``` | |
| === "Java" | |
| ```java | |
| // Serialize a name for our monster, called "Orc". | |
| int name = builder.createString("Orc"); | |
| // Create our monster using `startMonster()` and `endMonster()`. | |
| Monster.startMonster(builder); | |
| Monster.addPos(builder, Vec3.createVec3(builder, 1.0f, 2.0f, 3.0f)); | |
| Monster.addName(builder, name); | |
| Monster.addColor(builder, Color.Red); | |
| Monster.addHp(builder, (short)300); | |
| Monster.addInventory(builder, inv); | |
| Monster.addWeapons(builder, weapons); | |
| Monster.addEquippedType(builder, Equipment.Weapon); | |
| Monster.addEquipped(builder, axe); | |
| Monster.addPath(builder, path); | |
| int orc = Monster.endMonster(builder); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| // Serialize a name for our monster, called 'Orc'. | |
| var name = builder.createString('Orc'); | |
| // Create our monster by using `startMonster()` and `endMonster()`. | |
| MyGame.Sample.Monster.startMonster(builder); | |
| MyGame.Sample.Monster.addPos(builder, | |
| MyGame.Sample.Vec3.createVec3(builder, 1.0, 2.0, 3.0)); | |
| MyGame.Sample.Monster.addHp(builder, 300); | |
| MyGame.Sample.Monster.addColor(builder, MyGame.Sample.Color.Red) | |
| MyGame.Sample.Monster.addName(builder, name); | |
| MyGame.Sample.Monster.addInventory(builder, inv); | |
| MyGame.Sample.Monster.addWeapons(builder, weapons); | |
| MyGame.Sample.Monster.addEquippedType(builder, | |
| MyGame.Sample.Equipment.Weapon); | |
| MyGame.Sample.Monster.addEquipped(builder, axe); | |
| MyGame.Sample.Monster.addPath(builder, path); | |
| var orc = MyGame.Sample.Monster.endMonster(builder); | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| // Serialize a name for our monster, called "Orc". | |
| val name = builder.createString("Orc") | |
| // Create our monster using `startMonster()` and `endMonster()`. | |
| Monster.startMonster(builder) | |
| Monster.addPos(builder, Vec3.createVec3(builder, 1.0f, 2.0f, 3.0f)) | |
| Monster.addName(builder, name) | |
| Monster.addColor(builder, Color.Red) | |
| Monster.addHp(builder, 300.toShort()) | |
| Monster.addInventory(builder, inv) | |
| Monster.addWeapons(builder, weapons) | |
| Monster.addEquippedType(builder, Equipment.Weapon) | |
| Monster.addEquipped(builder, axe) | |
| Monster.addPath(builder, path) | |
| val orc = Monster.endMonster(builder) | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| // Name of the monster. | |
| let name = builder.CreateString("Orc") | |
| let orc = MyGame_Sample_MonsterBuilder { b } | |
| .start() | |
| .add_pos(b.MyGame_Sample_CreateVec3(1.0, 2.0, 3.0)) | |
| .add_hp(300) | |
| .add_name(name) | |
| .add_inventory(inv) | |
| .add_color(MyGame_Sample_Color_Red) | |
| .add_weapons(weapons) | |
| .add_equipped_type(MyGame_Sample_Equipment_Weapon) | |
| .add_equipped(weapon_offsets[1]) | |
| .add_path(path) | |
| .end() | |
| ``` | |
| === "Lua" | |
| ```lua | |
| -- Serialize a name for our monster, called 'orc' | |
| local name = builder:CreateString("Orc") | |
| -- Create our monster by using Start() andEnd() | |
| monster.Start(builder) | |
| monster.AddPos(builder, vec3.CreateVec3(builder, 1.0, 2.0, 3.0)) | |
| monster.AddHp(builder, 300) | |
| monster.AddName(builder, name) | |
| monster.AddInventory(builder, inv) | |
| monster.AddColor(builder, color.Red) | |
| monster.AddWeapons(builder, weapons) | |
| monster.AddEquippedType(builder, equipment.Weapon) | |
| monster.AddEquipped(builder, axe) | |
| monster.AddPath(builder, path) | |
| local orc = monster.End(builder) | |
| ``` | |
| === "PHP" | |
| ```php | |
| // Serialize a name for our monster, called "Orc". | |
| $name = $builder->createString("Orc"); | |
| // Create our monster by using `StartMonster()` and `EndMonster()`. | |
| \MyGame\Sample\Monster::StartMonster($builder); | |
| \MyGame\Sample\Monster::AddPos($builder, | |
| \MyGame\Sample\Vec3::CreateVec3($builder, 1.0, 2.0, 3.0)); | |
| \MyGame\Sample\Monster::AddHp($builder, 300); | |
| \MyGame\Sample\Monster::AddName($builder, $name); | |
| \MyGame\Sample\Monster::AddInventory($builder, $inv); | |
| \MyGame\Sample\Monster::AddColor($builder, \MyGame\Sample\Color::Red); | |
| \MyGame\Sample\Monster::AddWeapons($builder, $weapons); | |
| \MyGame\Sample\Monster::AddEquippedType($builder, | |
| \MyGame\Sample\Equipment::Weapon); | |
| \MyGame\Sample\Monster::AddEquipped($builder, $axe); | |
| \MyGame\Sample\Monster::AddPath($builder, $path); | |
| $orc = \MyGame\Sample\Monster::EndMonster($builder); | |
| ``` | |
| === "Python" | |
| ```py | |
| # Serialize a name for our monster, called "Orc". | |
| name = builder.CreateString("Orc") | |
| # Create our monster by using `Monster.Start()` and `Monster.End()`. | |
| MyGame.Sample.Monster.Start(builder) | |
| MyGame.Sample.Monster.AddPos(builder, | |
| MyGame.Sample.Vec3.CreateVec3(builder, 1.0, 2.0, 3.0)) | |
| MyGame.Sample.Monster.AddHp(builder, 300) | |
| MyGame.Sample.Monster.AddName(builder, name) | |
| MyGame.Sample.Monster.AddInventory(builder, inv) | |
| MyGame.Sample.Monster.AddColor(builder, | |
| MyGame.Sample.Color.Color().Red) | |
| MyGame.Sample.Monster.AddWeapons(builder, weapons) | |
| MyGame.Sample.Monster.AddEquippedType( | |
| builder, MyGame.Sample.Equipment.Equipment().Weapon) | |
| MyGame.Sample.Monster.AddEquipped(builder, axe) | |
| MyGame.Sample.Monster.AddPath(builder, path) | |
| orc = MyGame.Sample.Monster.End(builder) | |
| ``` | |
| === "Rust" | |
| ```rust | |
| // Name of the Monster. | |
| let name = builder.create_string("Orc"); | |
| // Create the monster using the `Monster::create` helper function. This | |
| // function accepts a `MonsterArgs` struct, which supplies all of the data | |
| // needed to build a `Monster`. To supply empty/default fields, just use the | |
| // Rust built-in `Default::default()` function, as demonstrated below. | |
| let orc = Monster::create(&mut builder, &MonsterArgs{ | |
| pos: Some(&Vec3::new(1.0f32, 2.0f32, 3.0f32)), | |
| mana: 150, | |
| hp: 80, | |
| name: Some(name), | |
| inventory: Some(inventory), | |
| color: Color::Red, | |
| weapons: Some(weapons), | |
| equipped_type: Equipment::Weapon, | |
| equipped: Some(axe.as_union_value()), | |
| path: Some(path), | |
| ..Default::default() | |
| }); | |
| ``` | |
| === "Swift" | |
| ```swift | |
| // Name of the Monster. | |
| let name = builder.create(string: "Orc") | |
| let orc = Monster.createMonster( | |
| &builder, | |
| pos: MyGame_Sample_Vec3(x: 1, y: 2, z: 3), | |
| hp: 300, | |
| nameOffset: name, | |
| inventoryVectorOffset: inventoryOffset, | |
| color: .red, | |
| weaponsVectorOffset: weaponsOffset, | |
| equippedType: .weapon, | |
| equippedOffset: axe) | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| // Serialize a name for our monster, called 'Orc'. | |
| let name = builder.createString('Orc'); | |
| // Create our monster by using `startMonster()` and `endMonster()`. | |
| MyGame.Sample.Monster.startMonster(builder); | |
| MyGame.Sample.Monster.addPos(builder, | |
| MyGame.Sample.Vec3.createVec3(builder, 1.0, 2.0, 3.0)); | |
| MyGame.Sample.Monster.addHp(builder, 300); | |
| MyGame.Sample.Monster.addColor(builder, MyGame.Sample.Color.Red) | |
| MyGame.Sample.Monster.addName(builder, name); | |
| MyGame.Sample.Monster.addInventory(builder, inv); | |
| MyGame.Sample.Monster.addWeapons(builder, weapons); | |
| MyGame.Sample.Monster.addEquippedType(builder, | |
| MyGame.Sample.Equipment.Weapon); | |
| MyGame.Sample.Monster.addEquipped(builder, axe); | |
| MyGame.Sample.Monster.addPath(builder, path); | |
| let orc = MyGame.Sample.Monster.endMonster(builder); | |
| ``` | |
| ### Finishing | |
| At this point, we have serialized a `Monster` we've named "orc" to the | |
| flatbuffer and have its offset. The `root_type` of the schema is also a | |
| `Monster`, so we have everything we need to finish the serialization step. | |
| This is done by calling the appropriate `finish` method on the Builder, passing | |
| in the orc offset to indicate this `table` is the "entry" point when | |
| deserializing the buffer later. | |
| === "C++" | |
| ```c++ | |
| // Call `Finish()` to instruct the builder that this monster is complete. | |
| // You could also call `FinishMonsterBuffer(builder, orc);` | |
| builder.Finish(orc); | |
| ``` | |
| === "C" | |
| ```c | |
| // Because we used `Monster_create_as_root`, we do not need a `finish` call | |
| // in C. | |
| ``` | |
| === "C#" | |
| ```c# | |
| // Call `Finish()` to instruct the builder that this monster is complete. | |
| // You could also call `Monster.FinishMonsterBuffer(builder, orc);` | |
| builder.Finish(orc.Value); | |
| ``` | |
| === "Dart" | |
| ```dart | |
| // Call `finish()` to instruct the builder that this monster is complete. | |
| // See the next code section, as in Dart `finish` will also return the byte | |
| // array. | |
| ``` | |
| === "Go" | |
| ```go | |
| // Call `Finish()` to instruct the builder that this monster is complete. | |
| builder.Finish(orc) | |
| ``` | |
| === "Java" | |
| ```java | |
| // Call `finish()` to instruct the builder that this monster is complete. | |
| builder.finish(orc); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| // Call `finish()` to instruct the builder that this monster is complete. | |
| builder.finish(orc); | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| // Call `finish()` to instruct the builder that this monster is complete. | |
| builder.finish(orc) ; | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| // Call `Finish()` to instruct the builder that this monster is complete. | |
| builder.Finish(orc) | |
| ``` | |
| === "Lua" | |
| ```lua | |
| -- Call 'Finish()' to instruct the builder that this monster is complete. | |
| builder:Finish(orc) | |
| ``` | |
| === "PHP" | |
| ```php | |
| // Call `finish()` to instruct the builder that this monster is complete. | |
| $builder->finish($orc); | |
| ``` | |
| === "Python" | |
| ```py | |
| # Call `Finish()` to instruct the builder that this monster is complete. | |
| builder.Finish(orc) | |
| ``` | |
| === "Rust" | |
| ```rust | |
| // Call `finish()` to instruct the builder that this monster is complete. | |
| builder.finish(orc, None); | |
| ``` | |
| === "Swift" | |
| ```swift | |
| // Call `finish()` to instruct the builder that this monster is complete. | |
| builder.finish(offset: orc) | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| // Call `finish()` to instruct the builder that this monster is complete. | |
| builder.finish(orc); | |
| ``` | |
| Once you finish a Builder, you can no longer serialize more data to it. | |
| #### Buffer Access | |
| The flatbuffer is now ready to be stored somewhere, sent over the network, | |
| compressed, or whatever you would like to do with it. You access the raw buffer | |
| like so: | |
| === "C++" | |
| ```c++ | |
| // This must be called after `Finish()`. | |
| uint8_t *buf = builder.GetBufferPointer(); | |
| // Returns the size of the buffer that `GetBufferPointer()` points to. | |
| int size = builder.GetSize(); | |
| ``` | |
| === "C" | |
| ```c | |
| uint8_t *buf; | |
| size_t size; | |
| // Allocate and extract a readable buffer from internal builder heap. | |
| // The returned buffer must be deallocated using `free`. | |
| // NOTE: Finalizing the buffer does NOT change the builder, it | |
| // just creates a snapshot of the builder content. | |
| buf = flatcc_builder_finalize_buffer(B, &size); | |
| // use buf | |
| free(buf); | |
| // Optionally reset builder to reuse builder without deallocating | |
| // internal stack and heap. | |
| flatcc_builder_reset(B); | |
| // build next buffer. | |
| // ... | |
| // Cleanup. | |
| flatcc_builder_clear(B); | |
| ``` | |
| === "C#" | |
| ```c# | |
| // This must be called after `Finish()`. | |
| // | |
| // The data in this ByteBuffer does NOT start at 0, but at buf.Position. | |
| // The end of the data is marked by buf.Length, so the size is | |
| // buf.Length - buf.Position. | |
| FlatBuffers.ByteBuffer dataBuffer = builder.DataBuffer; | |
| // Alternatively this copies the above data out of the ByteBuffer for you: | |
| byte[] buf = builder.SizedByteArray(); | |
| ``` | |
| === "Dart" | |
| ```dart | |
| final Uint8List buf = builder.finish(orc); | |
| ``` | |
| === "Go" | |
| ```go | |
| // This must be called after `Finish()`. | |
| buf := builder.FinishedBytes() // Of type `byte[]`. | |
| ``` | |
| === "Java" | |
| ```java | |
| // This must be called after `finish()`. | |
| java.nio.ByteBuffer buf = builder.dataBuffer(); | |
| // The data in this ByteBuffer does NOT start at 0, but at buf.position(). | |
| // The number of bytes is buf.remaining(). | |
| // Alternatively this copies the above data out of the ByteBuffer for you: | |
| byte[] buf = builder.sizedByteArray(); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| // This must be called after `finish()`. | |
| var buf = builder.asUint8Array(); // Of type `Uint8Array`. | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| // This must be called after `finish()`. | |
| val buf = builder.dataBuffer() | |
| // The data in this ByteBuffer does NOT start at 0, but at buf.position(). | |
| // The number of bytes is buf.remaining(). | |
| // Alternatively this copies the above data out of the ByteBuffer for you: | |
| val buf = builder.sizedByteArray() | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| // This must be called after `Finish()`. | |
| let buf = builder.SizedCopy() // Of type `string`. | |
| ``` | |
| === "Lua" | |
| ```lua | |
| local bufAsString = builder:Output() | |
| ``` | |
| === "PHP" | |
| ```php | |
| // This must be called after `finish()`. | |
| $buf = $builder->dataBuffer(); // Of type `Google\FlatBuffers\ByteBuffer` | |
| // The data in this ByteBuffer does NOT start at 0, but at | |
| // buf->getPosition(). | |
| // The end of the data is marked by buf->capacity(), so the size is | |
| // buf->capacity() - buf->getPosition(). | |
| ``` | |
| === "Python" | |
| ```py | |
| # This must be called after `Finish()`. | |
| buf = builder.Output() // Of type `bytearray`. | |
| ``` | |
| === "Rust" | |
| ```rust | |
| // This must be called after `finish()`. | |
| // `finished_data` returns a byte slice. | |
| let buf = builder.finished_data(); // Of type `&[u8]` | |
| ``` | |
| === "Swift" | |
| ```swift | |
| // This must be called after `finish()`. | |
| // `sizedByteArray` returns the finished buf of type [UInt8]. | |
| let buf = builder.sizedByteArray | |
| // or you can use to get an object of type Data | |
| let bufData = ByteBuffer(data: builder.data) | |
| // or | |
| let buf = builder.sizedBuffer | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| // This must be called after `finish()`. | |
| let buf = builder.asUint8Array(); // Of type `Uint8Array`. | |
| ``` | |
| Now you can write the bytes to a file or send them over the network. The buffer | |
| stays valid until the Builder is cleared or destroyed. | |
| Make sure your file mode (or transfer protocol) is set to BINARY, and not TEXT. | |
| If you try to transfer a flatbuffer in TEXT mode, the buffer will be corrupted | |
| and be hard to diagnose. | |
| ## Deserialization | |
| Deserialization is a bit of a misnomer, since FlatBuffers doesn't deserialize | |
| the whole buffer when accessed. It just "decodes" the data that is requested, | |
| leaving all the other data untouched. It is up to the application to decide if | |
| the data is copied out or even read in the first place. However, we continue to | |
| use the word `deserialize` to mean accessing data from a binary flatbuffer. | |
| Now that we have successfully create an orc FlatBuffer, the data can be saved, | |
| sent over a network, etc. At some point, the buffer will be accessed to obtain | |
| the underlying data. | |
| The same application setup used for serialization is needed for deserialization | |
| (see [application integration](#application-integration)). | |
| ### Root Access | |
| All access to the data in the flatbuffer must first go through the root object. | |
| There is only one root object per flatbuffer. The generated code provides | |
| functions to get the root object given the buffer. | |
| === "C++" | |
| ```c++ | |
| uint8_t *buffer_pointer = /* the data you just read */; | |
| // Get a view to the root object inside the buffer. | |
| Monster monster = GetMonster(buffer_pointer); | |
| ``` | |
| === "C" | |
| ```c | |
| // Note that we use the `table_t` suffix when reading a table object | |
| // as opposed to the `ref_t` suffix used during the construction of | |
| // the buffer. | |
| ns(Monster_table_t) monster = ns(Monster_as_root(buffer)); | |
| // Note: root object pointers are NOT the same as the `buffer` pointer. | |
| ``` | |
| === "C#" | |
| ```c# | |
| byte[] bytes = /* the data you just read */ | |
| // Get a view to the root object inside the buffer. | |
| Monster monster = Monster.GetRootAsMonster(new ByteBuffer(bytes)); | |
| ``` | |
| === "Dart" | |
| ```dart | |
| List<int> data = ... // the data, e.g. from file or network | |
| // A generated factory constructor that will read the data. | |
| myGame.Monster monster = new myGame.Monster(data); | |
| ``` | |
| === "Go" | |
| ```go | |
| var buf []byte = /* the data you just read */ | |
| // Get an accessor to the root object inside the buffer. | |
| monster := sample.GetRootAsMonster(buf, 0) | |
| // Note: We use `0` for the offset here, which is typical for most buffers | |
| // you would read. If you wanted to read from `builder.Bytes` directly, you | |
| // would need to pass in the offset of `builder.Head()`, as the builder | |
| // constructs the buffer backwards, so may not start at offset 0. | |
| ``` | |
| === "Java" | |
| ```java | |
| byte[] bytes = /* the data you just read */ | |
| java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(bytes); | |
| // Get an accessor to the root object inside the buffer. | |
| Monster monster = Monster.getRootAsMonster(buf); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| // the data you just read, as a `Uint8Array` | |
| // Note that the example here uses `readFileSync` from the built-in `fs` | |
| // module, but other methods for accessing the file contents will also work. | |
| var bytes = new Uint8Array(readFileSync('./monsterdata.bin')); | |
| var buf = new flatbuffers.ByteBuffer(bytes); | |
| // Get an accessor to the root object inside the buffer. | |
| var monster = MyGame.Sample.Monster.getRootAsMonster(buf); | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| val bytes = /* the data you just read */ | |
| val buf = java.nio.ByteBuffer.wrap(bytes) | |
| // Get an accessor to the root object inside the buffer. | |
| Monster monster = Monster.getRootAsMonster(buf) | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| buf = /* the data you just read, in a string */ | |
| // Get an accessor to the root object inside the buffer. | |
| let monster = MyGame_Sample_GetRootAsMonster(buf) | |
| ``` | |
| === "Lua" | |
| ```lua | |
| local bufAsString = -- The data you just read in | |
| -- Convert the string representation into binary array Lua structure | |
| local buf = flatbuffers.binaryArray.New(bufAsString) | |
| -- Get an accessor to the root object insert the buffer | |
| local mon = monster.GetRootAsMonster(buf, 0) | |
| ``` | |
| === "PHP" | |
| ```php | |
| $bytes = /* the data you just read, in a string */ | |
| $buf = Google\FlatBuffers\ByteBuffer::wrap($bytes); | |
| // Get an accessor to the root object inside the buffer. | |
| $monster = \MyGame\Sample\Monster::GetRootAsMonster($buf); | |
| ``` | |
| === "Python" | |
| ```py | |
| buf = /* the data you just read, in an object of type "bytearray" */ | |
| # Get an accessor to the root object inside the buffer. | |
| monster = MyGame.Sample.Monster.Monster.GetRootAs(buf, 0) | |
| # Note: We use `0` for the offset here, which is typical for most buffers | |
| # you would read. If you wanted to read from the `builder.Bytes` directly, | |
| # you would need to pass in the offset of `builder.Head()`, as the builder | |
| # constructs the buffer backwards, so may not start at offset 0. | |
| ``` | |
| === "Rust" | |
| ```rust | |
| let buf = /* the data you just read, in a &[u8] */ | |
| // Get an accessor to the root object inside the buffer. | |
| let monster = root_as_monster(buf).unwrap(); | |
| ``` | |
| === "Swift" | |
| ```swift | |
| // create a ByteBuffer(:) from an [UInt8] or Data() | |
| var buf = // Get your data | |
| // Get an accessor to the root object inside the buffer. | |
| let monster: Monster = try! getCheckedRoot(byteBuffer: &byteBuffer) | |
| // let monster: Monster = getRoot(byteBuffer: &byteBuffer) | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| // the data you just read, as a `Uint8Array`. | |
| // Note that the example here uses `readFileSync` from the built-in `fs` | |
| // module, but other methods for accessing the file contents will also work. | |
| let bytes = new Uint8Array(readFileSync('./monsterdata.bin')); | |
| let buf = new flatbuffers.ByteBuffer(bytes); | |
| // Get an accessor to the root object inside the buffer. | |
| let monster = MyGame.Sample.Monster.getRootAsMonster(buf); | |
| ``` | |
| Again, make sure you read the bytes in BINARY mode, otherwise the buffer may be | |
| corrupted. | |
| In most languages, the returned object is just a "view" of the data with helpful | |
| accessors. Data is typically not copied out of the backing buffer. This also | |
| means the backing buffer must remain alive for the duration of the views. | |
| ### Table Access | |
| If you look in the generated files emitted by `flatc`, you will see it generated | |
| , for each `table`, accessors of all its non-`deprecated` fields. For example, | |
| some of the accessors of the `Monster` root table would look like: | |
| === "C++" | |
| ```c++ | |
| auto hp = monster->hp(); | |
| auto mana = monster->mana(); | |
| auto name = monster->name()->c_str(); | |
| ``` | |
| === "C" | |
| ```c | |
| uint16_t hp = ns(Monster_hp(monster)); | |
| uint16_t mana = ns(Monster_mana(monster)); | |
| flatbuffers_string_t name = ns(Monster_name(monster)); | |
| ``` | |
| === "C#" | |
| ```c# | |
| // For C#, unlike most other languages support by FlatBuffers, most values | |
| // (except for vectors and unions) are available as properties instead of | |
| // accessor methods. | |
| var hp = monster.Hp; | |
| var mana = monster.Mana; | |
| var name = monster.Name; | |
| ``` | |
| === "Dart" | |
| ```dart | |
| // For Dart, unlike other languages support by FlatBuffers, most values | |
| // are available as properties instead of accessor methods. | |
| var hp = monster.hp; | |
| var mana = monster.mana; | |
| var name = monster.name; | |
| ``` | |
| === "Go" | |
| ```go | |
| hp := monster.Hp() | |
| mana := monster.Mana() | |
| name := string(monster.Name()) // Note: `monster.Name()` returns a byte[]. | |
| ``` | |
| === "Java" | |
| ```java | |
| short hp = monster.hp(); | |
| short mana = monster.mana(); | |
| String name = monster.name(); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| var hp = monster.hp(); | |
| var mana = monster.mana(); | |
| var name = monster.name(); | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| val hp = monster.hp | |
| val mana = monster.mana | |
| val name = monster.name | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| let hp = monster.hp | |
| let mana = monster.mana | |
| let name = monster.name | |
| ``` | |
| === "Lua" | |
| ```lua | |
| local hp = mon:Hp() | |
| local mana = mon:Mana() | |
| local name = mon:Name() | |
| ``` | |
| === "PHP" | |
| ```php | |
| $hp = $monster->getHp(); | |
| $mana = $monster->getMana(); | |
| $name = monster->getName(); | |
| ``` | |
| === "Python" | |
| ```py | |
| hp = monster.Hp() | |
| mana = monster.Mana() | |
| name = monster.Name() | |
| ``` | |
| === "Rust" | |
| ```rust | |
| // Get and test some scalar types from the FlatBuffer. | |
| let hp = monster.hp(); | |
| let mana = monster.mana(); | |
| let name = monster.name(); | |
| ``` | |
| === "Swift" | |
| ```swift | |
| let hp = monster.hp | |
| let mana = monster.mana | |
| let name = monster.name // returns an optional string | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| let hp = monster.hp(); | |
| let mana = monster.mana(); | |
| let name = monster.name(); | |
| ``` | |
| These accessors should hold the values `300`, `150`, and `"Orc"` respectively. | |
| The default value of `150` wasn't stored in the `mana` field, but we are still | |
| able to retrieve it. That is because the generated accessors return a hard-coded | |
| default value when it doesn't find the value in the buffer. | |
| #### Nested Object Access | |
| Accessing nested objects is very similar, with the nested field pointing to | |
| another object type. Be careful, the field could be `null` if not present. | |
| For example, accessing the `pos` `struct`, which is type `Vec3` you would do: | |
| === "C++" | |
| ```c++ | |
| auto pos = monster->pos(); | |
| auto x = pos->x(); | |
| auto y = pos->y(); | |
| auto z = pos->z(); | |
| ``` | |
| === "C" | |
| ```c | |
| ns(Vec3_struct_t) pos = ns(Monster_pos(monster)); | |
| float x = ns(Vec3_x(pos)); | |
| float y = ns(Vec3_y(pos)); | |
| float z = ns(Vec3_z(pos)); | |
| ``` | |
| === "C#" | |
| ```c# | |
| var pos = monster.Pos.Value; | |
| var x = pos.X; | |
| var y = pos.Y; | |
| var z = pos.Z; | |
| ``` | |
| === "Dart" | |
| ```dart | |
| myGame.Vec3 pos = monster.pos; | |
| double x = pos.x; | |
| double y = pos.y; | |
| double z = pos.z; | |
| ``` | |
| === "Go" | |
| ```go | |
| pos := monster.Pos(nil) | |
| x := pos.X() | |
| y := pos.Y() | |
| z := pos.Z() | |
| // Note: Whenever you access a new object, like in `Pos()`, a new temporary | |
| // accessor object gets created. If your code is very performance sensitive, | |
| // you can pass in a pointer to an existing `Vec3` instead of `nil`. This | |
| // allows you to reuse it across many calls to reduce the amount of object | |
| // allocation/garbage collection. | |
| ``` | |
| === "Java" | |
| ```java | |
| Vec3 pos = monster.pos(); | |
| float x = pos.x(); | |
| float y = pos.y(); | |
| float z = pos.z(); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| var pos = monster.pos(); | |
| var x = pos.x(); | |
| var y = pos.y(); | |
| var z = pos.z(); | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| val pos = monster.pos!! | |
| val x = pos.x | |
| val y = pos.y | |
| val z = pos.z | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| let pos = monster.pos | |
| let x = pos.x | |
| let y = pos.y | |
| let z = pos.z | |
| ``` | |
| === "Lua" | |
| ```lua | |
| local pos = mon:Pos() | |
| local x = pos:X() | |
| local y = pos:Y() | |
| local z = pos:Z() | |
| ``` | |
| === "PHP" | |
| ```php | |
| $pos = $monster->getPos(); | |
| $x = $pos->getX(); | |
| $y = $pos->getY(); | |
| $z = $pos->getZ(); | |
| ``` | |
| === "Python" | |
| ```py | |
| pos = monster.Pos() | |
| x = pos.X() | |
| y = pos.Y() | |
| z = pos.Z() | |
| ``` | |
| === "Rust" | |
| ```rust | |
| let pos = monster.pos().unwrap(); | |
| let x = pos.x(); | |
| let y = pos.y(); | |
| let z = pos.z(); | |
| ``` | |
| === "Swift" | |
| ```swift | |
| let pos = monster.pos | |
| let x = pos.x | |
| let y = pos.y | |
| let z = pos.z | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| let pos = monster.pos(); | |
| let x = pos.x(); | |
| let y = pos.y(); | |
| let z = pos.z(); | |
| ``` | |
| Where `x`, `y`, and `z` will contain `1.0`, `2.0`, and `3.0` respectively. | |
| ### Vector Access | |
| Similarly, we can access elements of the `inventory` `vector` by indexing it. | |
| You can also iterate over the length of the vector. | |
| === "C++" | |
| ```c++ | |
| flatbuffers::Vector<unsigned char> inv = monster->inventory(); | |
| auto inv_len = inv->size(); | |
| auto third_item = inv->Get(2); | |
| ``` | |
| === "C" | |
| ```c | |
| // If `inv` hasn't been set, it will be null. It is valid get | |
| // the length of null which will be 0, useful for iteration. | |
| flatbuffers_uint8_vec_t inv = ns(Monster_inventory(monster)); | |
| size_t inv_len = flatbuffers_uint8_vec_len(inv); | |
| ``` | |
| === "C#" | |
| ```c# | |
| int invLength = monster.InventoryLength; | |
| var thirdItem = monster.Inventory(2); | |
| ``` | |
| === "Dart" | |
| ```dart | |
| int invLength = monster.inventory.length; | |
| var thirdItem = monster.inventory[2]; | |
| ``` | |
| === "Go" | |
| ```go | |
| invLength := monster.InventoryLength() | |
| thirdItem := monster.Inventory(2) | |
| ``` | |
| === "Java" | |
| ```java | |
| int invLength = monster.inventoryLength(); | |
| byte thirdItem = monster.inventory(2); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| var invLength = monster.inventoryLength(); | |
| var thirdItem = monster.inventory(2); | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| val invLength = monster.inventoryLength | |
| val thirdItem = monster.inventory(2)!! | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| let inv_len = monster.inventory_length | |
| let third_item = monster.inventory(2) | |
| ``` | |
| === "Lua" | |
| ```lua | |
| local invLength = mon:InventoryLength() | |
| local thirdItem = mon:Inventory(3) -- Lua is 1-based | |
| ``` | |
| === "PHP" | |
| ```php | |
| $inv_len = $monster->getInventoryLength(); | |
| $third_item = $monster->getInventory(2); | |
| ``` | |
| === "Python" | |
| ```py | |
| inv_len = monster.InventoryLength() | |
| third_item = monster.Inventory(2) | |
| ``` | |
| === "Rust" | |
| ```rust | |
| // Get and test an element from the `inventory` FlatBuffer's `vector`. | |
| let inv = monster.inventory().unwrap(); | |
| // Note that this vector is returned as a slice, because direct access for | |
| // this type, a `u8` vector, is safe on all platforms: | |
| let third_item = inv[2]; | |
| ``` | |
| === "Swift" | |
| ```swift | |
| // Get a the count of objects in the vector | |
| let count = monster.inventoryCount | |
| // get item at index 4 | |
| let object = monster.inventory(at: 4) | |
| // or you can fetch the entire array | |
| let inv = monster.inventory | |
| // inv[4] should equal object | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| let invLength = monster.inventoryLength(); | |
| let thirdItem = monster.inventory(2); | |
| ``` | |
| For vectors of tables, you can access the elements like any other vector, except | |
| you need to handle the result as a FlatBuffer table. Here we iterate over the | |
| `weapons` vector that is houses `Weapon` `tables`. | |
| === "C++" | |
| ```c++ | |
| flatbuffers::Vector<Weapon> weapons = monster->weapons(); | |
| auto weapon_len = weapons->size(); | |
| auto second_weapon_name = weapons->Get(1)->name()->str(); | |
| auto second_weapon_damage = weapons->Get(1)->damage() | |
| ``` | |
| === "C" | |
| ```c | |
| ns(Weapon_vec_t) weapons = ns(Monster_weapons(monster)); | |
| size_t weapons_len = ns(Weapon_vec_len(weapons)); | |
| // We can use `const char *` instead of `flatbuffers_string_t`. | |
| const char *second_weapon_name = | |
| ns(Weapon_name(ns(Weapon_vec_at(weapons, 1)))); | |
| uint16_t second_weapon_damage = | |
| ns(Weapon_damage(ns(Weapon_vec_at(weapons, 1)))); | |
| ``` | |
| === "C#" | |
| ```c# | |
| int weaponsLength = monster.WeaponsLength; | |
| var secondWeaponName = monster.Weapons(1).Name; | |
| var secondWeaponDamage = monster.Weapons(1).Damage; | |
| ``` | |
| === "Dart" | |
| ```dart | |
| int weaponsLength = monster.weapons.length; | |
| var secondWeaponName = monster.weapons[1].name; | |
| var secondWeaponDamage = monster.Weapons[1].damage; | |
| ``` | |
| === "Go" | |
| ```go | |
| weaponLength := monster.WeaponsLength() | |
| // We need a `sample.Weapon` to pass into `monster.Weapons()` | |
| // to capture the output of the function.k | |
| weapon := new(sample.Weapon) | |
| if monster.Weapons(weapon, 1) { | |
| secondWeaponName := weapon.Name() | |
| secondWeaponDamage := weapon.Damage() | |
| } | |
| ``` | |
| === "Java" | |
| ```java | |
| int weaponsLength = monster.weaponsLength(); | |
| String secondWeaponName = monster.weapons(1).name(); | |
| short secondWeaponDamage = monster.weapons(1).damage(); | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| var weaponsLength = monster.weaponsLength(); | |
| var secondWeaponName = monster.weapons(1).name(); | |
| var secondWeaponDamage = monster.weapons(1).damage(); | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| val weaponsLength = monster.weaponsLength | |
| val secondWeaponName = monster.weapons(1)!!.name | |
| val secondWeaponDamage = monster.weapons(1)!!.damage | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| let weapons_length = monster.weapons_length | |
| let second_weapon_name = monster.weapons(1).name | |
| let second_weapon_damage = monster.weapons(1).damage | |
| ``` | |
| === "Lua" | |
| ```lua | |
| local weaponsLength = mon:WeaponsLength() | |
| local secondWeaponName = mon:Weapon(2):Name() | |
| local secondWeaponDamage = mon:Weapon(2):Damage() | |
| ``` | |
| === "PHP" | |
| ```php | |
| $weapons_len = $monster->getWeaponsLength(); | |
| $second_weapon_name = $monster->getWeapons(1)->getName(); | |
| $second_weapon_damage = $monster->getWeapons(1)->getDamage(); | |
| ``` | |
| === "Python" | |
| ```py | |
| weapons_length = monster.WeaponsLength() | |
| second_weapon_name = monster.Weapons(1).Name() | |
| second_weapon_damage = monster.Weapons(1).Damage() | |
| ``` | |
| === "Rust" | |
| ```rust | |
| // Get and test the `weapons` FlatBuffers's `vector`. | |
| let weps = monster.weapons().unwrap(); | |
| let weps_len = weps.len(); | |
| let wep2 = weps.get(1); | |
| let second_weapon_name = wep2.name(); | |
| let second_weapon_damage = wep2.damage(); | |
| ``` | |
| === "Swift" | |
| ```swift | |
| // Get the count of weapon objects | |
| let wepsCount = monster.weaponsCount | |
| let weapon2 = monster.weapons(at: 1) | |
| let weaponName = weapon2.name | |
| let weaponDmg = weapon2.damage | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| let weaponsLength = monster.weaponsLength(); | |
| let secondWeaponName = monster.weapons(1).name(); | |
| let secondWeaponDamage = monster.weapons(1).damage(); | |
| ``` | |
| ### Union Access | |
| Lastly , we can access our `equipped` `union` field. Just like when we created | |
| the union, we need to get both parts of the union: the type and the data. | |
| We can access the type to dynamically cast the data as needed (since the union | |
| only stores a FlatBuffer `table`). | |
| === "C++" | |
| ```c++ | |
| auto union_type = monster.equipped_type(); | |
| if (union_type == Equipment_Weapon) { | |
| // Requires `static_cast` to type `const Weapon*`. | |
| auto weapon = static_cast<const Weapon*>(monster->equipped()); | |
| auto weapon_name = weapon->name()->str(); // "Axe" | |
| auto weapon_damage = weapon->damage(); // 5 | |
| } | |
| ``` | |
| === "C" | |
| ```c | |
| // Access union type field. | |
| if (ns(Monster_equipped_type(monster)) == ns(Equipment_Weapon)) { | |
| // Cast to appropriate type: | |
| // C allows for silent void pointer assignment, so we need no | |
| // explicit cast. | |
| ns(Weapon_table_t) weapon = ns(Monster_equipped(monster)); | |
| const char *weapon_name = ns(Weapon_name(weapon)); // "Axe" | |
| uint16_t weapon_damage = ns(Weapon_damage(weapon)); // 5 | |
| } | |
| ``` | |
| === "C#" | |
| ```c# | |
| var unionType = monster.EquippedType; | |
| if (unionType == Equipment.Weapon) { | |
| var weapon = monster.Equipped<Weapon>().Value; | |
| var weaponName = weapon.Name; // "Axe" | |
| var weaponDamage = weapon.Damage; // 5 | |
| } | |
| ``` | |
| === "Dart" | |
| ```dart | |
| var unionType = monster.equippedType.value; | |
| if (unionType == myGame.EquipmentTypeId.Weapon.value) { | |
| myGame.Weapon weapon = mon.equipped as myGame.Weapon; | |
| var weaponName = weapon.name; // "Axe" | |
| var weaponDamage = weapon.damage; // 5 | |
| } | |
| ``` | |
| === "Go" | |
| ```go | |
| // We need a `flatbuffers.Table` to capture the output of the | |
| // `monster.Equipped()` function. | |
| unionTable := new(flatbuffers.Table) | |
| if monster.Equipped(unionTable) { | |
| unionType := monster.EquippedType() | |
| if unionType == sample.EquipmentWeapon { | |
| // Create a `sample.Weapon` object that can be initialized with the | |
| // contents of the `flatbuffers.Table` (`unionTable`), which was | |
| // populated by `monster.Equipped()`. | |
| unionWeapon = new(sample.Weapon) | |
| unionWeapon.Init(unionTable.Bytes, unionTable.Pos) | |
| weaponName = unionWeapon.Name() | |
| weaponDamage = unionWeapon.Damage() | |
| } | |
| } | |
| ``` | |
| === "Java" | |
| ```java | |
| int unionType = monster.EquippedType(); | |
| if (unionType == Equipment.Weapon) { | |
| // Requires an explicit cast to `Weapon`. | |
| Weapon weapon = (Weapon)monster.equipped(new Weapon()); | |
| String weaponName = weapon.name(); // "Axe" | |
| short weaponDamage = weapon.damage(); // 5 | |
| } | |
| ``` | |
| === "JavaScript" | |
| ```javascript | |
| var unionType = monster.equippedType(); | |
| if (unionType == MyGame.Sample.Equipment.Weapon) { | |
| // 'Axe' | |
| var weaponName = monster.equipped(new MyGame.Sample.Weapon()).name(); | |
| // 5 | |
| var weaponDamage = | |
| monster.equipped(new MyGame.Sample.Weapon()).damage(); | |
| } | |
| ``` | |
| === "Kotlin" | |
| ```kotlin | |
| val unionType = monster.EquippedType | |
| if (unionType == Equipment.Weapon) { | |
| // Requires an explicit cast to `Weapon`. | |
| val weapon = monster.equipped(Weapon()) as Weapon | |
| val weaponName = weapon.name // "Axe" | |
| val weaponDamage = weapon.damage // 5 | |
| } | |
| ``` | |
| === "Lobster" | |
| ```lobster | |
| union_type = monster.equipped_type | |
| if union_type == MyGame_Sample_Equipment_Weapon: | |
| // `monster.equipped_as_Weapon` returns a FlatBuffer handle much like | |
| // normal table fields, but this is only valid to call if we already | |
| // know it is the correct type. | |
| let union_weapon = monster.equipped_as_Weapon | |
| let weapon_name = union_weapon.name // "Axe" | |
| let weapon_damage = union_weapon.damage // 5 | |
| ``` | |
| === "Lua" | |
| ```lua | |
| local unionType = mon:EquippedType() | |
| if unionType == equipment.Weapon then | |
| local unionWeapon = weapon.New() | |
| unionWeapon:Init(mon:Equipped().bytes, mon:Equipped().pos) | |
| local weaponName = unionWeapon:Name() -- 'Axe' | |
| local weaponDamage = unionWeapon:Damage() -- 5 | |
| end | |
| ``` | |
| === "PHP" | |
| ```php | |
| $union_type = $monster->getEquippedType(); | |
| if ($union_type == \MyGame\Sample\Equipment::Weapon) { | |
| // "Axe" | |
| $weapon_name = | |
| $monster->getEquipped(new \MyGame\Sample\Weapon())->getName(); | |
| // 5 | |
| $weapon_damage = | |
| $monster->getEquipped(new \MyGame\Sample\Weapon())->getDamage(); | |
| } | |
| ``` | |
| === "Python" | |
| ```py | |
| union_type = monster.EquippedType() | |
| if union_type == MyGame.Sample.Equipment.Equipment().Weapon: | |
| # `monster.Equipped()` returns a `flatbuffers.Table`, which can be used | |
| # to initialize a `MyGame.Sample.Weapon.Weapon()`. | |
| union_weapon = MyGame.Sample.Weapon.Weapon() | |
| union_weapon.Init(monster.Equipped().Bytes, monster.Equipped().Pos) | |
| weapon_name = union_weapon.Name() // 'Axe' | |
| weapon_damage = union_weapon.Damage() // 5 | |
| ``` | |
| === "Rust" | |
| ```rust | |
| // Get and test the `Equipment` union (`equipped` field). | |
| // `equipped_as_weapon` returns a FlatBuffer handle much like normal table | |
| // fields, but this will return `None` if the union is not actually of that | |
| // type. | |
| if monster.equipped_type() == Equipment::Weapon { | |
| let equipped = monster.equipped_as_weapon().unwrap(); | |
| let weapon_name = equipped.name(); | |
| let weapon_damage = equipped.damage(); | |
| ``` | |
| === "Swift" | |
| ```swift | |
| // Get and check if the monster has an equipped item | |
| if monster.equippedType == .weapon { | |
| let _weapon = monster.equipped(type: Weapon.self) | |
| let name = _weapon.name // should return "Axe" | |
| let dmg = _weapon.damage // should return 5 | |
| } | |
| ``` | |
| === "TypeScript" | |
| ```ts | |
| let unionType = monster.equippedType(); | |
| if (unionType == MyGame.Sample.Equipment.Weapon) { | |
| // 'Axe' | |
| let weaponName = monster.equipped(new MyGame.Sample.Weapon()).name(); | |
| // 5 | |
| let weaponDamage = monster.equipped(new MyGame.Sample.Weapon()).damage(); | |
| } | |
| ``` |