bincodec

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2026 License: MIT Imports: 3 Imported by: 0

README

bincodec

Zero-reflection binary codec for Go with schema-driven serialization, designed for compact state and protocol data.

中文文档

Features

  • Zero reflection — no reflect, no struct tags parsed at runtime, no interface{} dispatch
  • Fixed-width typesuint8/16/32/64, int32/64, bool, []byte (cross-platform consistent)
  • Big-endian — network byte order, deterministic output
  • Schema-driven — objects declare their field layout; codec handles encode/decode automatically
  • Code generationbincodec-gen generates BinaryObject implementations from struct definitions via go:generate
  • Near hand-written performance — the only overhead is a switch on field type (compiler-optimized jump table)

Install

go get github.com/iamkivae/bincodec

Quick Start

Using BinaryObject interface
type cursorState struct {
    Version  uint8
    Offset   uint32
    Finished bool
}

// Implement BinaryObject (or use code generation, see below)
func (s *cursorState) Schema() []bincodec.FieldDef {
    return []bincodec.FieldDef{bincodec.FUint8(), bincodec.FUint32(), bincodec.FBool()}
}

func (s *cursorState) Get(i int) bincodec.Value {
    switch i {
    case 0: return bincodec.Uint8Val(s.Version)
    case 1: return bincodec.Uint32Val(s.Offset)
    case 2: return bincodec.BoolVal(s.Finished)
    default: return bincodec.Value{}
    }
}

func (s *cursorState) Set(i int, v bincodec.Value) {
    switch i {
    case 0: s.Version = v.AsUint8()
    case 1: s.Offset = v.AsUint32()
    case 2: s.Finished = v.AsBool()
    }
}

// Encode
state := &cursorState{Version: 1, Offset: 42, Finished: false}
data, err := bincodec.Encode(state) // 6 bytes: [01 00 00 00 2A 00]

// Decode
restored := &cursorState{}
err = bincodec.Decode(data, restored)
Using primitives directly
// Encode
enc := bincodec.NewEncoder(16)
enc.PutUint8(1)
enc.PutUint32(1024)
enc.PutBool(true)
enc.PutBytes([]byte("hello"))
data := enc.Bytes()

// Decode
dec := bincodec.NewDecoder(data)
version, _ := dec.Uint8()
offset, _ := dec.Uint32()
flag, _ := dec.Bool()
payload, _ := dec.Bytes()
Code generation

Add a go:generate directive to your struct file:

//go:generate go run github.com/iamkivae/bincodec/generate -type=cursorState
type cursorState struct {
    Version  uint8
    Offset   uint32
    Finished bool
}

Run go generate ./... to produce cursor_state_gen.go with Schema(), Get(), and Set() implementations.

Generated files follow Go naming conventions (snake_case_gen.go) and include the standard // Code generated ... DO NOT EDIT. header.

Binary Layout

All types use big-endian encoding:

Type Size Notes
uint8 1 byte
uint16 2 bytes big-endian
uint32 4 bytes big-endian
uint64 8 bytes big-endian
int32 4 bytes two's complement, big-endian
int64 8 bytes two's complement, big-endian
bool 1 byte 0x00=false, 0x01=true
[]byte 4 + N bytes 4-byte big-endian length prefix + data

Version Compatibility

Include a version field as the first struct field:

type myState struct {
    Version uint8  // always field 0
    // ... other fields
}

After decoding, validate:

if state.Version != expectedVersion {
    return bincodec.ErrVersionMismatch
}

Adding new fields in future versions is safe — append to the struct and regenerate. Older decoders will return ErrBufferTooShort if the data is shorter than expected.

Performance

  • No reflection, no JSON, no interface{} dynamic dispatch
  • Value is a 24-byte fixed struct (stack-allocated)
  • Schema() returns a package-level static variable (no per-call allocation)
  • Encode/Decode path is a single switch on ValueType
  • Benchmarks show performance within 5% of hand-written binary encoding

License

MIT

Documentation

Overview

Package bincodec provides a lightweight fixed-width binary encoding/decoding toolkit.

Design goals:

  • Zero reflection, zero JSON, zero interface{} dynamic dispatch
  • Fixed-width types (cross-platform safe: amd64/arm64/32bit consistent)
  • Big-endian byte order (network byte order)
  • Near hand-written binary encoding performance
  • Minimal memory allocation

Use cases:

  • Pagination cookie encoding/decoding
  • Backend-defined cursor state
  • Internal tokens and binary state

This package is NOT a general-purpose serialization framework. Each consumer defines its own field order and semantics via the BinaryObject interface or hand-written Encode/Decode using the primitives.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrBufferTooShort is returned when the decoder runs out of data.
	ErrBufferTooShort = errors.New("bincodec: buffer too short")
	// ErrVersionMismatch is returned when the version field does not match.
	ErrVersionMismatch = errors.New("bincodec: version mismatch")
)

