Go implementation of FlatBuffers leveraging Go's memory management and providing idiomatic Go interfaces for FlatBuffer operations. The Go binding maintains zero-copy deserialization benefits while integrating seamlessly with Go's type system and concurrency features.
Install FlatBuffers for Go and import the necessary components.
# Install FlatBuffers Go package
go get github.com/google/flatbuffers/go
# Or with specific version
go get github.com/google/flatbuffers/go@v25.2.10// Core imports
import (
flatbuffers "github.com/google/flatbuffers/go"
)
// Common additional imports
import (
"fmt"
"io/ioutil"
"os"
)Main builder type for constructing FlatBuffer data in Go applications.
type Builder struct {
// Internal fields (not exported)
}
/**
* Create new FlatBuffer builder
* @param initialSize Initial buffer size in bytes
* @return New Builder instance
*/
func NewBuilder(initialSize int) *Builder
/**
* Reset builder for reuse, keeping allocated memory
*/
func (b *Builder) Reset()
/**
* Get current write position/size
* @return Current offset
*/
func (b *Builder) Offset() UOffsetT
/**
* Create string and return its offset
* @param s String to store
* @return Offset to string
*/
func (b *Builder) CreateString(s string) UOffsetT
/**
* Create byte vector from slice
* @param v Byte slice to store
* @return Offset to vector
*/
func (b *Builder) CreateByteVector(v []byte) UOffsetT
/**
* Start building a vector
* @param elemSize Size of each element
* @param numElems Number of elements
* @param alignment Required alignment
*/
func (b *Builder) StartVector(elemSize, numElems, alignment int)
/**
* End vector construction
* @return Offset to completed vector
*/
func (b *Builder) EndVector() UOffsetT
/**
* Start building a table
* @param numFields Number of fields in table
*/
func (b *Builder) StartObject(numFields int)
/**
* Add slot for field (prepares vtable entry)
* @param slotnum Field slot number
*/
func (b *Builder) Slot(slotnum int)
/**
* Add byte value to current position
* @param x Byte value
*/
func (b *Builder) PrependByte(x byte)
/**
* Add uint8 value to current position
* @param x Uint8 value
*/
func (b *Builder) PrependUint8(x uint8)
/**
* Add int8 value to current position
* @param x Int8 value
*/
func (b *Builder) PrependInt8(x int8)
/**
* Add uint16 value to current position
* @param x Uint16 value
*/
func (b *Builder) PrependUint16(x uint16)
/**
* Add int16 value to current position
* @param x Int16 value
*/
func (b *Builder) PrependInt16(x int16)
/**
* Add uint32 value to current position
* @param x Uint32 value
*/
func (b *Builder) PrependUint32(x uint32)
/**
* Add int32 value to current position
* @param x Int32 value
*/
func (b *Builder) PrependInt32(x int32)
/**
* Add uint64 value to current position
* @param x Uint64 value
*/
func (b *Builder) PrependUint64(x uint64)
/**
* Add int64 value to current position
* @param x Int64 value
*/
func (b *Builder) PrependInt64(x int64)
/**
* Add float32 value to current position
* @param x Float32 value
*/
func (b *Builder) PrependFloat32(x float32)
/**
* Add float64 value to current position
* @param x Float64 value
*/
func (b *Builder) PrependFloat64(x float64)
/**
* Add offset value relative to current position
* @param off Offset value
*/
func (b *Builder) PrependUOffsetTRelative(off UOffsetT)
/**
* End table construction
* @return Offset to completed table
*/
func (b *Builder) EndObject() UOffsetT
/**
* Finish buffer with root table
* @param rootTable Offset to root table
*/
func (b *Builder) Finish(rootTable UOffsetT)
/**
* Finish buffer with root table and file identifier
* @param rootTable Offset to root table
* @param fileIdentifier 4-byte file identifier
*/
func (b *Builder) FinishWithFileIdentifier(rootTable UOffsetT, fileIdentifier []byte)
/**
* Finish buffer with size prefix
* @param rootTable Offset to root table
*/
func (b *Builder) FinishSizePrefixed(rootTable UOffsetT)
/**
* Get finished buffer as byte slice
* @return Byte slice containing FlatBuffer data
*/
func (b *Builder) FinishedBytes() []byte
/**
* Get buffer size
* @return Size in bytes
*/
func (b *Builder) Size() intUsage Example:
package main
import (
flatbuffers "github.com/google/flatbuffers/go"
"fmt"
)
func main() {
// Create builder
builder := flatbuffers.NewBuilder(1024)
// Create string
name := builder.CreateString("Player")
// Create vector
scores := []int32{100, 200, 300, 400, 500}
builder.StartVector(4, len(scores), 4)
for i := len(scores) - 1; i >= 0; i-- {
builder.PrependInt32(scores[i])
}
scoresOffset := builder.EndVector()
// Create table
builder.StartObject(3)
builder.PrependUOffsetTRelative(name) // name field (slot 0)
builder.Slot(0)
builder.PrependInt32(42) // level field (slot 1)
builder.Slot(1)
builder.PrependUOffsetTRelative(scoresOffset) // scores field (slot 2)
builder.Slot(2)
player := builder.EndObject()
// Finish buffer
builder.Finish(player)
// Get binary data
buffer := builder.FinishedBytes()
fmt.Printf("Buffer size: %d bytes\n", len(buffer))
}Core type definitions used throughout the FlatBuffers Go API.
// Basic offset and size types
type UOffsetT uint32 // Unsigned offset type
type SOffsetT int32 // Signed offset type
type VOffsetT uint16 // VTable offset type
// Constants
const (
SizeUOffsetT = 4 // Size of UOffsetT in bytes
SizeSOffsetT = 4 // Size of SOffsetT in bytes
SizeVOffsetT = 2 // Size of VOffsetT in bytes
FileIdentifierLength = 4 // Length of file identifier
SizePrefixLength = 4 // Size prefix length
)
// Table interface for generated table types
type Table struct {
Bytes []byte // Buffer containing table data
Pos UOffsetT // Position of table in buffer
}
/**
* Get field offset from vtable
* @param table Table instance
* @param vtableOffset Offset in vtable
* @return Field offset or 0 if not present
*/
func (t *Table) Offset(vtableOffset VOffsetT) VOffsetT
/**
* Get indirect offset (for tables, vectors, strings)
* @param table Table instance
* @param offset Field offset
* @return Indirect offset
*/
func (t *Table) Indirect(offset UOffsetT) UOffsetT
/**
* Get string at offset
* @param table Table instance
* @param offset String offset
* @return String value
*/
func (t *Table) String(offset UOffsetT) string
/**
* Get byte slice at offset
* @param table Table instance
* @param offset Offset to data
* @param length Data length
* @return Byte slice
*/
func (t *Table) ByteVector(offset UOffsetT) []byte
/**
* Get vector length at offset
* @param table Table instance
* @param offset Vector offset
* @return Vector length
*/
func (t *Table) VectorLen(offset UOffsetT) int
/**
* Get vector element position
* @param table Table instance
* @param offset Vector offset
* @param i Element index
* @param elemSize Element size in bytes
* @return Element position
*/
func (t *Table) Vector(offset UOffsetT) UOffsetTWhen using flatc --go, the compiler generates Go code following these patterns.
// Example generated Go (from monster.fbs):
package MyGame
import (
flatbuffers "github.com/google/flatbuffers/go"
)
// Vec3 struct definition
type Vec3 struct {
X float32
Y float32
Z float32
}
// Pack Vec3 into builder
func (v *Vec3) Pack(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
builder.Prep(4, 12)
builder.PrependFloat32(v.Z)
builder.PrependFloat32(v.Y)
builder.PrependFloat32(v.X)
return builder.Offset()
}
// Monster table type
type Monster struct {
_tab flatbuffers.Table
}
// Get Monster from buffer bytes at offset
func GetRootAsMonster(buf []byte, offset flatbuffers.UOffsetT) *Monster {
n := flatbuffers.GetUOffsetT(buf[offset:])
x := &Monster{}
x.Init(buf, n+offset)
return x
}
// Initialize Monster with buffer and position
func (rcv *Monster) Init(buf []byte, i flatbuffers.UOffsetT) {
rcv._tab.Bytes = buf
rcv._tab.Pos = i
}
// Get Monster table
func (rcv *Monster) Table() *flatbuffers.Table {
return &rcv._tab
}
// Get position as Vec3
func (rcv *Monster) Pos(obj *Vec3) *Vec3 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
if o != 0 {
x := rcv._tab.Pos + o
if obj == nil {
obj = &Vec3{}
}
obj.X = flatbuffers.GetFloat32(rcv._tab.Bytes[x:])
obj.Y = flatbuffers.GetFloat32(rcv._tab.Bytes[x+4:])
obj.Z = flatbuffers.GetFloat32(rcv._tab.Bytes[x+8:])
return obj
}
return nil
}
// Get mana value
func (rcv *Monster) Mana() int16 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(6))
if o != 0 {
return flatbuffers.GetInt16(rcv._tab.Bytes[rcv._tab.Pos+o:])
}
return 150 // default value
}
// Get HP value
func (rcv *Monster) Hp() int16 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(8))
if o != 0 {
return flatbuffers.GetInt16(rcv._tab.Bytes[rcv._tab.Pos+o:])
}
return 100 // default value
}
// Get name string
func (rcv *Monster) Name() string {
o := flatbuffers.UOffsetT(rcv._tab.Offset(10))
if o != 0 {
return rcv._tab.String(rcv._tab.Pos + o)
}
return ""
}
// Get inventory item at index
func (rcv *Monster) Inventory(j int) uint8 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(14))
if o != 0 {
a := rcv._tab.Vector(rcv._tab.Pos + o)
return flatbuffers.GetUint8(rcv._tab.Bytes[a+flatbuffers.UOffsetT(j*1):])
}
return 0
}
// Get inventory vector length
func (rcv *Monster) InventoryLength() int {
o := flatbuffers.UOffsetT(rcv._tab.Offset(14))
if o != 0 {
return rcv._tab.VectorLen(rcv._tab.Pos + o)
}
return 0
}
// Get inventory as byte slice
func (rcv *Monster) InventoryBytes() []byte {
o := flatbuffers.UOffsetT(rcv._tab.Offset(14))
if o != 0 {
return rcv._tab.ByteVector(rcv._tab.Pos + o)
}
return nil
}
// Monster builder functions
func MonsterStart(builder *flatbuffers.Builder) {
builder.StartObject(6)
}
func MonsterAddPos(builder *flatbuffers.Builder, pos flatbuffers.UOffsetT) {
builder.PrependStructSlot(0, pos, 0)
}
func MonsterAddMana(builder *flatbuffers.Builder, mana int16) {
builder.PrependInt16Slot(1, mana, 150)
}
func MonsterAddHp(builder *flatbuffers.Builder, hp int16) {
builder.PrependInt16Slot(2, hp, 100)
}
func MonsterAddName(builder *flatbuffers.Builder, name flatbuffers.UOffsetT) {
builder.PrependUOffsetTSlot(3, name, 0)
}
func MonsterAddInventory(builder *flatbuffers.Builder, inventory flatbuffers.UOffsetT) {
builder.PrependUOffsetTSlot(5, inventory, 0)
}
func MonsterStartInventoryVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT {
return builder.StartVector(1, numElems, 1)
}
func MonsterEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
return builder.EndObject()
}
// Convenience function to create Monster
func MonsterCreate(builder *flatbuffers.Builder, pos flatbuffers.UOffsetT, mana int16, hp int16, name flatbuffers.UOffsetT, inventory flatbuffers.UOffsetT) flatbuffers.UOffsetT {
MonsterStart(builder)
MonsterAddPos(builder, pos)
MonsterAddMana(builder, mana)
MonsterAddHp(builder, hp)
MonsterAddName(builder, name)
MonsterAddInventory(builder, inventory)
return MonsterEnd(builder)
}Utility functions for working with FlatBuffer data in Go.
// Read scalar values from byte slices
func GetUint8(buf []byte) uint8
func GetInt8(buf []byte) int8
func GetUint16(buf []byte) uint16
func GetInt16(buf []byte) int16
func GetUint32(buf []byte) uint32
func GetInt32(buf []byte) int32
func GetUint64(buf []byte) uint64
func GetInt64(buf []byte) int64
func GetFloat32(buf []byte) float32
func GetFloat64(buf []byte) float64
// Get offset values
func GetUOffsetT(buf []byte) UOffsetT
func GetSOffsetT(buf []byte) SOffsetT
func GetVOffsetT(buf []byte) VOffsetT
// Buffer validation
func SizeUint32(buf []byte) uint32 // Get size prefix
func SizePrefixedBuffer(buf []byte) []byte // Get buffer without size prefix
// String operations
func GetRootAsString(buf []byte, offset UOffsetT) stringWorking with files and Go's concurrency features.
import (
"io/ioutil"
"os"
"sync"
flatbuffers "github.com/google/flatbuffers/go"
)
// File operations
func SaveFlatBuffer(builder *flatbuffers.Builder, filename string) error {
data := builder.FinishedBytes()
return ioutil.WriteFile(filename, data, 0644)
}
func LoadFlatBuffer(filename string) ([]byte, error) {
return ioutil.ReadFile(filename)
}
// Safe concurrent builder pool
type BuilderPool struct {
pool sync.Pool
}
func NewBuilderPool(initialSize int) *BuilderPool {
return &BuilderPool{
pool: sync.Pool{
New: func() interface{} {
return flatbuffers.NewBuilder(initialSize)
},
},
}
}
func (p *BuilderPool) Get() *flatbuffers.Builder {
return p.pool.Get().(*flatbuffers.Builder)
}
func (p *BuilderPool) Put(builder *flatbuffers.Builder) {
builder.Reset()
p.pool.Put(builder)
}
// Concurrent processing example
func ProcessMonstersAsync(monsters []MonsterData) [][]byte {
const numWorkers = 4
pool := NewBuilderPool(1024)
input := make(chan MonsterData, len(monsters))
output := make(chan []byte, len(monsters))
// Start workers
var wg sync.WaitGroup
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
builder := pool.Get()
defer pool.Put(builder)
for monster := range input {
// Build FlatBuffer from monster data
result := buildMonster(builder, monster)
output <- result
builder.Reset()
}
}()
}
// Send work
go func() {
for _, monster := range monsters {
input <- monster
}
close(input)
}()
// Wait and collect results
go func() {
wg.Wait()
close(output)
}()
var results [][]byte
for result := range output {
results = append(results, result)
}
return results
}Complete Usage Example:
package main
import (
"fmt"
"io/ioutil"
"log"
flatbuffers "github.com/google/flatbuffers/go"
// Import generated package
"example/MyGame"
)
func main() {
// Create monster
builder := flatbuffers.NewBuilder(1024)
// Create components
name := builder.CreateString("Dragon")
pos := &MyGame.Vec3{X: 1.0, Y: 2.0, Z: 3.0}
posOffset := pos.Pack(builder)
// Create inventory
inventoryData := []uint8{1, 2, 3, 4, 5}
MyGame.MonsterStartInventoryVector(builder, len(inventoryData))
for i := len(inventoryData) - 1; i >= 0; i-- {
builder.PrependUint8(inventoryData[i])
}
inventory := builder.EndVector()
// Build monster
monster := MyGame.MonsterCreate(builder, posOffset, 200, 150, name, inventory)
// Finish buffer
builder.Finish(monster)
buffer := builder.FinishedBytes()
// Read the data back
readMonster := MyGame.GetRootAsMonster(buffer, 0)
fmt.Printf("Name: %s\n", readMonster.Name())
fmt.Printf("HP: %d\n", readMonster.Hp())
fmt.Printf("Mana: %d\n", readMonster.Mana())
position := readMonster.Pos(nil)
if position != nil {
fmt.Printf("Position: %.1f, %.1f, %.1f\n", position.X, position.Y, position.Z)
}
fmt.Printf("Inventory size: %d\n", readMonster.InventoryLength())
inventoryBytes := readMonster.InventoryBytes()
for i, item := range inventoryBytes {
fmt.Printf("Item %d: %d\n", i, item)
}
// Save to file
if err := ioutil.WriteFile("monster.bin", buffer, 0644); err != nil {
log.Fatal(err)
}
// Load from file
loadedBuffer, err := ioutil.ReadFile("monster.bin")
if err != nil {
log.Fatal(err)
}
loadedMonster := MyGame.GetRootAsMonster(loadedBuffer, 0)
fmt.Printf("Loaded name: %s\n", loadedMonster.Name())
}