Serialization

Crous provides four core functions for serialization: dumps/loads for in-memory operations and dump/load for file/stream I/O.

In-Memory Serialization

dumps(obj, default=None)

Serialize a Python object to FLUX binary bytes.

dumps_example.py
import crous

# Serialize to bytes
data = {"key": "value", "numbers": [1, 2, 3]}
binary = crous.dumps(data)
print(type(binary))  # <class 'bytes'>
print(len(binary))   # compact binary representation

loads(data, object_hook=None)

Deserialize FLUX binary bytes back to a Python object. Automatically detects FLUX vs legacy CROUS format from the magic bytes.

loads_example.py
import crous

binary = crous.dumps({"name": "Alice", "age": 30})

# Deserialize from bytes
result = crous.loads(binary)
print(result)  # {'name': 'Alice', 'age': 30}

File I/O

dump(obj, file, default=None)

Serialize a Python object and write it to a file. Accepts both file paths (strings) and file-like objects.

dump_example.py
import crous

data = {"users": [{"name": "Alice"}, {"name": "Bob"}]}

# Using a file path
crous.dump(data, "users.crous")

# Using a file object
with open("users.crous", "wb") as f:
    crous.dump(data, f)

load(file, object_hook=None)

Read and deserialize from a file. Accepts both file paths and file-like objects.

load_example.py
import crous

# From file path
data = crous.load("users.crous")

# From file object
with open("users.crous", "rb") as f:
    data = crous.load(f)

print(data)  # {'users': [{'name': 'Alice'}, {'name': 'Bob'}]}

The default Parameter

The default parameter lets you handle types that Crous doesn't natively support. It receives the unsupported object and should return a serializable value.

default_example.py
import crous
from datetime import datetime, date
from decimal import Decimal

def default_handler(obj):
    if isinstance(obj, datetime):
        return obj.isoformat()
    if isinstance(obj, date):
        return obj.isoformat()
    if isinstance(obj, Decimal):
        return float(obj)
    raise TypeError(f"Cannot serialize {type(obj)}")

data = {
    "timestamp": datetime.now(),
    "price": Decimal("19.99"),
}

binary = crous.dumps(data, default=default_handler)
result = crous.loads(binary)
print(result)  # {'timestamp': '2024-...', 'price': 19.99}

One-way with default

The default parameter converts objects to basic types, so the original type information is lost on decode. For round-trip type preservation, use custom serializers instead.

The object_hook Parameter

The object_hook parameter is called for every decoded dictionary, letting you transform the result.

object_hook_example.py
import crous

def hook(d):
    if "type" in d and d["type"] == "user":
        return User(d["name"], d["age"])
    return d

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

data = {"type": "user", "name": "Alice", "age": 30}
binary = crous.dumps(data)

user = crous.loads(binary, object_hook=hook)
print(f"{user.name}, {user.age}")  # Alice, 30

Encoder & Decoder Classes

For repeated operations with the same options, use the class-based API:

encoder_decoder.py
import crous

# Create reusable encoder with default handler
encoder = crous.CrousEncoder(default=my_default_handler)
binary = encoder.encode(data)

# Create reusable decoder with object hook
decoder = crous.CrousDecoder(object_hook=my_hook)
result = decoder.decode(binary)

FLUX Binary Format

As of v2.0.0, Crous uses the FLUX binary format by default. FLUX provides:

  • Zigzag varint integers — small integers encoded in 1-2 bytes instead of 8
  • Small-int optimization — integers 0–24 use a single byte
  • Big-endian floats — IEEE 754 double precision
  • Varint lengths — container sizes use 7-bit LEB128 encoding

FLUX Header

# FLUX binary header structure:
# [F][L][U][X] [version: 1 byte] [flags: 1 byte]
#   4 bytes      0x01               0x00

# Total header: 6 bytes

Auto-Detection

crous.loads() automatically detects the format from magic bytes. FLUX data starts with b"FLUX", legacy CROUS data starts with b"CROU". Both formats are transparently decoded.