Predefined errors.

Functions

func Decode

func Decode(data []byte, obj BinaryObject) error

Decode deserializes data into obj following the field order defined by obj.Schema().

Returns ErrBufferTooShort if data is insufficient. Zero reflection, zero JSON, zero dynamic types.

func Encode

func Encode(obj BinaryObject) ([]byte, error)

Encode serializes obj into a binary byte slice following the field order defined by obj.Schema().

Zero reflection, zero JSON, zero dynamic types. Performance is near hand-written binary encoding.

Types

type BinaryObject

type BinaryObject interface {
	// Schema returns the field definition list. List order is the binary encoding order.
	Schema() []FieldDef

	// Get returns the value of the field at the given index. Called during Encode.
	Get(index int) Value

	// Set sets the value of the field at the given index. Called during Decode.
	Set(index int, v Value)
}

BinaryObject declares its own field layout; the codec handles encoding/decoding automatically.

Implementors declare field order and types via Schema(), and provide indexed access to field values via Get/Set.

Example:

type myState struct {
    Offset   uint32
    Finished bool
}

func (s *myState) Schema() []bincodec.FieldDef {
    return []bincodec.FieldDef{bincodec.FUint32(), bincodec.FBool()}
}

func (s *myState) Get(i int) bincodec.Value {
    switch i {
    case 0: return bincodec.Uint32Val(s.Offset)
    case 1: return bincodec.BoolVal(s.Finished)
    default: return bincodec.Value{}
    }
}

func (s *myState) Set(i int, v bincodec.Value) {
    switch i {
    case 0: s.Offset = v.AsUint32()
    case 1: s.Finished = v.AsBool()
    }
}

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

Decoder reads fixed-width fields in big-endian order from a byte slice.

All read methods return ErrBufferTooShort when data is insufficient. Callers should stop decoding after the first error.

Usage:

dec := bincodec.NewDecoder(data)
version, err := dec.Uint8()
offset, err := dec.Uint32()
finished, err := dec.Bool()

func CheckVersion

func CheckVersion(data []byte, expected uint8) (*Decoder, error)

CheckVersion reads the first byte as a version number and compares it to expected. Returns ErrVersionMismatch if they differ.

func NewDecoder

func NewDecoder(data []byte) *Decoder

NewDecoder creates a decoder over the given data.

func (*Decoder) Bool

func (d *Decoder) Bool() (bool, error)

Bool reads a 1-byte boolean.

func (*Decoder) Bytes

func (d *Decoder) Bytes() ([]byte, error)

Bytes reads a variable-length byte slice (4-byte big-endian length prefix + data). The returned slice is a sub-slice of the original data (zero-copy); callers must not modify it.

func (*Decoder) Int32

func (d *Decoder) Int32() (int32, error)

Int32 reads a 4-byte big-endian signed integer.

func (*Decoder) Int64

func (d *Decoder) Int64() (int64, error)

Int64 reads an 8-byte big-endian signed integer.

func (*Decoder) Pos

func (d *Decoder) Pos() int

Pos returns the current read position.

func (*Decoder) Remaining

func (d *Decoder) Remaining() int

Remaining returns the number of unread bytes.

func (*Decoder) RemainingBytes

func (d *Decoder) RemainingBytes() []byte

RemainingBytes returns the unread portion as a byte slice (zero-copy).

func (*Decoder) String added in v0.1.1

func (d *Decoder) String() (string, error)

String reads a variable-length UTF-8 string (4-byte big-endian length prefix + data).

func (*Decoder) Uint8

func (d *Decoder) Uint8() (uint8, error)

Uint8 reads a 1-byte unsigned integer.

func (*Decoder) Uint16

func (d *Decoder) Uint16() (uint16, error)

Uint16 reads a 2-byte big-endian unsigned integer.

func (*Decoder) Uint32

func (d *Decoder) Uint32() (uint32, error)

Uint32 reads a 4-byte big-endian unsigned integer.

func (*Decoder) Uint64

func (d *Decoder) Uint64() (uint64, error)

Uint64 reads an 8-byte big-endian unsigned integer.

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

Encoder writes fixed-width fields in big-endian order to an internal buffer.

Usage:

enc := bincodec.NewEncoder(16)
enc.PutUint8(1)
enc.PutUint32(offset)
enc.PutBool(finished)
data := enc.Bytes()

func NewEncoder

func NewEncoder(capacity int) *Encoder

NewEncoder creates an encoder with pre-allocated capacity (reduces reallocation).

func (*Encoder) Bytes

func (e *Encoder) Bytes() []byte

Bytes returns the encoded byte slice.

func (*Encoder) Len

func (e *Encoder) Len() int

Len returns the number of bytes encoded so far.

func (*Encoder) PutBool

