msgpack

package module
v0.0.0-...-81d4f84 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 7 Imported by: 0

README

go-ruby-msgpack/msgpack

msgpack — go-ruby-msgpack

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of Ruby's msgpack gem — the deterministic, interpreter-independent MessagePack codec for the Ruby value model. Pack renders a tree of Ruby values to MessagePack bytes and Unpack parses them back, byte-for-byte to the MessagePack spec and the gem — minimal integer widths, the str/bin/array/map/ext families, and the reserved Time extension (type -1) — without any Ruby runtime.

It is the MessagePack backend for go-embedded-ruby, but is a standalone, reusable module with no dependency on the Ruby runtime — a sibling of go-ruby-yaml (Psych), go-ruby-marshal (Marshal), go-ruby-regexp (Onigmo) and go-ruby-erb (ERB).

What it is — and isn't. Encoding and decoding MessagePack for the Ruby value model is fully deterministic and needs no interpreter, so it lives here as pure Go. Binding the bytes to live Ruby objects is the host's job; this library hands back a small, explicit value model (*Map, Bin, Symbol, *Ext, …) the host maps to and from its own objects.

Features

Faithful port of the gem's pack + unpack, validated against the msgpack gem on every supported platform:

  • Minimal integer widths — positive/negative fixint, uint8/16/32/64, int8/16/32/64, each narrowed exactly as the gem does; *big.Int for the full 64-bit unsigned/signed range.
  • Floats — IEEE-754 double (float64), the gem's default for Float; the unpacker also accepts float32 (0xca).
  • Strings vs binary — a Go string (and Symbol) packs as the str family (UTF-8); a Bin / []byte packs as the bin family (ASCII-8BIT), matching how the gem distinguishes String encodings. Symbols pack as strings by default.
  • Arrays & mapsfixarray/array16/array32, fixmap/map16/map32; maps round-trip as an insertion-ordered *Map (a plain Go map packs in a deterministic key order).
  • Extensionsfixext 1/2/4/8/16 and ext 8/16/32, plus the reserved Time extension (type -1) in its 32/64/96-bit forms for time.Time.
  • StreamingPacker accumulates successive Writes; Unpacker reads objects one at a time from a buffer.

CGO-free, dependency-free, 100% test coverage, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x) on Linux, macOS, and Windows.

Install

go get github.com/go-ruby-msgpack/msgpack

Usage

package main

import (
	"fmt"

	"github.com/go-ruby-msgpack/msgpack"
)

func main() {
	m := msgpack.NewMap()
	m.Set("name", "web")
	m.Set("ports", []any{80, 443})
	m.Set("raw", msgpack.Bin([]byte{0xde, 0xad}))

	b, _ := msgpack.Pack(m) // MessagePack.pack — minimal widths, str/bin/map families
	fmt.Printf("%x\n", b)

	v, _ := msgpack.Unpack(b) // MessagePack.unpack — maps come back as *msgpack.Map
	fmt.Printf("%T\n", v)     // *msgpack.Map
}

Ruby value model

Pack / Unpack round-trip an any drawn from a small, fixed set of Go types, so a host can map its own object graph to and from this package:

Ruby Go (Pack accepts) Go (Unpack returns)
nil nil nil
true / false bool bool
Integer int, int8..64, uint8..64, *big.Int int64 / uint64
Float float64, float32 float64
String (UTF-8) string string
String (binary) msgpack.Bin, []byte msgpack.Bin
Symbol msgpack.Symbol string (symbol→str)
Array []any []any
Hash *msgpack.Map, map[string]any, map[any]any *msgpack.Map (ordered)
Time time.Time time.Time (ext -1)
ext *msgpack.Ext *msgpack.Ext

A Go string packs as str (UTF-8) and Bin/[]byte as bin (ASCII-8BIT), since a Go string carries no encoding tag. A plain Go map packs in a deterministic key order; a *msgpack.Map preserves insertion order, and Unpack always returns maps as *msgpack.Map so key order round-trips.

API

// Pack serialises a Ruby value to MessagePack bytes (MessagePack.pack).
func Pack(v any) ([]byte, error)

// Unpack parses MessagePack bytes into a Ruby value (MessagePack.unpack).
func Unpack(b []byte) (any, error)

// Packer is a streaming encoder; Unpacker a streaming decoder.
type Packer struct{ /* ... */ }
func NewPacker() *Packer
func (p *Packer) Write(v any) error
func (p *Packer) Bytes() []byte
func (p *Packer) Reset()

type Unpacker struct{ /* ... */ }
func NewUnpacker(b []byte) *Unpacker
func (u *Unpacker) Read() (any, error)
func (u *Unpacker) Remaining() int
func (u *Unpacker) Reset(b []byte)

type Symbol string
type Bin    []byte
type Ext    struct { Type int8; Payload []byte }
type Map    struct { /* insertion-ordered Hash */ }
func NewMap() *Map
func (m *Map) Set(key, val any)
func (m *Map) Get(key any) (any, bool)
func (m *Map) Pairs() []Pair
func (m *Map) Len() int

Tests & coverage

The suite pairs deterministic, ruby-free golden-byte tests (which alone hold coverage at 100%, so the qemu cross-arch and Windows lanes pass the gate) with a differential gem oracle: a wide corpus is packed here and compared byte-for-byte to MessagePack::Factory#dump, and gem-packed bytes are unpacked here — round-tripping integers (every minimal-width boundary), str/bin, arrays, ordered maps, and the Time extension in both directions. The oracle scripts $stdout.binmode and skip themselves where ruby (or the msgpack gem) is absent.

COVERPKG=$(go list ./... | paste -sd, -)
go test -race -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # 100.0%

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-msgpack/msgpack authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package msgpack is a pure-Go (CGO-free) MRI-faithful MessagePack codec for the Ruby value model. It is the deterministic, interpreter-independent core of Ruby's `msgpack` gem: Pack renders a tree of Ruby values to MessagePack bytes and Unpack parses such bytes back, so Unpack(Pack(x)) round-trips the values Ruby programs serialise — byte-for-byte to the MessagePack spec and the gem, without any Ruby runtime.

Ruby value model

A Ruby value is represented by an [any] drawn from a small, fixed set of Go types so a host (such as go-embedded-ruby) can map its own object graph to and from this package:

Ruby            Go (Pack accepts)               Go (Unpack returns)
----            -----------------               -------------------
nil             nil                             nil
true / false    bool                            bool
Integer         int, int8..64, uint8..64        int64 or uint64
Float           float64, float32                float64
String (UTF-8)  string                          string
String (binary) Bin([]byte), []byte             Bin (ASCII-8BIT bytes)
Symbol          Symbol                          string (symbol→str default)
Array           []any                           []any
Hash            *Map (ordered), map[...]any      *Map (insertion order)
Time            time.Time                       time.Time (ext type -1)
ext             *Ext (type, payload)            *Ext

Pack also accepts a plain Go map[string]any / map[any]any (sorted by a stable key order for determinism); Unpack always yields an ordered *Map for a map so key order is preserved on round-trip.

Strings carry no encoding tag in Go, so Pack maps a Go string to the MessagePack str family (UTF-8) and a Bin / []byte to the bin family (ASCII-8BIT), matching how the gem distinguishes String encodings.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Pack

func Pack(v Value) ([]byte, error)

Pack serialises a Ruby value to MessagePack bytes, matching MessagePack.pack / Object#to_msgpack from the `msgpack` gem. v is drawn from the package value model (see the package doc); a value outside that model returns an error rather than panicking. Integers use the minimal MessagePack width, strings use the str family (UTF-8) and Bin/[]byte the bin family, and time.Time uses the reserved Time extension (type -1), each byte-for-byte to the gem.

