Skip to main content

Borsh Interface

software.sava.core.borsh.Borsh Provides Borsh (Binary Object Representation Serializer for Hashing) serialization utilities for all primitive types, strings, arrays, and vectors.

Overview

Borsh is the primary serialization format used by Solana programs. The Borsh interface provides static methods for:
  • Serializing and deserializing primitive types
  • Working with fixed-length arrays
  • Working with variable-length vectors
  • Handling optional values
  • Supporting multi-dimensional arrays

Type Categories

Primitives

  • byte / u8 / i8
  • boolean
  • short / u16 / i16
  • int / u32 / i32
  • long / u64 / i64
  • float / f32
  • double / f64
  • BigInteger (for u128 / i128)

Complex Types

  • String
  • PublicKey
  • Arrays (fixed-length)
  • Vectors (variable-length with 4-byte length prefix)
  • Optional values (1-byte discriminator + value)

String Methods

Read String

static String readString(byte[] data, int offset)
static String string(byte[] data, int offset)
data
byte[]
required
Byte array containing serialized string
offset
int
required
Offset to start reading
return
String
Deserialized UTF-8 string

Write String

static int write(String str, byte[] data, int offset)
str
String
required
String to serialize
return
int
Number of bytes written (4 + UTF-8 byte length)

String Length

static int len(String val)
return
int
Serialized length in bytes (4-byte length + UTF-8 bytes)
static int lenOptional(String val)
return
int
1 if null, otherwise 1 + len(val)

String Vectors

static String[] readStringVector(byte[] data, int offset)
return
String[]
Array of strings from vector
static int writeVector(String[] array, byte[] data, int offset)
return
int
Number of bytes written
static int lenVector(String[] array)
return
int
Total serialized length

Integer Methods

Write Integers

static int writeArray(int[] array, byte[] data, int offset)
static int writeVector(int[] array, byte[] data, int offset)
array
int[]
required
Integer array to write
return
int
Number of bytes written
static int writeArrayChecked(
    int[] array,
    int fixedLength,
    byte[] data,
    int offset
)
fixedLength
int
required
Expected array length (throws if mismatch)

Read Integers

static int readArray(int[] result, byte[] data, int offset)
result
int[]
required
Pre-allocated array to fill
return
int
Number of bytes read
static int[] readintVector(byte[] data, int offset)
return
int[]
Deserialized integer array from vector

Integer Length

static int lenArray(int[] array)
return
int
array.length * 4
static int lenVector(int[] array)
return
int
4 + (array.length * 4)

Optional Values

Write Optional

static int writeOptional(OptionalInt val, byte[] data, int offset)
static int writeOptional(OptionalLong val, byte[] data, int offset)
static int writeOptional(OptionalDouble val, byte[] data, int offset)
val
Optional*
required
Optional value to write
return
int
1 if empty, otherwise 1 + value size
static int writeOptional(Boolean val, byte[] data, int offset)
static int writeOptional(Byte val, byte[] data, int offset)
static int writeOptional(Short val, byte[] data, int offset)

Optional Length

static int lenOptional(OptionalInt val)
static int lenOptional(OptionalLong val)
static int lenOptional(Boolean val)
return
int
Size in bytes (1 for none, 1 + value size for some)

Boolean Methods

Write Boolean

static int write(boolean val, byte[] data, int offset)
return
int
1 (always writes 1 byte)
static int writeArray(boolean[] array, byte[] data, int offset)
static int writeVector(boolean[] array, byte[] data, int offset)

Read Boolean

static int readArray(boolean[] result, byte[] data, int offset)
static boolean[] readbooleanVector(byte[] data, int offset)

Byte Array Methods

Write Bytes

static int writeArray(byte[] array, byte[] data, int offset)
return
int
array.length (raw bytes, no length prefix)
static int writeVector(byte[] array, byte[] data, int offset)
return
int
4 + array.length (with 4-byte length prefix)
static int writeOptionalVector(byte[] bytes, byte[] data, int offset)
return
int
1 if null/empty, otherwise 1 + 4 + bytes.length

Read Bytes

static int readArray(byte[] result, byte[] data, int offset)
static byte[] readbyteVector(byte[] data, int offset)
return
byte[]
Byte array from vector

Byte Length

static int lenArray(byte[] array)
return
int
array.length
static int lenVector(byte[] array)
return
int
4 + array.length

Long Methods

Write Long

static int writeArray(long[] array, byte[] data, int offset)
static int writeVector(long[] array, byte[] data, int offset)

Read Long

