or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cpp-core.mdflexbuffers.mdgo.mdindex.mdjava.mdjavascript.mdpython.mdrust.mdschema-compiler.mdverification.md
tile.json

flexbuffers.mddocs/

FlexBuffers

Schema-less variant of FlatBuffers for dynamic data structures where the schema is not known at compile time. FlexBuffers provides JSON-like flexibility with FlatBuffers efficiency, making it ideal for configuration files, dynamic APIs, and mixed-type data storage.

Capabilities

Core Concepts

FlexBuffers stores self-describing data without requiring a predefined schema, similar to JSON but with better performance and smaller size.

namespace flexbuffers {

/**
 * Type enumeration for FlexBuffer values
 */
enum Type : uint8_t {
    FBT_NULL = 0,           // Null value
    FBT_INT = 1,            // Signed integer (variable size)
    FBT_UINT = 2,           // Unsigned integer (variable size)  
    FBT_FLOAT = 3,          // Floating point (variable precision)
    
    // Container types
    FBT_KEY = 4,            // String key in maps
    FBT_STRING = 5,         // String value
    FBT_INDIRECT_INT = 6,   // Indirect integer reference
    FBT_INDIRECT_UINT = 7,  // Indirect unsigned integer reference
    FBT_INDIRECT_FLOAT = 8, // Indirect float reference
    
    // Vector types (homogeneous)
    FBT_MAP = 9,            // Key-value map (like JSON object)
    FBT_VECTOR = 10,        // Mixed-type vector (like JSON array)
    FBT_VECTOR_INT = 11,    // Vector of integers
    FBT_VECTOR_UINT = 12,   // Vector of unsigned integers
    FBT_VECTOR_FLOAT = 13,  // Vector of floats
    FBT_VECTOR_KEY = 14,    // Vector of keys (internal use)
    FBT_VECTOR_STRING = 15, // Vector of strings
    
    // Fixed-size vector types (more compact)
    FBT_VECTOR_INT2 = 16,   // Vector of 2 integers
    FBT_VECTOR_UINT2 = 17,  // Vector of 2 unsigned integers
    FBT_VECTOR_FLOAT2 = 18, // Vector of 2 floats
    FBT_VECTOR_INT3 = 19,   // Vector of 3 integers
    FBT_VECTOR_UINT3 = 20,  // Vector of 3 unsigned integers
    FBT_VECTOR_FLOAT3 = 21, // Vector of 3 floats
    FBT_VECTOR_INT4 = 22,   // Vector of 4 integers
    FBT_VECTOR_UINT4 = 23,  // Vector of 4 unsigned integers
    FBT_VECTOR_FLOAT4 = 24, // Vector of 4 floats
    
    FBT_BLOB = 25,          // Binary blob
    FBT_BOOL = 26,          // Boolean value
    FBT_VECTOR_BOOL = 36    // Vector of booleans
};

/**
 * Bit width enumeration for variable-size encoding
 */
enum BitWidth : uint8_t {
    BIT_WIDTH_8 = 0,   // 8-bit values
    BIT_WIDTH_16 = 1,  // 16-bit values  
    BIT_WIDTH_32 = 2,  // 32-bit values
    BIT_WIDTH_64 = 3   // 64-bit values
};

/**
 * Utility functions for type checking
 */
inline bool IsInline(Type t) { return t <= FBT_FLOAT || t == FBT_BOOL; }
inline bool IsTypedVector(Type t) { return (t >= FBT_VECTOR_INT && t <= FBT_VECTOR_STRING) || t == FBT_VECTOR_BOOL; }
inline bool IsFixedTypedVector(Type t) { return t >= FBT_VECTOR_INT2 && t <= FBT_VECTOR_FLOAT4; }
inline bool IsAVector(Type t) { return t >= FBT_VECTOR && t <= FBT_VECTOR_BOOL; }

} // namespace flexbuffers

Builder Class

Builder for constructing FlexBuffer data with a fluent API supporting dynamic typing.

namespace flexbuffers {

class Builder {
public:
    /**
     * Create FlexBuffer builder
     * @param initial_size Initial buffer size (default: 256)
     * @param dedup_strings Whether to deduplicate strings (default: true)
     * @param dedup_keys Whether to deduplicate keys (default: true)
     * @param dedup_key_vectors Whether to deduplicate key vectors (default: true)
     */
    explicit Builder(
        size_t initial_size = 256,
        bool dedup_strings = true,
        bool dedup_keys = true, 
        bool dedup_key_vectors = true
    );