Types

type Bin

type Bin []byte

Bin is a Ruby binary (ASCII-8BIT) String. Pack emits it as the MessagePack bin family and Unpack returns a Bin for the bin family, distinguishing it from a UTF-8 str (which maps to a Go string).

type Ext

type Ext struct {
	Type    int8
	Payload []byte
}

Ext is a MessagePack extension object: an application-defined type byte and a raw payload. The reserved Time type is -1; Pack/Unpack handle time.Time via that type directly, so a host uses Ext for any other extension.

type Map

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

Map is an insertion-ordered Ruby Hash. Unpack returns maps as *Map so key order round-trips; Pack accepts *Map, a plain Go map (emitted in a stable key order), or a []Pair via NewMap.

func NewMap

func NewMap() *Map

NewMap returns an empty ordered Map.

func (*Map) Get

func (m *Map) Get(key Value) (Value, bool)

Get returns the value for a comparable key and whether it was present.

func (*Map) Len

func (m *Map) Len() int

Len reports the number of entries.

func (*Map) Pairs

func (m *Map) Pairs() []Pair

Pairs returns the entries in insertion order. The slice must not be mutated.

func (*Map) Set

func (m *Map) Set(key, val Value)

Set inserts or replaces the entry for key. A comparable key replaces an existing equal key; a non-comparable key (slice / map …) is always appended.

type Packer

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

Packer is a streaming MessagePack encoder. Successive Write calls append their encodings to an internal buffer; Bytes returns the accumulated output. The zero Packer is ready to use. It mirrors the gem's MessagePack::Packer.

func NewPacker

func NewPacker() *Packer

NewPacker returns a fresh, empty Packer.

func (*Packer) Bytes

func (p *Packer) Bytes() []byte

Bytes returns the bytes written so far. The slice aliases the internal buffer until the next Write; copy it if it must outlive further writes.

func (*Packer) Reset

func (p *Packer) Reset()

Reset discards any buffered output, readying the Packer for reuse.

func (*Packer) Write

func (p *Packer) Write(v Value) error

Write encodes one Ruby value and appends it to the buffer, matching the gem's Packer#write. A value outside the package model returns an error.

type Pair

type Pair struct {
	Key Value
	Val Value
}

Pair is one entry of an ordered map.

type Symbol

type Symbol string

Symbol is a Ruby Symbol (`:name`). By default the gem packs a Symbol as a str, so Pack emits Symbol as the str family and Unpack returns a plain string.

type Unpacker

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

Unpacker is a streaming MessagePack decoder over a byte buffer. Successive Read calls decode the next object; off advances past consumed bytes. It mirrors the gem's MessagePack::Unpacker.

func NewUnpacker

func NewUnpacker(b []byte) *Unpacker

NewUnpacker returns an Unpacker reading from b. The buffer is not copied.

func (*Unpacker) Read

func (u *Unpacker) Read() (Value, error)

Read decodes the next MessagePack object from the buffer.

func (*Unpacker) Remaining

func (u *Unpacker) Remaining() int

Remaining reports how many unread bytes are left in the buffer.

func (*Unpacker) Reset

func (u *Unpacker) Reset(b []byte)

Reset points the Unpacker at a fresh buffer, readying it for reuse.

type Value

type Value = any

Value is the interface satisfied by every Ruby value this package handles. It is purely documentary — the public API uses any — but a host may use it to constrain its own adapters.

func Unpack

func Unpack(b []byte) (Value, error)

Unpack parses MessagePack bytes into a Ruby value, matching MessagePack.unpack / MessagePack.load. Maps come back as an ordered *Map (key order preserved), arrays as []any, the str family as a Go string, the bin family as Bin, the Time extension as time.Time, and any other extension as *Ext. Trailing bytes after the first object are an error (use Unpacker to read a stream).

Jump to

Keyboard shortcuts

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