Skip to content

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 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 schema will be used for this tutorial. This is part of the FlatBuffers sample code to give complete sample binaries demonstrations.

FlatBuffers schema is a Interface Definition Language (IDL) that has a couple data structures, see the schema documentation for a detail description. Use the inline code annotations to get a brief synopsis of each part of the schema.

monster.fbs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 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) which is one of the binaries built in the repo.

Building flatc

FlatBuffers uses cmake to build projects files for your environment.

cmake -G "Unix Makefiles"
make flatc
cmake -G "Visual Studio 17 2022"
msbuild.exe FlatBuffers.sln

See the documentation on building 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.

flatc --cpp monster.fbs

Note

If you're working in C, you need to use the separate project FlatCC which contains a schema compiler and runtime library in C for C. See flatcc build instructions.

Please be aware of the difference between flatc and flatcc tools.

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
flatc --csharp monster.fbs
flatc --dart monster.fbs
flatc --go monster.fbs
flatc --java monster.fbs
flatc --js monster.fbs
flatc --kotlin monster.fbs
flatc --lobster monster.fbs
flatc --lua monster.fbs
flatc --php monster.fbs
flatc --python monster.fbs
flatc --rust monster.fbs
flatc --swift monster.fbs
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.
#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.
#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]))
using Google.FlatBuffers; // The runtime library for C#
using MyGame.Sample; // The generated files from `flatc`
import 'package:flat_buffers/flat_buffers.dart' as fb;

// Generated by `flatc`.
import 'monster_my_game.sample_generated.dart' as myGame;
import (
      flatbuffers "github.com/google/flatbuffers/go"
      sample "MyGame/Sample"
)
import MyGame.Sample.*; //The `flatc` generated files. (Monster, Vec3, etc.)

import com.google.flatbuffers.FlatBufferBuilder;
// 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`.
import MyGame.Sample.* //The `flatc` generated files. (Monster, Vec3, etc.)

import com.google.flatbuffers.FlatBufferBuilder
import from "../lobster/"  // Where to find flatbuffers.lobster
import monster_generated
-- 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")
// 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;
    }
  }
}
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
// 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};
/**
// 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
// 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.

// Construct a Builder with 1024 byte backing array.
flatbuffers::FlatBufferBuilder builder(1024);
flatcc_builder_t builder, *B;
B = &builder;
// Initialize the builder object.
flatcc_builder_init(B);
// Construct a Builder with 1024 byte backing array.
FlatBufferBuilder builder = new FlatBufferBuilder(1024);
// Construct a Builder with 1024 byte backing array.
var builder = new fb.Builder(initialSize: 1024);
// Construct a Builder with 1024 byte backing array.
builder := flatbuffers.NewBuilder(1024)
// Construct a Builder with 1024 byte backing array.
FlatBufferBuilder builder = new FlatBufferBuilder(1024);
// Construct a Builder with 1024 byte backing array.
var builder = new flatbuffers.Builder(1024);
// Construct a Builder with 1024 byte backing array.
val builder = FlatBufferBuilder(1024)
// Construct a Builder with 1024 byte backing array. 
let builder = flatbuffers_builder {}
-- Construct a Builder with 1024 byte backing array.
local builder = flatbuffers.Builder(1024)
// Construct a Builder with 1024 byte backing array.
$builder = new Google\FlatBuffers\FlatbufferBuilder(1024);
# Construct a Builder with 1024 byte backing array.
builder = flatbuffers.Builder(1024)
// Construct a Builder with 1024 byte backing array.
let mut builder = flatbuffers::FlatBufferBuilder::with_capacity(1024);
// Construct a Builder with 1024 byte backing array.
let builder = FlatBufferBuilder(initialSize: 1024)
// 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.

monster.fbs
28
29
30
31
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.

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.

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");
Offset<String> weaponOneName = builder.CreateString("Sword");
Offset<String> weaponTwoName = builder.CreateString("Axe");
final int weaponOneName = builder.writeString("Sword");
final int weaponTwoName = builder.writeString("Axe");
weaponOne := builder.CreateString("Sword")
weaponTwo := builder.CreateString("Axe")
int weaponOneName = builder.createString("Sword")
int weaponTwoName = builder.createString("Axe");
var weaponOne = builder.createString('Sword');
var weaponTwo = builder.createString('Axe');
val weaponOneName = builder.createString("Sword")
val weaponTwoName = builder.createString("Axe")
let weapon_one = builder.CreateString("Sword")
let weapon_two = builder.CreateString("Axe")
local weaponOne = builder:CreateString("Sword")
local weaponTwo = builder:CreateString("Axe")
$weapon_one_name = $builder->createString("Sword")
$weapon_two_name = $builder->createString("Axe");
weapon_one = builder.CreateString('Sword')
weapon_two = builder.CreateString('Axe')
let weapon_one_name = builder.create_string("Sword");
let weapon_two_name = builder.create_string("Axe");
let weapon1Name = builder.create(string: "Sword")
let weapon2Name = builder.create(string: "Axe")
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.

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);
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));
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);
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.

final myGame.WeaponBuilder sword = new myGame.WeaponObjectBuilder(
    name: "Sword",
    damage: 3,
);

final myGame.WeaponBuilder axe = new myGame.WeaponObjectBuilder(
    name: "Axe",
    damage: 5,
);
// 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)
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);
// 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);
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)
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()
-- 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)
$sword = \MyGame\Sample\Weapon::CreateWeapon($builder, $weapon_one_name, 3);
$axe = \MyGame\Sample\Weapon::CreateWeapon($builder, $weapon_two_name, 5);
# 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)
// 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,
});
// 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)
// 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

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.

monster.fbs
15
16
17
18
19
20
21
22
23
24
25
26
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.

// 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);
// 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));
// 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);
// 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];
// 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)
// 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);
// 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);
// 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)
let weapons = builder.MyGame_Sample_MonsterCreateWeaponsVector([sword, axe])
-- 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)
// 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);
# Create a FlatBuffer vector and prepend the weapons.
# Note: Since we prepend the data, prepend them in reverse order.
MyGame.Sample.Monster.StartWeaponsVector(builder, 2)
builder.PrependUOffsetTRelative(axe)
builder.PrependUOffsetTRelative(sword)
weapons = builder.EndVector()
// Create a FlatBuffer `vector` that contains offsets to the sword and axe
// we created above.
let weapons = builder.create_vector(&[sword, axe]);
// Create a FlatBuffer `vector` that contains offsets to the sword and axe
// we created above.
let weaponsOffset = builder.createVector(ofOffsets: [sword, axe])
// 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.

// 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);
// 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));
// 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();
// 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
// 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)
// 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();
// 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();
// 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()
// 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)
-- 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)
// 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();
# 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.
MyGame.Sample.Monster.StartInventoryVector(builder, 10)
for i in reversed(range(0, 10)):
    builder.PrependByte(i)
inv = builder.EndVector()

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)
path = builder.EndVector()
// 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]);
// 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)
])
// 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.

// 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);
// 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));
// 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);
// 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();
// 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)
// 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);
// 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);
// 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)
// 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()
-- 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)
// 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);
# 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)
// 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()
});
// 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)
// 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.

// Call `Finish()` to instruct the builder that this monster is complete.
// You could also call `FinishMonsterBuffer(builder, orc);`
builder.Finish(orc);
// Because we used `Monster_create_as_root`, we do not need a `finish` call
// in C.
// Call `Finish()` to instruct the builder that this monster is complete.
// You could also call `Monster.FinishMonsterBuffer(builder, orc);`
builder.Finish(orc.Value);
// 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.
// Call `Finish()` to instruct the builder that this monster is complete.
builder.Finish(orc)
// Call `finish()` to instruct the builder that this monster is complete.
builder.finish(orc);
// Call `finish()` to instruct the builder that this monster is complete.
builder.finish(orc);
// Call `finish()` to instruct the builder that this monster is complete.
builder.finish(orc) ;
// Call `Finish()` to instruct the builder that this monster is complete.
builder.Finish(orc)
-- Call 'Finish()' to instruct the builder that this monster is complete.
builder:Finish(orc)
// Call `finish()` to instruct the builder that this monster is complete.
$builder->finish($orc);
# Call `Finish()` to instruct the builder that this monster is complete.
builder.Finish(orc)
// Call `finish()` to instruct the builder that this monster is complete.
builder.finish(orc, None);
// Call `finish()` to instruct the builder that this monster is complete.
builder.finish(offset: orc)
// 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:

// 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();
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);
// 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();
final Uint8List buf = builder.finish(orc);
// This must be called after `Finish()`.
buf := builder.FinishedBytes() // Of type `byte[]`.
// 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();
// This must be called after `finish()`.
var buf = builder.asUint8Array(); // Of type `Uint8Array`.
// 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()
// This must be called after `Finish()`.
let buf = builder.SizedCopy() // Of type `string`.
local bufAsString = builder:Output()
// 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().
# This must be called after `Finish()`.
buf = builder.Output() // Of type `bytearray`.
// This must be called after `finish()`.
// `finished_data` returns a byte slice.
let buf = builder.finished_data(); // Of type `&[u8]`
// 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)
// 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).

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.

uint8_t *buffer_pointer = /* the data you just read */;

// Get an view to the root object inside the buffer.
Monster monster = GetMonster(buffer_pointer);
// 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.
byte[] bytes = /* the data you just read */

// Get an view to the root object inside the buffer.
Monster monster = Monster.GetRootAsMonster(new ByteBuffer(bytes));
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);
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.
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);
// 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);
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)
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)
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)
$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);
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.
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();
// 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)
// 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:

auto hp = monster->hp();
auto mana = monster->mana();
auto name = monster->name()->c_str();
uint16_t hp = ns(Monster_hp(monster));
uint16_t mana = ns(Monster_mana(monster));
flatbuffers_string_t name = ns(Monster_name(monster));
// 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;
// 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;
hp := monster.Hp()
mana := monster.Mana()
name := string(monster.Name()) // Note: `monster.Name()` returns a byte[].
short hp = monster.hp();
short mana = monster.mana();
String name = monster.name();
var hp = monster.hp();
var mana = monster.mana();
var name = monster.name();
val hp = monster.hp
val mana = monster.mana
val name = monster.name
let hp = monster.hp
let mana = monster.mana
let name = monster.name
local hp = mon:Hp()
local mana = mon:Mana()
local name = mon:Name()
$hp = $monster->getHp();
$mana = $monster->getMana();
$name = monster->getName();
hp = monster.Hp()
mana = monster.Mana()
name = monster.Name()
// Get and test some scalar types from the FlatBuffer.
let hp = monster.hp();
let mana = monster.mana();
let name = monster.name();
let hp = monster.hp
let mana = monster.mana
let name = monster.name // returns an optional string
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:

auto pos = monster->pos();
auto x = pos->x();
auto y = pos->y();
auto z = pos->z();
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));
var pos = monster.Pos.Value;
var x = pos.X;
var y = pos.Y;
var z = pos.Z;
myGame.Vec3 pos = monster.pos;
double x = pos.x;
double y = pos.y;
double z = pos.z;
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.
Vec3 pos = monster.pos();
float x = pos.x();
float y = pos.y();
float z = pos.z();
var pos = monster.pos();
var x = pos.x();
var y = pos.y();
var z = pos.z();
val pos = monster.pos!!
val x = pos.x
val y = pos.y
val z = pos.z
let pos = monster.pos
let x = pos.x
let y = pos.y
let z = pos.z
local pos = mon:Pos()
local x = pos:X()
local y = pos:Y()
local z = pos:Z()
$pos = $monster->getPos();
$x = $pos->getX();
$y = $pos->getY();
$z = $pos->getZ();
pos = monster.Pos()
x = pos.X()
y = pos.Y()
z = pos.Z()
let pos = monster.pos().unwrap();
let x = pos.x();
let y = pos.y();
let z = pos.z();
let pos = monster.pos
let x = pos.x
let y = pos.y
let z = pos.z
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.

flatbuffers::Vector<unsigned char> inv = monster->inventory();
auto inv_len = inv->size();
auto third_item = inv->Get(2);
// 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);
int invLength = monster.InventoryLength;
var thirdItem = monster.Inventory(2);
int invLength = monster.inventory.length;
var thirdItem = monster.inventory[2];
invLength := monster.InventoryLength()
thirdItem := monster.Inventory(2)
int invLength = monster.inventoryLength();
byte thirdItem = monster.inventory(2);
var invLength = monster.inventoryLength();
var thirdItem = monster.inventory(2);
val invLength = monster.inventoryLength
val thirdItem = monster.inventory(2)!!
let inv_len = monster.inventory_length
let third_item = monster.inventory(2)
local invLength = mon:InventoryLength()
local thirdItem = mon:Inventory(3) -- Lua is 1-based
$inv_len = $monster->getInventoryLength();
$third_item = $monster->getInventory(2);
inv_len = monster.InventoryLength()
third_item = monster.Inventory(2)
// 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];
// 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
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.

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()
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))));
int weaponsLength = monster.WeaponsLength;
var secondWeaponName = monster.Weapons(1).Name;
var secondWeaponDamage = monster.Weapons(1).Damage;
int weaponsLength = monster.weapons.length;
var secondWeaponName = monster.weapons[1].name;
var secondWeaponDamage = monster.Weapons[1].damage;
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()
}
int weaponsLength = monster.weaponsLength();
String secondWeaponName = monster.weapons(1).name();
short secondWeaponDamage = monster.weapons(1).damage();
var weaponsLength = monster.weaponsLength();
var secondWeaponName = monster.weapons(1).name();
var secondWeaponDamage = monster.weapons(1).damage();
val weaponsLength = monster.weaponsLength
val secondWeaponName = monster.weapons(1)!!.name
val secondWeaponDamage = monster.weapons(1)!!.damage
let weapons_length = monster.weapons_length
let second_weapon_name = monster.weapons(1).name
let second_weapon_damage = monster.weapons(1).damage
local weaponsLength = mon:WeaponsLength()
local secondWeaponName = mon:Weapon(2):Name()
local secondWeaponDamage = mon:Weapon(2):Damage()
$weapons_len = $monster->getWeaponsLength();
$second_weapon_name = $monster->getWeapons(1)->getName();
$second_weapon_damage = $monster->getWeapons(1)->getDamage();
weapons_length = monster.WeaponsLength()
second_weapon_name = monster.Weapons(1).Name()
second_weapon_damage = monster.Weapons(1).Damage()
// 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();
// Get the count of weapon objects
let wepsCount = monster.weaponsCount

let weapon2 = monster.weapons(at: 1)
let weaponName = weapon2.name
let weaponDmg = weapon2.damage
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).

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
}
// 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
}
var unionType = monster.EquippedType;

if (unionType == Equipment.Weapon) {
    var weapon = monster.Equipped<Weapon>().Value;

    var weaponName = weapon.Name;     // "Axe"
    var weaponDamage = weapon.Damage; // 5
}
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
}
// 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()
    }
}
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
}
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();
}
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
}
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
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
$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(); 
}
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
// 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();
// 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
}
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();
}