func (e *Encoder) PutBool(v bool)

PutBool writes a 1-byte boolean (0x00=false, 0x01=true).

func (*Encoder) PutBytes

func (e *Encoder) PutBytes(v []byte)

PutBytes writes a variable-length byte slice (4-byte big-endian length prefix + data). A nil slice is encoded as length 0.

func (*Encoder) PutInt32

func (e *Encoder) PutInt32(v int32)

PutInt32 writes a 4-byte big-endian signed integer.

func (*Encoder) PutInt64

func (e *Encoder) PutInt64(v int64)

PutInt64 writes an 8-byte big-endian signed integer.

func (*Encoder) PutString added in v0.1.1

func (e *Encoder) PutString(v string)

PutString writes a variable-length UTF-8 string (4-byte big-endian length prefix + data). An empty string is encoded as length 0.

func (*Encoder) PutUint8

func (e *Encoder) PutUint8(v uint8)

PutUint8 writes a 1-byte unsigned integer.

func (*Encoder) PutUint16

func (e *Encoder) PutUint16(v uint16)

PutUint16 writes a 2-byte big-endian unsigned integer.

func (*Encoder) PutUint32

func (e *Encoder) PutUint32(v uint32)

PutUint32 writes a 4-byte big-endian unsigned integer.

func (*Encoder) PutUint64

func (e *Encoder) PutUint64(v uint64)

PutUint64 writes an 8-byte big-endian unsigned integer.

func (*Encoder) Reset

func (e *Encoder) Reset()

Reset clears the buffer (retains the underlying array to avoid reallocation).

type FieldDef

type FieldDef struct {
	Type ValueType
}

FieldDef describes the type of a single field in a BinaryObject. The position in the slice returned by Schema() defines the encoding order.

func FBool

func FBool() FieldDef

func FBytes

func FBytes() FieldDef

func FInt32

func FInt32() FieldDef

func FInt64

func FInt64() FieldDef

func FString added in v0.1.1

func FString() FieldDef

func FUint8

func FUint8() FieldDef

func FUint16

func FUint16() FieldDef

func FUint32

func FUint32() FieldDef

func FUint64

func FUint64() FieldDef

type Value

type Value struct {
	// Type identifies which value is currently held.
	Type ValueType
	// Num stores all fixed-width values:
	//   - Uint8/16/32/64: stored directly
	//   - Int32/64: stored as two's complement uint64
	//   - Bool: 0=false, 1=true
	Num uint64
	// Data is used only for TypeBytes (variable-length byte slice).
	Data []byte
	// Str is used only for TypeString (variable-length UTF-8 string).
	Str string
}

Value is a fixed-layout tagged union holding a single field's value.

Non-Bytes types are zero-allocation (only the Num field is used). No interface{}, no reflection, no dynamic type assertion.

func BoolVal

func BoolVal(v bool) Value

func BytesVal

func BytesVal(v []byte) Value

func Int32Val

func Int32Val(v int32) Value

func Int64Val

func Int64Val(v int64) Value

func StringVal added in v0.1.1

func StringVal(v string) Value

func Uint8Val

func Uint8Val(v uint8) Value

func Uint16Val

func Uint16Val(v uint16) Value

func Uint32Val

func Uint32Val(v uint32) Value

func Uint64Val

func Uint64Val(v uint64) Value

func (Value) AsBool

func (v Value) AsBool() bool

func (Value) AsBytes

func (v Value) AsBytes() []byte

func (Value) AsInt32

func (v Value) AsInt32() int32

func (Value) AsInt64

func (v Value) AsInt64() int64

func (Value) AsString added in v0.1.1

func (v Value) AsString() string

func (Value) AsUint8

func (v Value) AsUint8() uint8

func (Value) AsUint16

func (v Value) AsUint16() uint16

func (Value) AsUint32

func (v Value) AsUint32() uint32

func (Value) AsUint64

func (v Value) AsUint64() uint64

type ValueType

type ValueType uint8

ValueType describes the binary encoding type of a single field in a BinaryObject.

const (
	TypeUint8  ValueType = iota // 1 byte
	TypeUint16                  // 2 bytes big-endian
	TypeUint32                  // 4 bytes big-endian
	TypeUint64                  // 8 bytes big-endian
	TypeInt32                   // 4 bytes big-endian (signed)
	TypeInt64                   // 8 bytes big-endian (signed)
	TypeBool                    // 1 byte (0x00/0x01)
	TypeBytes                   // 4-byte big-endian length prefix + data
	TypeString                  // 4-byte big-endian length prefix + UTF-8 data
)

Directories

Path Synopsis
bincodec-gen 根据 struct 定义自动生成 bincodec.BinaryObject 实现。
bincodec-gen 根据 struct 定义自动生成 bincodec.BinaryObject 实现。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL