Documentation
¶
Overview ¶
Package encoding provides Solana wire-format serialization primitives.
bincode.go implements the little-endian binary codec used for Solana transaction and message encoding, including Solana's compact-u16 (shortvec) length-prefix format.
Index ¶
- Variables
- func BinDecodeTo(data []byte, v any) error
- func BorshDecodeTo(data []byte, v any) error
- func DecodeShortvec(b []byte) (uint16, int, error)
- func EncodeShortvec(b []byte, v uint16) int
- func RegisterDecoder[T any](fn func(*Decoder, *T) error)
- type Decoder
- func (d *Decoder) DecodeFast(v any) error
- func (d *Decoder) DecodeTo(v any) error
- func (d *Decoder) PeekUint8() (uint8, error)
- func (d *Decoder) Pos() int
- func (d *Decoder) Position() int
- func (d *Decoder) ReadBytes(n int) ([]byte, error)
- func (d *Decoder) ReadInt8() (int8, error)
- func (d *Decoder) ReadInt16() (int16, error)
- func (d *Decoder) ReadInt32() (int32, error)
- func (d *Decoder) ReadInt64() (int64, error)
- func (d *Decoder) ReadShortvec() (uint16, error)
- func (d *Decoder) ReadU128() (U128, error)
- func (d *Decoder) ReadU256() (U256, error)
- func (d *Decoder) ReadUint8() (uint8, error)
- func (d *Decoder) ReadUint16() (uint16, error)
- func (d *Decoder) ReadUint32() (uint32, error)
- func (d *Decoder) ReadUint64() (uint64, error)
- func (d *Decoder) Remaining() int
- func (d *Decoder) UseBorsh() *Decoder
- type Encoder
- func (e *Encoder) Bool(v bool) *Encoder
- func (e *Encoder) Bytes() []byte
- func (e *Encoder) Discriminator(d [8]byte) *Encoder
- func (e *Encoder) I8(v int8) *Encoder
- func (e *Encoder) I16(v int16) *Encoder
- func (e *Encoder) I32(v int32) *Encoder
- func (e *Encoder) I64(v int64) *Encoder
- func (e *Encoder) Len() int
- func (e *Encoder) OptBool(v *bool) *Encoder
- func (e *Encoder) OptI64(v *int64) *Encoder
- func (e *Encoder) OptRaw(b []byte) *Encoder
- func (e *Encoder) OptU8(v *uint8) *Encoder
- func (e *Encoder) OptU32(v *uint32) *Encoder
- func (e *Encoder) OptU64(v *uint64) *Encoder
- func (e *Encoder) OptU128(v *U128) *Encoder
- func (e *Encoder) Raw(b []byte) *Encoder
- func (e *Encoder) Reset()
- func (e *Encoder) StrU64(s string) *Encoder
- func (e *Encoder) U8(v uint8) *Encoder
- func (e *Encoder) U16(v uint16) *Encoder
- func (e *Encoder) U32(v uint32) *Encoder
- func (e *Encoder) U64(v uint64) *Encoder
- func (e *Encoder) U128c(v U128) *Encoder
- func (e *Encoder) U256c(v U256) *Encoder
- func (e *Encoder) WriteBytes(b []byte)
- func (e *Encoder) WriteInt8(v int8)
- func (e *Encoder) WriteInt16(v int16)
- func (e *Encoder) WriteInt32(v int32)
- func (e *Encoder) WriteInt64(v int64)
- func (e *Encoder) WriteShortvec(v uint16)
- func (e *Encoder) WriteU128(u U128)
- func (e *Encoder) WriteU256(u U256)
- func (e *Encoder) WriteUint8(v uint8)
- func (e *Encoder) WriteUint16(v uint16)
- func (e *Encoder) WriteUint32(v uint32)
- func (e *Encoder) WriteUint64(v uint64)
- type Reader
- func (r *Reader) Bool() bool
- func (r *Reader) Bytes(n int) []byte
- func (r *Reader) Bytes32() [32]byte
- func (r *Reader) Bytes64() [64]byte
- func (r *Reader) Decoder() *Decoder
- func (r *Reader) Done() error
- func (r *Reader) Err() error
- func (r *Reader) I8() int8
- func (r *Reader) I16() int16
- func (r *Reader) I32() int32
- func (r *Reader) I64() int64
- func (r *Reader) Pos() int
- func (r *Reader) Read(out []byte)
- func (r *Reader) Remaining() int
- func (r *Reader) Skip(n int)
- func (r *Reader) StrU64() string
- func (r *Reader) U8() uint8
- func (r *Reader) U16() uint16
- func (r *Reader) U32() uint32
- func (r *Reader) U64() uint64
- func (r *Reader) U128() U128
- func (r *Reader) U256() U256
- type TrailingBytesError
- type U128
- func (u U128) BigInt() *big.Int
- func (u U128) Hi() uint64
- func (u U128) IsZero() bool
- func (u U128) Lo() uint64
- func (u U128) MarshalJSON() ([]byte, error)
- func (u U128) MarshalText() ([]byte, error)
- func (u *U128) SetBigInt(x *big.Int) error
- func (u *U128) SetLoHi(lo, hi uint64)
- func (u *U128) SetUint64(v uint64)
- func (u U128) String() string
- func (u *U128) UnmarshalJSON(b []byte) error
- func (u *U128) UnmarshalText(b []byte) error
- type U256
- func (u U256) BigInt() *big.Int
- func (u U256) IsZero() bool
- func (u U256) MarshalJSON() ([]byte, error)
- func (u U256) MarshalText() ([]byte, error)
- func (u *U256) SetBigInt(x *big.Int) error
- func (u *U256) SetWord(i int, v uint64)
- func (u U256) String() string
- func (u *U256) UnmarshalJSON(b []byte) error
- func (u *U256) UnmarshalText(b []byte) error
- func (u U256) Word(i int) uint64
- type Unmarshaler
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidShortvec = errors.New("solana/encoding: invalid shortvec")
ErrInvalidShortvec is returned when a compact-u16 encoding is structurally invalid: either the third byte sets the continuation bit (attempting a 4-byte encoding) or its value would overflow the destination uint16. The canonical Solana shortvec is 1-3 bytes and the third byte must be in the range 0x00-0x03.
var ErrShortBuffer = errors.New("solana/encoding: short buffer")
ErrShortBuffer is returned when a Decoder runs out of bytes mid-value or when a caller asks for a negative read length.
Functions ¶
func BinDecodeTo ¶ added in v0.1.3
BinDecodeTo is a one-shot bincode reflection decoder: equivalent to NewDecoder(data).DecodeTo(v). Use this when you have a struct tagged with bin:"..." and just want to parse a buffer into it. For fixed-shape, performance-sensitive decoders prefer the Reader API. The Borsh counterpart is BorshDecodeTo.
func BorshDecodeTo ¶
BorshDecodeTo decodes data into v using Borsh prefix conventions. It is the Borsh counterpart to BinDecodeTo.
func DecodeShortvec ¶
DecodeShortvec reads a compact-u16 from b and returns the decoded value, the number of bytes consumed, and an error. The error is ErrShortBuffer when b is truncated mid-value, and ErrInvalidShortvec when the encoding is structurally invalid (fourth-byte continuation or uint16 overflow).
func EncodeShortvec ¶
EncodeShortvec writes v into b using Solana's compact-u16 (shortvec) encoding and returns the number of bytes written. b must have length at least 3. The encoding is 1-3 bytes: each byte holds 7 data bits in the low bits and a continuation flag in the high bit.
func RegisterDecoder ¶
RegisterDecoder associates a hand-written decoder with type T. When Decode encounters a value of type T — directly, as a struct field, slice element, pointer target, or inside a larger containing type — fn is called instead of walking the type reflectively.
Intended use is during package init. RegisterDecoder is safe for concurrent use, but registering a second decoder for the same type replaces the first.
The wrapper handles the unsafe.Pointer → *T conversion so callers write normal typed Go.
When registration helps vs hurts, by type shape:
Leaf [N]byte-backed named types (PublicKey, Hash, Signature, U128, U256, …): do NOT register. The reflective fast path emits opFixedBytes for them — a single ReadBytes(N) + memmove the compiler inlines through the op switch. opCallFunc dispatches through a function pointer that cannot inline, so leaf registration is 1.5–5% SLOWER under concurrent load (the gap widens with goroutine count due to BTB pressure).
Large composite structs (10+ exported fields, account-shaped): registration CAN help. The reflective walker pays per-field op-dispatch cost; a hand-written closure that reads the whole struct in one shot amortises that to zero. Bench shows 5–15% speedup on a ~30-field AMM pool struct (see poolstate_decode_bench_test.go for methodology). Whether the win materialises depends on the field-count vs decode-cost ratio — bench before committing.
Custom wire formats / layout transformations / third-party types that can't implement Unmarshaler / post-decode validation: register without question — no other path expresses these.
Types ¶
type Decoder ¶
type Decoder struct {
// contains filtered or unexported fields
}
Decoder is a zero-copy reader for Solana's little-endian wire format. It holds a cursor into a caller-owned byte slice; ReadBytes returns slices aliasing that buffer.
func NewBinDecoder ¶
NewBinDecoder returns a Decoder reading from data. It is an alias for NewDecoder and exists so the gagliardetto/binary idiom
d := encoding.NewBinDecoder(data) d.DecodeTo(&v)
compiles unchanged against this package.
func NewDecoder ¶
NewDecoder returns a Decoder that reads from b. The caller retains ownership of b and must not mutate it for the lifetime of the Decoder or of any slice returned by ReadBytes.
func (*Decoder) DecodeFast ¶
DecodeFast is Decode using a cached, compiled per-type plan.
func (*Decoder) DecodeTo ¶
DecodeTo decodes the next value from the stream into v, which must be a non-nil pointer. It is the entry point for reflection-based decoding and is a drop-in replacement for the gagliardetto/binary NewBinDecoder+Decode pattern.
Before reflecting, DecodeTo consults two escape hatches in order:
- A hand-written decoder registered via RegisterDecoder for the target type — library-provided fast paths for PublicKey, Transaction, etc.
- The Unmarshaler interface — types that implement it are dispatched directly without reflecting into their fields.
For reflected values, the supported kinds are: uint8/16/32/64, int8/16/32/64, bool, [N]byte and [N]T fixed arrays, []byte and []T slices (length-prefixed), string (length-prefixed), pointer (bincode Option: 1-byte tag + optional payload), and struct (recursively, respecting `bin:"..."` field tags).
The default length prefix is Rust's bincode u64. Override on a per-field basis with `bin:"sizePrefix=shortvec"` etc. Top-level slice decoding uses the default; to read a shortvec-prefixed slice at the top level, either wrap it in a struct or call ReadShortvec + a loop manually.
func (*Decoder) Position ¶
Position returns the number of bytes consumed so far. It is an alias for Pos, using the gagliardetto/binary spelling.
func (*Decoder) ReadBytes ¶
ReadBytes returns a zero-copy subslice of n bytes aliasing the underlying buffer. The returned slice is valid only as long as the buffer passed to NewDecoder is valid, and its capacity is clamped to n so that a rogue append cannot overwrite neighbouring bytes.
func (*Decoder) ReadShortvec ¶
ReadShortvec decodes a Solana compact-u16 length prefix and advances the cursor by the number of bytes consumed. On error the cursor is left unchanged.
func (*Decoder) ReadU128 ¶
ReadU128 reads a 16-byte little-endian U128. The bytes are copied, so the returned value is independent of the Decoder's underlying buffer.
func (*Decoder) ReadUint16 ¶
ReadUint16 reads a little-endian uint16.
func (*Decoder) ReadUint32 ¶
ReadUint32 reads a little-endian uint32.
func (*Decoder) ReadUint64 ¶
ReadUint64 reads a little-endian uint64.
type Encoder ¶
type Encoder struct {
// contains filtered or unexported fields
}
Encoder is an append-based writer for Solana's little-endian wire format. It holds a growable byte buffer and never reflects on its arguments.
func New ¶
func New() *Encoder
New returns an Encoder with a 128-byte default capacity — large enough that essentially every Solana instruction encodes without a single buffer re-allocation. Use this when you don't care to compute the exact instruction-data size yourself; reach for NewEncoder(N) only when N is much larger than 128 (e.g. a variable-length address list).
func NewEncoder ¶
NewEncoder returns an Encoder whose internal buffer starts with the given initial capacity. Passing 0 is legal — the buffer grows on first write — but providing an estimate avoids the small re-alloc cost.
func Wrap ¶
Wrap returns an Encoder whose buffer is the slice b truncated to zero length. The underlying array is reused, so callers who want to preserve b's contents must not write through the returned Encoder.
func (*Encoder) Bytes ¶
Bytes returns the accumulated bytes. The returned slice aliases the internal buffer and is valid only until the next write.
func (*Encoder) Discriminator ¶
Discriminator is an 8-byte Anchor instruction discriminator. Identical to Raw(d[:]) but the named method documents intent at the call site.
func (*Encoder) OptRaw ¶
OptRaw writes a Rust Option<[N]byte>: 1-byte tag, then b verbatim when non-nil. Pass nil to encode None. The slice length is not length-prefixed: this is intended for fixed-size optional fields such as Option<Pubkey> via OptRaw(pk[:]).
func (*Encoder) Raw ¶
Raw appends b verbatim (no length prefix). Use for fixed-size byte arrays such as pubkeys (caller passes pk[:]) or pre-encoded discriminators.
func (*Encoder) Reset ¶
func (e *Encoder) Reset()
Reset truncates the buffer length to zero, keeping the allocated capacity so the Encoder can be reused for a new encoding run.
func (*Encoder) StrU64 ¶ added in v0.1.3
StrU64 writes a bincode string: u64 little-endian length, then UTF-8 bytes. For Borsh strings (u32 length) compose e.U32(uint32(len(s))).Raw([]byte(s)).
func (*Encoder) WriteBytes ¶
WriteBytes appends b verbatim, without any length prefix.
func (*Encoder) WriteInt16 ¶
WriteInt16 appends a little-endian int16.
func (*Encoder) WriteInt32 ¶
WriteInt32 appends a little-endian int32.
func (*Encoder) WriteInt64 ¶
WriteInt64 appends a little-endian int64.
func (*Encoder) WriteShortvec ¶
WriteShortvec appends a Solana compact-u16 length prefix (1-3 bytes), encoding v using EncodeShortvec.
func (*Encoder) WriteUint8 ¶
WriteUint8 appends a single byte.
func (*Encoder) WriteUint16 ¶
WriteUint16 appends a little-endian uint16.
func (*Encoder) WriteUint32 ¶
WriteUint32 appends a little-endian uint32.
func (*Encoder) WriteUint64 ¶
WriteUint64 appends a little-endian uint64.
type Reader ¶
type Reader struct {
// contains filtered or unexported fields
}
Reader is a sticky-error wrapper around Decoder for chained, fluent decoding of fixed-shape Solana wire data. Each accessor reads one field; the first short-buffer (or invalid-shortvec) error is stored on the Reader and silences subsequent reads — so callers can sequence the happy path without per-call err checks and check Err() once at the end.
r := encoding.NewReader(data)
flag := r.U32()
var pk [32]byte
r.Read(pk[:])
supply := r.U64()
decimals := r.U8()
if err := r.Err(); err != nil {
return nil, err
}
The underlying Decoder is exposed via Decoder() for callers who need shortvec, position, or the reflective Decode path mid-stream.
func (*Reader) Bytes ¶
Bytes reads exactly n bytes and returns them as a fresh, independent slice (the data is copied out of the underlying buffer). Use Read when you already have a destination slice.
func (*Reader) Bytes32 ¶
Bytes32 reads exactly 32 bytes and returns them by value. Convenient for fields backed by solana.PublicKey or solana.Hash, which are both [32]byte.
func (*Reader) Bytes64 ¶
Bytes64 reads exactly 64 bytes and returns them by value. Convenient for fields backed by solana.Signature, which is [64]byte.
func (*Reader) Decoder ¶
Decoder returns the underlying Decoder. Mutations through it are visible to subsequent Reader calls.
func (*Reader) Done ¶
Done returns nil if all bytes were consumed exactly. If a sticky read error is pending it returns that. Otherwise, if any unread bytes remain, it returns a *TrailingBytesError reporting how many.
Use this to enforce "no extra data" at the end of a decode. Callers that want to allow trailing data should check Err() instead.
func (*Reader) Err ¶
Err returns the first error encountered, or nil. Once non-nil, all subsequent reads are no-ops and return zero values.
func (*Reader) Read ¶
Read fills out from the stream. The slice length determines how many bytes are consumed; passing pubkey[:] reads exactly 32 bytes.
func (*Reader) StrU64 ¶ added in v0.1.3
StrU64 reads a bincode string: u64 little-endian length, then UTF-8 bytes. For Borsh strings (u32 length) compose r.U32() + r.Bytes(int(n)).
type TrailingBytesError ¶
type TrailingBytesError struct {
// Remaining is the number of unread bytes at the end of the buffer.
Remaining int
}
TrailingBytesError is returned by Reader.Done when the buffer was not fully consumed. Match it via errors.As:
var te *encoding.TrailingBytesError
if errors.As(err, &te) {
// log te.Remaining, recover, etc.
}
func (*TrailingBytesError) Error ¶
func (e *TrailingBytesError) Error() string
type U128 ¶
type U128 [16]byte
U128 is a 128-bit unsigned integer stored in Rust / Solana little-endian byte order: u[0] is the least-significant byte, u[15] the most-significant. This matches the wire representation, so ReadU128 / WriteU128 are a single 16-byte memcpy on little-endian hosts.
func (U128) MarshalJSON ¶
MarshalJSON emits u as a JSON *string* decimal, so JavaScript consumers that lose precision above 2^53 still receive the exact value.
func (U128) MarshalText ¶
MarshalText implements encoding.TextMarshaler (decimal string).
func (*U128) SetBigInt ¶
SetBigInt sets u from x, which must be non-negative and fit in 128 bits. Returns an error otherwise; u is left unchanged on error.
func (*U128) UnmarshalJSON ¶
UnmarshalJSON accepts either a JSON string or a JSON number encoding a non-negative decimal U128.
func (*U128) UnmarshalText ¶
UnmarshalText parses a decimal U128 produced by MarshalText.
type U256 ¶
type U256 [32]byte
U256 is a 256-bit unsigned integer in Rust / Solana little-endian byte order (u[0] least-significant, u[31] most-significant). Used by SPL programs that expose u256 fields (oracle prices, curve parameters, some concentrated-liquidity math) and by Ethereum-style bridged state.
func (U256) MarshalJSON ¶
MarshalJSON emits u as a JSON string decimal. See U128.MarshalJSON.
func (U256) MarshalText ¶
MarshalText implements encoding.TextMarshaler (decimal string).
func (*U256) SetBigInt ¶
SetBigInt sets u from x, which must be non-negative and fit in 256 bits. Returns an error otherwise; u is left unchanged on error.
func (*U256) UnmarshalJSON ¶
UnmarshalJSON accepts either a JSON string or a JSON number encoding a non-negative decimal U256.
func (*U256) UnmarshalText ¶
UnmarshalText parses a decimal U256 produced by MarshalText.
type Unmarshaler ¶
Unmarshaler lets a type plug in hand-written decoding logic and bypass reflection entirely. Types whose addressable value satisfies this interface are dispatched directly by Decode; reflection never descends into them.