    /// Scalar value methods
    
    /**
     * Add null value
     */
    void Null();
    
    /**
     * Add signed integer
     * @param i Integer value
     */
    void Int(int64_t i);
    
    /**
     * Add unsigned integer
     * @param u Unsigned integer value
     */
    void UInt(uint64_t u);
    
    /**
     * Add floating point value
     * @param f Float value
     */
    void Float(double f);
    
    /**
     * Add boolean value
     * @param b Boolean value
     */
    void Bool(bool b);
    
    /**
     * Add string value
     * @param str String value
     */
    void String(const char *str);
    void String(const std::string &str);
    void String(const char *str, size_t len);
    
    /**
     * Add binary blob
     * @param data Pointer to binary data
     * @param len Length of data
     */
    void Blob(const void *data, size_t len);
    void Blob(const std::vector<uint8_t> &v);

    /// Container methods
    
    /**
     * Start building a vector (JSON array equivalent)
     */
    size_t StartVector();
    
    /**
     * Start building a vector with known size
     * @param len Expected number of elements
     */
    size_t StartVector(size_t len);
    
    /**
     * End vector construction
     * @param start Return value from StartVector
     * @param typed Whether to create typed vector if possible
     * @param fixed Whether to create fixed-size vector if possible
     */
    void EndVector(size_t start, bool typed = true, bool fixed = true);
    
    /**
     * Start building a map (JSON object equivalent)
     */
    size_t StartMap();
    
    /**
     * Start building a map with known size
     * @param len Expected number of key-value pairs
     */
    size_t StartMap(size_t len);
    
    /**
     * End map construction
     * @param start Return value from StartMap
     */
    void EndMap(size_t start);
    
    /**
     * Add key for next value in map
     * @param key Key string
     */
    void Key(const char *key);
    void Key(const std::string &key);

    /// Typed vector methods (more efficient)
    
    /**
     * Create vector of integers
     * @param v Vector of integers
     */
    void Vector(const std::vector<int64_t> &v);
    
    /**
     * Create vector of unsigned integers
     * @param v Vector of unsigned integers
     */
    void Vector(const std::vector<uint64_t> &v);
    
    /**
     * Create vector of floats
     * @param v Vector of floats
     */
    void Vector(const std::vector<double> &v);
    
    /**
     * Create vector of strings
     * @param v Vector of strings
     */
    void Vector(const std::vector<std::string> &v);

    /// Fixed-size vector methods (most efficient)
    
    /**
     * Create fixed-size vector from array
     * @param v Array of values
     * @param len Number of elements (2, 3, or 4)
     */
    void FixedTypedVector(const int64_t *v, size_t len);
    void FixedTypedVector(const uint64_t *v, size_t len);
    void FixedTypedVector(const double *v, size_t len);

    /// Buffer management
    
    /**
     * Finish building and get buffer
     * @return Vector containing FlexBuffer data
     */
    std::vector<uint8_t> GetBuffer();
    
    /**
     * Clear builder for reuse
     */
    void Clear();
    
    /**
     * Get current buffer size
     * @return Size in bytes
     */
    size_t GetSize() const;
};

} // namespace flexbuffers

Builder Usage Example:

#include "flatbuffers/flexbuffers.h"

// Create JSON-like data structure
void CreateFlexBuffer() {
    flexbuffers::Builder fbb;
    
    // Create nested structure equivalent to:
    // {
    //   "name": "John",
    //   "age": 30,
    //   "scores": [95, 87, 92],
    //   "active": true,
    //   "address": {
    //     "street": "123 Main St",
    //     "city": "Anytown"
    //   }
    // }
    
    fbb.Map([&]() {
        fbb.Key("name");
        fbb.String("John");
        
        fbb.Key("age");
        fbb.Int(30);
        
        fbb.Key("scores");
        fbb.Vector([&]() {
            fbb.Int(95);
            fbb.Int(87);
            fbb.Int(92);
        });
        
        fbb.Key("active");
        fbb.Bool(true);
        
        fbb.Key("address");
        fbb.Map([&]() {
            fbb.Key("street");
            fbb.String("123 Main St");
            
            fbb.Key("city");
            fbb.String("Anytown");
        });
    });
    
    // Get buffer
    auto buffer = fbb.GetBuffer();
    
    // Buffer now contains the FlexBuffer data
    std::cout << "FlexBuffer size: " << buffer.size() << " bytes" << std::endl;
}

Reference Class

Reference class for reading FlexBuffer data with type-safe access methods.

namespace flexbuffers {

class Reference {
public:
    /**
     * Create reference from buffer
     * @param buf Buffer containing FlexBuffer data
     * @param offset Offset to reference in buffer
     */
    Reference(const uint8_t *buf, size_t offset);
    
    /**
     * Create reference from vector
     * @param buffer Vector containing FlexBuffer data
     * @param offset Offset to reference in buffer
     */
    Reference(const std::vector<uint8_t> &buffer, size_t offset = 0);

    /// Type checking
    
    /**
     * Get the type of this reference
     * @return Type enum value
     */
    Type GetType() const;
    
    /**
     * Check if reference is null
     * @return True if null
     */
    bool IsNull() const;
    
    /**
     * Check if reference is boolean
     * @return True if boolean
     */
    bool IsBool() const;
    
    /**
     * Check if reference is numeric (int, uint, float)
     * @return True if numeric
     */
    bool IsNumeric() const;
    
    /**
     * Check if reference is integer (signed or unsigned)
     * @return True if integer
     */
    bool IsIntOrUint() const;
    
    /**
     * Check if reference is float
     * @return True if float
     */
    bool IsFloat() const;
    
    /**
     * Check if reference is string
     * @return True if string
     */
    bool IsString() const;
    
    /**
     * Check if reference is vector (any type)
     * @return True if vector
     */
    bool IsVector() const;
    
    /**
     * Check if reference is typed vector
     * @return True if typed vector
     */
    bool IsTypedVector() const;
    
    /**
     * Check if reference is fixed-size vector
     * @return True if fixed-size vector
     */
    bool IsFixedTypedVector() const;
    
    /**
     * Check if reference is map
     * @return True if map
     */
    bool IsMap() const;
    
    /**
     * Check if reference is blob
     * @return True if blob
     */
    bool IsBlob() const;

    /// Value access
    
    /**
     * Get value as boolean
     * @return Boolean value (false if not boolean)
     */
    bool AsBool() const;
    
    /**
     * Get value as signed integer
     * @return Integer value (0 if not integer)
     */
    int64_t AsInt64() const;
    int32_t AsInt32() const;
    int16_t AsInt16() const;
    int8_t AsInt8() const;
    
    /**
     * Get value as unsigned integer
     * @return Unsigned integer value (0 if not integer)
     */
    uint64_t AsUInt64() const;
    uint32_t AsUInt32() const;
    uint16_t AsUInt16() const;
    uint8_t AsUInt8() const;
    
    /**
     * Get value as floating point
     * @return Float value (0.0 if not float)
     */
    double AsDouble() const;
    float AsFloat() const;
    
    /**
     * Get value as string
     * @return String value (empty if not string)
     */
    const char *AsString() const;
    std::string AsString() const;
    
    /**
     * Get value as blob
     * @return Blob data (null if not blob)
     */
    const uint8_t *AsBlob() const;

    /// Container access
    
    /**
     * Get vector/map size
     * @return Number of elements (0 if not container)
     */
    size_t size() const;
    
    /**
     * Check if container is empty
     * @return True if empty or not a container
     */
    bool empty() const;
    
    /**
     * Access vector element by index
     * @param i Element index
     * @return Reference to element
     */
    Reference operator[](size_t i) const;
    
    /**
     * Access map element by key
     * @param key Key string
     * @return Reference to element
     */
    Reference operator[](const char *key) const;
    Reference operator[](const std::string &key) const;

    /// Iteration support
    
    /**
     * Get keys for map iteration
     * @return Vector reference containing keys
     */
    Reference Keys() const;
    
    /**
     * Get values for map iteration  
     * @return Vector reference containing values
     */
    Reference Values() const;
    
    /**
     * Iterator support for C++11 range-based for loops
     */
    class iterator {
        // Implementation details
    public:
        Reference operator*() const;
        iterator& operator++();
        bool operator!=(const iterator& other) const;
    };
    
    iterator begin() const;
    iterator end() const;

    /// Conversion and output
    
    /**
     * Convert to string representation (JSON-like)
     * @return String representation
     */
    std::string ToString() const;
    
    /**
     * Convert to pretty-printed string
     * @param indent Indentation string
     * @return Formatted string
     */
    std::string ToPrettyString(const std::string &indent = "  ") const;
};

/**
 * Get root reference from FlexBuffer
 * @param buffer FlexBuffer data
 * @return Root reference
 */
Reference GetRoot(const std::vector<uint8_t> &buffer);
Reference GetRoot(const uint8_t *buffer, size_t size);

} // namespace flexbuffers

Reference Usage Example:

#include "flatbuffers/flexbuffers.h"
#include <iostream>

void ReadFlexBuffer(const std::vector<uint8_t> &buffer) {
    // Get root reference
    auto root = flexbuffers::GetRoot(buffer);
    
    // Check if root is a map
    if (root.IsMap()) {
        std::cout << "Root is a map with " << root.size() << " entries" << std::endl;
        
        // Access values by key
        auto name = root["name"];
        if (name.IsString()) {
            std::cout << "Name: " << name.AsString() << std::endl;
        }
        
        auto age = root["age"];
        if (age.IsIntOrUint()) {
            std::cout << "Age: " << age.AsInt64() << std::endl;
        }
        
        auto scores = root["scores"];
        if (scores.IsVector()) {
            std::cout << "Scores: ";
            for (size_t i = 0; i < scores.size(); i++) {
                std::cout << scores[i].AsInt64() << " ";
            }
            std::cout << std::endl;
        }
        
        auto active = root["active"];
        if (active.IsBool()) {
            std::cout << "Active: " << (active.AsBool() ? "true" : "false") << std::endl;
        }
        
        // Access nested map
        auto address = root["address"];
        if (address.IsMap()) {
            std::cout << "Address: " << address["street"].AsString() 
                     << ", " << address["city"].AsString() << std::endl;
        }
    }
    
    // Print entire structure
    std::cout << "JSON representation:" << std::endl;
    std::cout << root.ToPrettyString() << std::endl;
}

Language-Specific APIs

FlexBuffers implementations adapted for different programming languages while maintaining API consistency.

JavaScript/TypeScript:

import { flexbuffers } from 'flatbuffers';

// Builder usage
const builder = new flexbuffers.Builder();
builder.startMap();
builder.key('name');
builder.string('John');
builder.key('age');
builder.int(30);
builder.endMap();

const buffer = builder.buffer;

// Reader usage  
const root = flexbuffers.getRoot(buffer);
console.log(root.get('name').stringValue());
console.log(root.get('age').intValue());

Python:

import flatbuffers.flexbuffers as flexbuffers

# Builder usage
builder = flexbuffers.Builder()
with builder.Map():
    builder.Key("name")
    builder.String("John")
    builder.Key("age") 
    builder.Int(30)

buffer = builder.Output()

# Reader usage
root = flexbuffers.GetRoot(buffer)
print(root["name"].AsString())
print(root["age"].AsInt())

Java:

import com.google.flatbuffers.flexbuffers.*;

// Builder usage
FlexBuffersBuilder builder = new FlexBuffersBuilder();
int map = builder.startMap();
builder.key("name");
builder.string("John");
builder.key("age");
builder.int(30);
builder.endMap(map);

ByteBuffer buffer = builder.finish();

// Reader usage
Reference root = FlexBuffers.getRoot(buffer);
System.out.println(root.get("name").asString());
System.out.println(root.get("age").asLong());

Advanced Usage Patterns

Common patterns for using FlexBuffers in real-world applications.

// Configuration file handling
class ConfigManager {
private:
    flexbuffers::Reference config_;
    std::vector<uint8_t> buffer_;
    
public:
    bool LoadFromFile(const std::string &filename) {
        std::ifstream file(filename, std::ios::binary);
        if (!file) return false;
        
        buffer_.assign(std::istreambuf_iterator<char>(file), 
                      std::istreambuf_iterator<char>());
        config_ = flexbuffers::GetRoot(buffer_);
        return config_.IsMap();
    }
    
    template<typename T>
    T GetValue(const std::string &key, const T &default_value) const {
        auto ref = config_[key];
        if constexpr (std::is_same_v<T, std::string>) {
            return ref.IsString() ? ref.AsString() : default_value;
        } else if constexpr (std::is_integral_v<T>) {
            return ref.IsIntOrUint() ? static_cast<T>(ref.AsInt64()) : default_value;
        } else if constexpr (std::is_floating_point_v<T>) {
            return ref.IsFloat() ? static_cast<T>(ref.AsDouble()) : default_value;
        } else if constexpr (std::is_same_v<T, bool>) {
            return ref.IsBool() ? ref.AsBool() : default_value;
        }
        return default_value;
    }
    
    std::vector<std::string> GetStringArray(const std::string &key) const {
        std::vector<std::string> result;
        auto ref = config_[key];
        if (ref.IsVector()) {
            for (size_t i = 0; i < ref.size(); i++) {
                if (ref[i].IsString()) {
                    result.push_back(ref[i].AsString());
                }
            }
        }
        return result;
    }
};

