Api

Array Methods

Methods for serializing and deserializing typed arrays of arbitrary objects using custom serializer and deserializer functions.

Sia's array methods serialize and deserialize arrays of arbitrary types. You provide a serializer function (for writing) and a deserializer function (for reading), giving you full control over how each element is encoded. All methods are available as both class methods and standalone functions:

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

// Functional API (tree-shakeable)
import { Buffer, addArray16, readArray16 } from "@timeleap/sia";

Serializer and Deserializer Functions

Array methods are generic and require callback functions to handle individual elements.

Serializer — called once per element when writing:

type Serializer<T> = (sia: Sia, item: T) => void;

Deserializer — called once per element when reading:

type Deserializer<T> = (sia: Sia) => T;

The serializer receives the Sia instance and the current item. It should call add* methods on the Sia instance to write the item's fields. The deserializer receives the Sia instance and should call read* methods to reconstruct the item, returning it.

Array Methods

addArray8(arr, fn) / readArray8(fn)

Writes or reads an array with an 8-bit (1 byte) length prefix. Maximum array length: 255 elements.

addArray8<T>(arr: T[], fn: (sia: Sia, item: T) => void): Sia
readArray8<T>(fn: (sia: Sia) => T): T[]
ParameterTypeDescription
arrT[]Array of items to serialize
fn (write)(sia: Sia, item: T) => voidSerializer for each element
fn (read)(sia: Sia) => TDeserializer for each element
const sia = new Sia();
const numbers = [1, 2, 3, 255];

sia.addArray8(numbers, (s, n) => s.addUInt8(n));
sia.seek(0);

const result = sia.readArray8((s) => s.readUInt8());
console.log(result); // [1, 2, 3, 255]

addArray16(arr, fn) / readArray16(fn)

Writes or reads an array with a 16-bit (2 byte) length prefix. Maximum array length: 65,535 elements.

addArray16<T>(arr: T[], fn: (sia: Sia, item: T) => void): Sia
readArray16<T>(fn: (sia: Sia) => T): T[]
ParameterTypeDescription
arrT[]Array of items to serialize
fn (write)(sia: Sia, item: T) => voidSerializer for each element
fn (read)(sia: Sia) => TDeserializer for each element
const sia = new Sia();
const values = [1000, 2000, 3000, 65535];

sia.addArray16(values, (s, n) => s.addUInt16(n));
sia.seek(0);

const result = sia.readArray16((s) => s.readUInt16());
console.log(result); // [1000, 2000, 3000, 65535]

addArray32(arr, fn) / readArray32(fn)

Writes or reads an array with a 32-bit (4 byte) length prefix. Maximum array length: ~4.29 billion elements.

addArray32<T>(arr: T[], fn: (sia: Sia, item: T) => void): Sia
readArray32<T>(fn: (sia: Sia) => T): T[]
ParameterTypeDescription
arrT[]Array of items to serialize
fn (write)(sia: Sia, item: T) => voidSerializer for each element
fn (read)(sia: Sia) => TDeserializer for each element
const sia = new Sia();
const values = [100000, 200000, 300000, 4294967295];

sia.addArray32(values, (s, n) => s.addUInt32(n));
sia.seek(0);

const result = sia.readArray32((s) => s.readUInt32());
console.log(result); // [100000, 200000, 300000, 4294967295]

addArray64(arr, fn) / readArray64(fn)

Writes or reads an array with a 64-bit (8 byte) length prefix.

addArray64<T>(arr: T[], fn: (sia: Sia, item: T) => void): Sia
readArray64<T>(fn: (sia: Sia) => T): T[]
ParameterTypeDescription
arrT[]Array of items to serialize
fn (write)(sia: Sia, item: T) => voidSerializer for each element
fn (read)(sia: Sia) => TDeserializer for each element
const sia = new Sia();
const values = [9007199254740991, 1234567890123456];

sia.addArray64(values, (s, n) => s.addUInt64(n));
sia.seek(0);

const result = sia.readArray64((s) => s.readUInt64());
console.log(result); // [9007199254740991, 1234567890123456]

TypeScript Generics

All array methods are generic over T. TypeScript infers the type from the array you pass in and the return type of your deserializer:

// T is inferred as `string`
sia.addArray8(["a", "b", "c"], (s, str) => s.addString8(str));
sia.seek(0);

// T is inferred as `string` from the return type of readString8
const strings = sia.readArray8((s) => s.readString8());
// strings: string[]

For complex types, you can explicitly specify the generic parameter:

interface Point {
  x: number;
  y: number;
}

const points = sia.readArray8<Point>((s) => ({
  x: s.readInt32(),
  y: s.readInt32(),
}));

Length Prefix Sizes

MethodPrefix SizeMax Elements
addArray81 byte255
addArray162 bytes65,535
addArray324 bytes~4.29 billion
addArray648 bytes~2^53
Choose the smallest length prefix that fits your use case. For most applications, addArray8 or addArray16 is sufficient and saves bytes on the wire.

Complete Example

Serializing and deserializing an array of structured log entries:

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

interface LogEntry {
  timestamp: number;
  level: string;
  message: string;
  errorCode: number;
}

// Define reusable serializer and deserializer
const serializeLog = (sia: Sia, entry: LogEntry): void => {
  sia
    .addUInt64(entry.timestamp)
    .addAscii8(entry.level)
    .addString16(entry.message)
    .addUInt16(entry.errorCode);
};

const deserializeLog = (sia: Sia): LogEntry => ({
  timestamp: sia.readUInt64(),
  level: sia.readAscii8(),
  message: sia.readString16(),
  errorCode: sia.readUInt16(),
});

// Sample data
const logs: LogEntry[] = [
  {
    timestamp: 1709654400000,
    level: "INFO",
    message: "Service started successfully",
    errorCode: 0,
  },
  {
    timestamp: 1709654401000,
    level: "WARN",
    message: "High memory usage detected: 89%",
    errorCode: 2001,
  },
  {
    timestamp: 1709654402000,
    level: "ERROR",
    message: "Failed to connect to database",
    errorCode: 5003,
  },
];

// Serialize
const sia = new Sia();
sia.addArray8(logs, serializeLog);
const bytes = sia.toUint8Array();

console.log(`Serialized ${logs.length} log entries into ${bytes.length} bytes`);

// Deserialize
const reader = new Sia(bytes);
const decoded = reader.readArray8(deserializeLog);

console.log(decoded);
// [
//   { timestamp: 1709654400000, level: "INFO", message: "Service started successfully", errorCode: 0 },
//   { timestamp: 1709654401000, level: "WARN", message: "High memory usage detected: 89%", errorCode: 2001 },
//   { timestamp: 1709654402000, level: "ERROR", message: "Failed to connect to database", errorCode: 5003 },
// ]