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)
Byte array containing serialized string
Deserialized UTF-8 string
Write String
static int write(String str, byte[] data, int offset)
Number of bytes written (4 + UTF-8 byte length)
String Length
static int len(String val)
Serialized length in bytes (4-byte length + UTF-8 bytes)
static int lenOptional(String val)
1 if null, otherwise 1 + len(val)
String Vectors
static String[] readStringVector(byte[] data, int offset)
Array of strings from vector
static int writeVector(String[] array, byte[] data, int offset)
static int lenVector(String[] array)
Integer Methods
Write Integers
static int writeArray(int[] array, byte[] data, int offset)
static int writeVector(int[] array, byte[] data, int offset)
static int writeArrayChecked(
int[] array,
int fixedLength,
byte[] data,
int offset
)
Expected array length (throws if mismatch)
Read Integers
static int readArray(int[] result, byte[] data, int offset)
Pre-allocated array to fill
static int[] readintVector(byte[] data, int offset)
Deserialized integer array from vector
Integer Length
static int lenArray(int[] array)
static int lenVector(int[] array)
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)
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)
Size in bytes (1 for none, 1 + value size for some)
Boolean Methods
Write Boolean
static int write(boolean val, byte[] data, int offset)
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)
array.length (raw bytes, no length prefix)
static int writeVector(byte[] array, byte[] data, int offset)
4 + array.length (with 4-byte length prefix)
static int writeOptionalVector(byte[] bytes, byte[] data, int offset)
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)
Byte Length
static int lenArray(byte[] array)
static int lenVector(byte[] array)
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)
static int lenVector(long[] array)
BigInteger Methods (128-bit)
Write 128-bit
static int write128(BigInteger val, byte[] data, int offset)
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)
static int len128Vector(BigInteger[] array)
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);
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
(No length prefix)
Optionals
[1-byte discriminator][value if discriminator == 1]
Structs
Fields serialized in order, no padding:
[field1][field2][field3]...