// Dynamic API response handling
class APIResponse {
private:
    flexbuffers::Reference root_;
    
public:
    APIResponse(const std::vector<uint8_t> &data) 
        : root_(flexbuffers::GetRoot(data)) {}
    
    bool IsSuccess() const {
        auto status = root_["status"];
        return status.IsString() && status.AsString() == "success";
    }
    
    std::string GetErrorMessage() const {
        auto error = root_["error"];
        return error.IsString() ? error.AsString() : "Unknown error";
    }
    
    template<typename T>
    std::optional<T> GetData(const std::string &field) const {
        auto data = root_["data"];
        if (!data.IsMap()) return std::nullopt;
        
        auto field_ref = data[field];
        if constexpr (std::is_same_v<T, std::string>) {
            return field_ref.IsString() ? 
                std::make_optional(field_ref.AsString()) : std::nullopt;
        } else if constexpr (std::is_integral_v<T>) {
            return field_ref.IsIntOrUint() ? 
                std::make_optional(static_cast<T>(field_ref.AsInt64())) : std::nullopt;
        }
        return std::nullopt;
    }
};

// Schema evolution example
void HandleVersionedData(const std::vector<uint8_t> &buffer) {
    auto root = flexbuffers::GetRoot(buffer);
    
    // Check version for backward compatibility
    auto version = root["version"];
    int ver = version.IsIntOrUint() ? version.AsInt32() : 1;
    
    switch (ver) {
        case 1:
            // Handle v1 format
            ProcessV1Data(root);
            break;
        case 2:
            // Handle v2 format with new fields
            ProcessV2Data(root);
            break;
        default:
            // Unknown version - try to handle gracefully
            ProcessGenericData(root);
            break;
    }
}

Complete FlexBuffers Example:

#include "flatbuffers/flexbuffers.h"
#include <iostream>
#include <fstream>

int main() {
    // Create complex nested structure
    flexbuffers::Builder fbb;
    
    fbb.Map([&]() {
        fbb.Key("user_id");
        fbb.Int(12345);
        
        fbb.Key("username");
        fbb.String("johndoe");
        
        fbb.Key("profile");
        fbb.Map([&]() {
            fbb.Key("display_name");
            fbb.String("John Doe");
            
            fbb.Key("email");
            fbb.String("john@example.com");
            
            fbb.Key("preferences");
            fbb.Map([&]() {
                fbb.Key("theme");
                fbb.String("dark");
                
                fbb.Key("notifications");
                fbb.Bool(true);
                
                fbb.Key("languages");
                fbb.Vector([&]() {
                    fbb.String("en");
                    fbb.String("es");
                    fbb.String("fr");
                });
            });
        });
        
        fbb.Key("scores");
        fbb.Vector(std::vector<int64_t>{95, 87, 92, 88, 96});
        
        fbb.Key("location");
        fbb.FixedTypedVector(new double[2]{37.7749, -122.4194}, 2); // lat, lng
    });
    
    // Get buffer
    auto buffer = fbb.GetBuffer();
    
    // Save to file
    std::ofstream file("user_data.flexbuf", std::ios::binary);
    file.write(reinterpret_cast<const char*>(buffer.data()), buffer.size());
    file.close();
    
    // Read and parse
    auto root = flexbuffers::GetRoot(buffer);
    
    std::cout << "User ID: " << root["user_id"].AsInt64() << std::endl;
    std::cout << "Username: " << root["username"].AsString() << std::endl;
    std::cout << "Display Name: " << root["profile"]["display_name"].AsString() << std::endl;
    std::cout << "Theme: " << root["profile"]["preferences"]["theme"].AsString() << std::endl;
    
    auto languages = root["profile"]["preferences"]["languages"];
    std::cout << "Languages: ";
    for (size_t i = 0; i < languages.size(); i++) {
        std::cout << languages[i].AsString() << " ";
    }
    std::cout << std::endl;
    
    auto scores = root["scores"];
    std::cout << "Scores: ";
    for (size_t i = 0; i < scores.size(); i++) {
        std::cout << scores[i].AsInt64() << " ";
    }
    std::cout << std::endl;
    
    auto location = root["location"];
    std::cout << "Location: " << location[0].AsDouble() << ", " << location[1].AsDouble() << std::endl;
    
    // Pretty print entire structure
    std::cout << "\nComplete structure:" << std::endl;
    std::cout << root.ToPrettyString() << std::endl;
    
    return 0;
}