nDimensional/zig-flatbuffers
No description provided.
Very WIP. Currently only supports decoding.
This project implements a code generator for efficient FlatBuffers decoders in Zig, for Zig.
First, a binary .bfbs schema file is decoded into an IR defined in src/types.zig. The code generator in src/codegen.zig then consumes this IR and emits Zig source code containing type definitions and field accessors specific to the given schema. The generated code uses generic functions from a runtime flatbuffers module to perform safe, typed, zero-copy access on serialized data.
The repo includes a special pre-generated file, src/reflection.zig, which is a decoder for the FlatBuffers reflection schema (reflection.fbs, the "schema schema"). This allows the code generator to read .bfbs files, creating a bootstrapping loop where the code generator both imports reflection.zig and can reproduce it byte-for-byte when run on reflection.fbs.
First, compile your Flatbuffers schema to a binary .bfbs file using the flatc CLI (via homebrew, Ubuntu, Debian etc).
// myschema.fbs
enum Fruit : byte { Banana = -1, Orange = 42 }
table FooBar {
meal : Fruit = Banana;
density : long (deprecated);
say : string;
height : short;
}
flatc -b --schema --bfbs-comments --bfbs-builtins myschema.fbs
Then generate a Zig decoder library for your schema with
zig build generate -- myschema.bfbs | zig fmt --stdin
Alternatively, you can build a standalone executable:
zig build && ./zig-out/bin/generate myschema.bfbs | zig fmt --stdin
Save the output to a file such as myschema.zig and check it into your repo.
To use the generated library, you will also have to add the flatbuffers module as a runtime dependency.
zig fetch --save=flatbuffers \
https://github.com/nDimensional/zig-flatbuffers/archive/refs/{COMMIT_HASH}.tar.gz
In build.zig:
pub fn build(b: *std.Build) void {
// ...
const flatbuffers_dep = b.dependency("flatbuffers", .{});
const flatbuffers = flatbuffers_dep.module("flatbuffers");
// ...
const myschema = b.createModule(.{
// ...
.root_source_file = b.path("myschema.zig"), // output of `zig build generate`
.imports = &.{
.{ .name = "flatbuffers", .module = flatbuffers },
},
});
// now add both `myschema` and `flatbuffers` as imports to your lib/exe
const lib = b.addModule(.{
// ...
.root_source_file = b.path("src/lib.zig"),
.imports = &.{
.{ .name = "flatbuffers", .module = flatbuffers },
.{ .name = "myschema", .module = myschema },
},
});
}
Then in your code (e.g. src/lib.zig)
const flatbuffers = @import("flatbuffers");
const myschema = @import("myschema");
// ...
// data: []align(8) const u8
const root = try flatbuffers.decodeRoot(myschema.FooBar, data);
// root: myschema.FooBar
The result root: myschema.FooBar is a table reference, a lightweight pointer to an offset within data with accessor methods for all of the table fields from your schema.
const meal = root.meal();
// meal: myschema.Fruit (a real Zig enum type!)
const say = root.say();
// say: [:0]const u8 (a regular slice into the original data buffer)
const height = root.height();
// height: u16 (a regular Zig integer value)
MIT © 2025 nDimensional Studios