static int readArray(long[] result, byte[] data, int offset)
static long[] readlongVector(byte[] data, int offset)

Long Length

static int lenArray(long[] array)
return
int
array.length * 8
static int lenVector(long[] array)
return
int
4 + (array.length * 8)

BigInteger Methods (128-bit)

Write 128-bit

static int write128(BigInteger val, byte[] data, int offset)
return
int
16 (always writes 16 bytes)
static int write128Array(BigInteger[] array, byte[] data, int offset)
static int write128Vector(BigInteger[] array, byte[] data, int offset)

Read 128-bit

static int read128Array(BigInteger[] result, byte[] data, int offset)
static BigInteger[] read128Vector(byte[] data, int offset)

128-bit Length

static int len128Array(BigInteger[] array)
return
int
array.length * 16
static int len128Vector(BigInteger[] array)
return
int
4 + (array.length * 16)

Multi-Dimensional Arrays

Read Multi-Dimensional

static int[][] readMultiDimensionintVector(byte[] data, int offset)
static String[][] readMultiDimensionStringVector(byte[] data, int offset)
static byte[][] readMultiDimensionbyteVector(byte[] data, int offset)

Write Multi-Dimensional

static int writeVector(int[][] array, byte[] data, int offset)
static int writeVector(String[][] array, byte[] data, int offset)
static int writeVector(byte[][] array, byte[] data, int offset)

Multi-Dimensional Length

static int lenVector(int[][] array)
static int lenVector(String[][] array)

Example Usage

Serialize Struct

import software.sava.core.borsh.Borsh;
import software.sava.core.accounts.PublicKey;

// Define data
String name = "My Token";
long amount = 1_000_000_000L;
PublicKey owner = PublicKey.fromBase58Encoded("...");
boolean initialized = true;

// Calculate size
int size = Borsh.len(name)
    + Long.BYTES
    + PublicKey.PUBLIC_KEY_LENGTH
    + 1;

// Serialize
byte[] data = new byte[size];
int offset = 0;
offset += Borsh.write(name, data, offset);
ByteUtil.putInt64LE(data, offset, amount);
offset += Long.BYTES;
offset += owner.write(data, offset);
Borsh.write(initialized, data, offset);

Deserialize Struct

// Deserialize
int offset = 0;
String name = Borsh.readString(data, offset);
offset += Borsh.len(name);

long amount = ByteUtil.getInt64LE(data, offset);
offset += Long.BYTES;

PublicKey owner = PublicKey.readPubKey(data, offset);
offset += PublicKey.PUBLIC_KEY_LENGTH;

boolean initialized = data[offset] == 1;

Serialize Vector

// Write vector of integers
int[] values = {100, 200, 300, 400, 500};
int size = Borsh.lenVector(values); // 4 + (5 * 4) = 24

byte[] data = new byte[size];
Borsh.writeVector(values, data, 0);

// Read vector
int[] deserialized = Borsh.readintVector(data, 0);

Serialize Optional

import java.util.OptionalLong;

// Write optional value
OptionalLong someValue = OptionalLong.of(12345);
OptionalLong noneValue = OptionalLong.empty();

byte[] data1 = new byte[Borsh.lenOptional(someValue)];
Borsh.writeOptional(someValue, data1, 0);
// data1 = [1, 57, 48, 0, 0, 0, 0, 0, 0]

byte[] data2 = new byte[Borsh.lenOptional(noneValue)];
Borsh.writeOptional(noneValue, data2, 0);
// data2 = [0]

Serialize Strings

// Write string vector
String[] names = {"Alice", "Bob", "Charlie"};
int size = Borsh.lenVector(names);

byte[] data = new byte[size];
Borsh.writeVector(names, data, 0);

// Read string vector
String[] deserialized = Borsh.readStringVector(data, 0);

Borsh Format Rules

Primitives

  • All integers are little-endian
  • bool: 1 byte (0 or 1)
  • u8/i8: 1 byte
  • u16/i16: 2 bytes
  • u32/i32: 4 bytes
  • u64/i64: 8 bytes
  • u128/i128: 16 bytes
  • f32: 4 bytes (IEEE 754)
  • f64: 8 bytes (IEEE 754)

Strings

[4-byte length][UTF-8 bytes]

Vectors (Dynamic Arrays)

[4-byte length][elements...]

Fixed Arrays

[elements...]
(No length prefix)

Optionals

[1-byte discriminator][value if discriminator == 1]
  • 0 = None
  • 1 = Some(value)

Structs

Fields serialized in order, no padding:
[field1][field2][field3]...

Build docs developers (and LLMs) love