Introduction

Sia is a binary serialization library for TypeScript, Go, Python, and C++. It writes typed values into byte buffers with a chainable API and no schema layer.

What is Sia?

Sia is a binary serialization and deserialization library. It provides a chainable API for reading and writing typed binary data into byte buffers. No schemas required, no code generation needed, no boilerplate.

Sia has native implementations in TypeScript, Go, Python, and C++, all sharing the same wire format. Data serialized in one language can be deserialized in another.

Sia is the serialization format used by the Timeleap network. It is designed for high throughput and minimal allocations, making it well-suited for network protocols, WebSocket communication, file formats, and any scenario where JSON is too slow or too large.

Key Features

  • Fast: In our benchmarks, 2-4x faster than JSON, MessagePack, CBOR, and Protobuf for serialization.
  • Multi-language: Native implementations in TypeScript, Go, Python, and C++ with the same wire format.
  • Compact: No field tags, no type markers, no key names. 14-77% smaller than equivalent JSON.
  • Chainable: All add* methods return the instance, enabling fluent one-liner serialization.
  • Zero-copy capable: Optional reference-based reads avoid unnecessary memory copies (TypeScript).
  • Schema compiler: Optional schema language with code generation for all four languages.

How It Works

Sia works by writing typed values sequentially into a byte buffer. You serialize data with add* methods and deserialize it with the corresponding read* methods, in the same order.

import { Sia } from "@timeleap/sia";

// Serialize
const sia = new Sia();
sia.addString8("Alice").addUInt8(30).addBool(true);
const bytes = sia.toUint8Array();

// Deserialize
const reader = new Sia(bytes);
const name = reader.readString8(); // "Alice"
const age = reader.readUInt8(); // 30
const active = reader.readBool(); // true
import sia "github.com/TimeleapLabs/go-sia/v2/pkg"

// Serialize
s := sia.New()
s.AddString8("Alice").AddUInt8(30).AddBool(true)
bytes := s.Bytes()

// Deserialize
reader := sia.NewFromBytes(bytes)
name := reader.ReadString8()  // "Alice"
age := reader.ReadUInt8()     // 30
active := reader.ReadBool()   // true
from sia import Sia

# Serialize
s = Sia()
s.add_string8("Alice").add_uint8(30).add_bool(True)
raw_bytes = s.content

# Deserialize
reader = Sia()
reader.set_content(raw_bytes)
name = reader.read_string8()   # "Alice"
age = reader.read_uint8()      # 30
active = reader.read_bool()    # True
#include <sia/sia.hpp>

// Serialize
auto s = sia::New();
s->AddString8("Alice")->AddUInt8(30)->AddBool(true);
auto bytes = s->Bytes();

// Deserialize
auto reader = sia::NewFromBytes(bytes);
auto name = reader->ReadString8();  // "Alice"
auto age = reader->ReadUInt8();     // 30
auto active = reader->ReadBool();   // true

The add* and read* methods are symmetric: every write method has a matching read method. The number suffix (8, 16, 32, 64) indicates the bit width used to store the value or its length prefix:

  • For integers, the suffix is the storage size: addUInt8 stores 1 byte, addUInt32 stores 4 bytes.
  • For strings and byte arrays, the suffix indicates the length prefix size: addString8 prefixes the string data with a 1-byte length (max 255 bytes), while addString32 uses a 4-byte length prefix (max ~4 GB).
  • For arrays, the suffix indicates the length prefix for the array count: addArray8 supports up to 255 elements, addArray32 up to ~4 billion.

Quick Start

Install

npm install @timeleap/sia
go get github.com/TimeleapLabs/go-sia/v2
pip install timeleap-sia
# Add as a CMake subdirectory or install system-wide
add_subdirectory(sia)
target_link_libraries(your_target PRIVATE sia)

Serialize and deserialize

import { Sia } from "@timeleap/sia";

const sia = new Sia();
sia.addString8("Hello, Sia!").addUInt32(2025).addBool(true);
const bytes = sia.toUint8Array();

const reader = new Sia(bytes);
console.log(reader.readString8()); // "Hello, Sia!"
console.log(reader.readUInt32()); // 2025
console.log(reader.readBool()); // true
s := sia.New()
s.AddString8("Hello, Sia!").AddUInt32(2025).AddBool(true)
bytes := s.Bytes()

reader := sia.NewFromBytes(bytes)
fmt.Println(reader.ReadString8())  // "Hello, Sia!"
fmt.Println(reader.ReadUInt32())   // 2025
fmt.Println(reader.ReadBool())     // true
s = Sia()
s.add_string8("Hello, Sia!").add_uint32(2025).add_bool(True)
raw = s.content

reader = Sia()
reader.set_content(raw)
print(reader.read_string8())   # "Hello, Sia!"
print(reader.read_uint32())    # 2025
print(reader.read_bool())      # True
auto s = sia::New();
s->AddString8("Hello, Sia!")->AddUInt32(2025)->AddBool(true);
auto bytes = s->Bytes();

auto reader = sia::NewFromBytes(bytes);
std::cout << reader->ReadString8() << std::endl;  // "Hello, Sia!"
std::cout << reader->ReadUInt32() << std::endl;    // 2025
std::cout << reader->ReadBool() << std::endl;      // true

Explore by Topic

Core Concepts

Learn how Sia manages buffers, offsets, and memory under the hood.

API Reference

Complete reference for every method on the Sia class (TypeScript).

Performance Guide

Tips for getting the most out of Sia in production workloads.

Schema Compiler

Define schemas in .sia files and generate serialization code for all four languages.

Language Support

Sia has native implementations in four languages. The core API is the same across all of them: add* methods to serialize, read* methods to deserialize, chainable calls, and the same wire format.

FeatureTypeScriptGoPythonC++
Integers (8-64 bit)
Strings (N/8/16/32/64)
Byte arrays
Booleans
BigInt
Typed arrays
ASCII encoding
UTFZ compression
Functional API
Shared buffer pool

Use the language selector in the sidebar to switch all code examples across the docs.

Next Steps

Installation

Install the package for your language and verify your setup.

Quick Start Guide

Serialize and deserialize your first message.

API Reference

Full reference for every method on the Sia class.