Skip to content

encoding.cbor #

Description

encoding.cbor is an RFC 8949 Concise Binary Object Representation codec.

CBOR is a compact, schema-free binary format that supports the same value model as JSON (numbers, strings, arrays, maps) plus byte strings, tagged items, IEEE 754 floats at three widths, and a small set of "simple" values (true, false, null, undefined). It is used by COSE/CWT (IETF security stack), WebAuthn/FIDO2, the Matter smart-home protocol, and many IoT stacks because messages are typically 30–60 % smaller than JSON and parse without quoting/escaping.

Three layers of API are available:

  • encode[T] / decode[T] — comptime-driven generic API. Works on primitives, strings, arrays, maps, structs (with @[cbor: 'name'], @[skip], @[cbor_rename_all: 'snake_case']), enums, time.Time (auto-tagged), and any type implementing Marshaler / Unmarshaler.
  • Packer / Unpacker — manual streaming API. Use when the schema isn't known at compile time, or when you need full control over tags, indefinite-length items and simple values.
  • Value sumtype — dynamic representation for round-tripping unknown payloads or inspecting tagged data.

Defaults follow RFC 8949 preferred serialisation (§4.2.2): floats shrink to the shortest IEEE 754 width that preserves their value, and every length argument uses the shortest encoding. Set EncodeOpts.canonical = true to additionally sort map keys for hash/signature stability (§4.2.1, deterministic encoding). Set EncodeOpts.validate_utf8 = true if callers may build strings from raw bytes (e.g. bytestr()) — the streaming pack_text trusts its input for performance, but encode[T] will then refuse to emit non-UTF-8 text strings the strict-by-default decoder would reject on the way back.

Usage

encode[T] / decode[T]

import encoding.cbor
import time

struct Person {
    name     string
    age      int
    email    ?string
    birthday time.Time
}

fn main() {
    bob := Person{
        name:     'Bob'
        age:      30
        birthday: time.now()
    }

    bytes := cbor.encode[Person](bob, cbor.EncodeOpts{})!
    // bytes is []u8 — wire-ready CBOR

    back := cbor.decode[Person](bytes, cbor.DecodeOpts{})!
    assert back.name == 'Bob'
}

Optional fields (?T) encode as CBOR null when set to none. Enums encode as their underlying integer.

Struct attributes

@[cbor_rename_all: 'kebab-case']
struct Login {
    user_name string @[cbor: 'u'] // emit/read key "u" (overrides rename_all)
    password  string @[skip]      // never serialise
    remember  bool // becomes "remember"
    is_admin  bool // becomes "is-admin"
}

The @[cbor_rename_all: '...'] attribute on a struct applies a global rename strategy to every field that doesn't have an explicit @[cbor: '...'] override — supported strategies: snake_case, camelCase, PascalCase, kebab-case, SCREAMING_SNAKE_CASE. Use @[cbor: '-'] as an alternative to @[skip].

Manual streaming with Packer / Unpacker

Use this when the schema is dynamic or when you need access to CBOR features that don't map directly to V types (tags, indefinite-length strings, custom simple values):

import encoding.cbor

fn main() {
    mut p := cbor.new_packer(cbor.EncodeOpts{})
    p.pack_array_header(3)
    p.pack_uint(42)
    p.pack_text('hello')
    p.pack_bool(true)
    bytes := p.bytes()

    mut u := cbor.new_unpacker(bytes, cbor.DecodeOpts{})
    n := u.unpack_array_header()! // 3
    a := u.unpack_uint()! // 42
    b := u.unpack_text()! // 'hello'
    c := u.unpack_bool()! // true
    _ = n
    _ = a
    _ = b
    _ = c
}

Dynamic values with Value

When the payload schema is unknown at compile time, decode into cbor.Value and walk the sumtype:

import encoding.cbor

fn main() {
    bytes := cbor.encode[map[string]int]({
        'a': 1
        'b': 2
    }, cbor.EncodeOpts{})!

    v := cbor.decode[cbor.Value](bytes, cbor.DecodeOpts{})!
    if val := v.get('a') {
        if i := val.as_int() {
            assert i == 1
        }
    }
}

Value covers every CBOR type: IntNum, FloatNum, Text, Bytes, Array, Map, Tag, Bool, Null, Undefined, Simple. Re-encoding a Value round-trips bit-for-bit when the source was already in preferred form.

Custom Marshaler / Unmarshaler

For types that need a custom on-wire representation, implement either side of the interface:

import encoding.cbor

struct Color {
mut:
    r u8
    g u8
    b u8
}

pub fn (c Color) to_cbor() []u8 {
    mut p := cbor.new_packer(cbor.EncodeOpts{})
    p.pack_array_header(3)
    p.pack_uint(c.r)
    p.pack_uint(c.g)
    p.pack_uint(c.b)
    return p.bytes().clone()
}

pub fn (mut c Color) from_cbor(data []u8) ! {
    mut u := cbor.new_unpacker(data, cbor.DecodeOpts{})
    n := u.unpack_array_header()!
    if n != 3 {
        return error('Color expects 3 elements')
    }
    c.r = u8(u.unpack_uint()!)
    c.g = u8(u.unpack_uint()!)
    c.b = u8(u.unpack_uint()!)
}

to_cbor must return exactly one well-formed CBOR data item — the generic encoder copies the bytes verbatim. from_cbor receives a slice already trimmed to one item.

Canonical (deterministic) encoding

For hashing or signing, set canonical: true so that map keys are sorted by length-then-lexicographic order (RFC 8949 §4.2.1):

import encoding.cbor

bytes := cbor.encode[map[string]int]({
    'b': 2
    'a': 1
}, cbor.EncodeOpts{ canonical: true })!
// keys are emitted in the order "a", "b" regardless of input order

Tags and time.Time

Values of type time.Time round-trip losslessly: whole-second values use tag 1 (epoch seconds, integer) for the smallest canonical wire, and sub-second values use tag 0 (RFC 3339 string with nanosecond precision) — necessary because a tag-1 float can't carry both a 10-digit unix epoch and 9 fractional digits. Decode accepts tag 0 (RFC 3339 text, any sub-second precision) or tag 1 (integer or float). Custom tags can be emitted/read via pack_tag / unpack_tag or by constructing a Value with cbor.new_tag(number, content).

Conformance

The test suite (vlib/encoding/cbor/tests/) covers every vector from RFC 8949 Appendix A, plus indefinite-length strings, depth limits, malformed-input rejection, UTF-8 validation, canonical ordering, and tagged time round-trips.

v test vlib/encoding/cbor/tests/

Constants #

const tag_date_time = u64(0) // RFC 3339 date/time text string
const tag_epoch = u64(1) // POSIX epoch seconds (int or float)
const tag_unsigned_bignum = u64(2) // byte string, big-endian magnitude
const tag_negative_bignum = u64(3) // byte string, -(1 + n)
const tag_decimal_fraction = u64(4) // [exponent, mantissa] with base 10
const tag_bigfloat = u64(5) // [exponent, mantissa] with base 2
const tag_base64url_hint = u64(21)
const tag_base64_hint = u64(22)
const tag_base16_hint = u64(23)
const tag_embedded_cbor = u64(24) // byte string holding well-formed CBOR
const tag_uri = u64(32) // RFC 3986 URI as text string
const tag_base64url = u64(33)
const tag_base64 = u64(34)
const tag_self_describe = u64(55799) // CBOR magic prefix
const self_describe_prefix = [u8(0xd9), 0xd9, 0xf7]

Magic prefix d9d9f7 produced when wrapping any value in tag 55799.

fn decode #

fn decode[T](data []u8, opts DecodeOpts) !T

decode parses CBOR bytes into a value of type T. Rejects extra bytes after the top-level item by default — callers feeding a buffer that holds multiple concatenated items (or that may carry an unrelated suffix) must opt in via DecodeOpts.allow_trailing_bytes = true and drive an Unpacker themselves.

A leading self-describe tag (d9 d9 f7, RFC 8949 §3.4.6) is stripped transparently so payloads encoded with EncodeOpts.self_describe round-trip through decode[T] without the caller having to peel it.

fn decode_from #

fn decode_from[T](mut r io.Reader, opts DecodeOpts) !T

decode_from reads bytes from r until EOF (or until DecodeOpts.max_stream_bytes is hit) and decodes a single top-level value. For multi-value streams, use Unpacker directly on a pre-buffered slice.

Always set max_stream_bytes on untrusted readers — otherwise a peer that never sends EOF blocks the call forever.

fn encode #

fn encode[T](val T, opts EncodeOpts) ![]u8

encode serialises any V value into CBOR bytes. The returned slice owns its backing buffer (V's GC tracks it) — no copy, so the returned bytes are safe to keep across calls and to pass to other modules.

fn encode_to #

fn encode_to[T](val T, mut w io.Writer, opts EncodeOpts) !

encode_to serialises val into an internal buffer, then writes the bytes to w in a loop until everything is accepted. Errors on the first I/O failure.

fn encode_value #

fn encode_value(v Value, opts EncodeOpts) ![]u8

encode_value emits a Value tree to a fresh byte slice with default opts.

fn new_bytes #

fn new_bytes(b []u8) Value

new_bytes wraps a []u8 as a CBOR byte-string Value.

fn new_float #

fn new_float(v f64) Value

new_float wraps an f64 as a CBOR FloatNum that re-encodes at full precision unless f64_to_half / f32 conversion is lossless.

fn new_int #

fn new_int(n i64) Value

new_int wraps a signed i64 in a Value, picking unsigned vs negative.

fn new_negative #

fn new_negative(magnitude u64) Value

new_negative wraps the encoded argument of a major-type-1 value, where the actual integer is -1 - magnitude. Useful when magnitude exceeds i64.

fn new_packer #

fn new_packer(opts EncodeOpts) Packer

new_packer builds a Packer with the given options. opts.initial_cap reserves the buffer up-front; oversize is harmless, undersize triggers the usual growth policy.

fn new_tag #

fn new_tag(number u64, content Value) Value

new_tag wraps an existing Value with a tag number.

fn new_text #

fn new_text(s string) Value

new_text wraps a string as a CBOR text Value.

fn new_uint #

fn new_uint(n u64) Value

new_uint wraps a u64 in a Value (unsigned-int variant).

fn new_unpacker #

fn new_unpacker(data []u8, opts DecodeOpts) Unpacker

new_unpacker constructs an Unpacker over the given byte slice.

fn FloatBits.from #

fn FloatBits.from[W](input W) !FloatBits

fn Kind.from #

fn Kind.from[W](input W) !Kind

interface Marshaler #

interface Marshaler {
	to_cbor() []u8
}

Marshaler lets a user type control its own CBOR encoding. Returned bytes must be exactly one well-formed CBOR data item — the generic encoder copies them verbatim, so malformed output corrupts the surrounding stream.

interface Unmarshaler #

interface Unmarshaler {
mut:
	from_cbor(data []u8) !
}

Unmarshaler is the reverse: given the bytes of one CBOR data item, populate the receiver. The slice is already trimmed to exactly one item by the generic decoder.

Implementers use a mut receiver:

pub fn (mut ip Ipv4) from_cbor(data []u8) ! { ... }

type Value #

type Value = Array
	| Bool
	| Bytes
	| FloatNum
	| IntNum
	| Map
	| Null
	| Simple
	| Tag
	| Text
	| Undefined

Value is the dynamic representation of any CBOR data item. Use it when the schema isn't known at compile time, when you need to inspect tags, or when keys aren't strings:

v := cbor.decodecbor.Value! match v { cbor.Text { println(v.value) } else { ... } }

For known schemas prefer decode[YourStruct] — it's faster and avoids the heap allocations of building a Value tree.

fn (Value) is_nil #

fn (v &Value) is_nil() bool

is_nil returns true if v is the CBOR null value.

fn (Value) is_undefined #

fn (v &Value) is_undefined() bool

is_undefined returns true if v is the CBOR undefined value.

fn (Value) as_int #

fn (v &Value) as_int() ?i64

as_int returns the value as an i64 when it fits, or none otherwise. Returns none for FloatNum, Text, etc.

CBOR negative integers represent -1 - magnitude, so magnitude 2^63 - 1 maps to i64::min and magnitude 2^63 would map to -2^63 - 1 — outside i64 range — hence the strict ">" cutoff for negatives. Use as_uint plus the negative flag to recover the full -2^64..2^64-1 CBOR range.

fn (Value) as_uint #

fn (v &Value) as_uint() ?u64

as_uint returns the value as a u64 if it's a non-negative integer, else none.

fn (Value) as_float #

fn (v &Value) as_float() ?f64

as_float returns the f64 value, or none if v isn't a FloatNum.

fn (Value) as_bool #

fn (v &Value) as_bool() ?bool

as_bool returns the boolean value, or none if v isn't a Bool.

fn (Value) as_string #

fn (v &Value) as_string() ?string

as_string returns the text-string value, or none if v isn't Text.

fn (Value) as_bytes #

fn (v &Value) as_bytes() ?[]u8

as_bytes returns the byte-string payload, or none if v isn't Bytes.

fn (Value) as_array #

fn (v &Value) as_array() ?[]Value

as_array returns the elements of an Array, or none.

fn (Value) as_map #

fn (v &Value) as_map() ?[]MapPair

as_map returns the pairs of a Map, or none.

fn (Value) as_tag #

fn (v &Value) as_tag() ?(u64, Value)

as_tag returns (number, content) of a Tag, or none.

fn (Value) get #

fn (v &Value) get(key string) ?Value

get does a linear lookup of a string-keyed entry in a Map. O(n) — for hot paths decode into a typed struct or map[string]V.

fn (Value) at #

fn (v &Value) at(index int) ?Value

at returns the element at index of an Array.

fn (Value) len #

fn (v &Value) len() int

len returns the length of an Array, Map, Text, or Bytes value, or 0.

enum FloatBits #

enum FloatBits as u8 {
	@none  = 0
	half   = 16
	single = 32
	double = 64
}

FloatBits records which IEEE 754 width the float was originally encoded at. The encoder honours this when re-emitting a Value, so round-tripping preserves the original byte width.

enum Kind #

enum Kind {
	unsigned   // major type 0
	negative   // major type 1
	bytes      // major type 2 (definite or indefinite)
	text       // major type 3 (definite or indefinite)
	array_val  // major type 4 (definite or indefinite)
	map_val    // major type 5 (definite or indefinite)
	tag_val    // major type 6
	bool_val   // simple 20/21
	null_val   // simple 22
	undefined  // simple 23
	simple_val // other simple values
	float_val  // half/single/double
	break_code // 0xff outside a definite header
}

Kind classifies the next item without consuming it. Useful to branch before committing to a typed read.

struct Array #

struct Array {
pub mut:
	elements []Value
}

Array holds the elements of a CBOR array (major type 4).

struct Bool #

struct Bool {
pub:
	value bool
}

Bool is the wrapped form of CBOR true/false (simple values 20/21).

struct Bytes #

struct Bytes {
pub mut:
	data []u8
}

Bytes is a CBOR byte string (major type 2).

struct DecodeOpts #

struct DecodeOpts {
pub:
	max_depth            int = 256
	max_stream_bytes     int // 0 = unbounded for stream readers
	validate_utf8        bool = true
	deny_unknown_fields  bool // struct decode rejects unmapped keys
	deny_duplicate_keys  bool // Map decode rejects repeated keys
	allow_trailing_bytes bool // accept extra bytes after the top-level item
}

DecodeOpts tunes the decoder. Defaults are conservative: UTF-8 is validated, depth is capped to fend off stack-blow-up payloads, and duplicate map keys are tolerated (callers that need detection turn deny_duplicate_keys on).

struct EncodeOpts #

struct EncodeOpts {
pub:
	initial_cap   int = 64
	canonical     bool // sort map keys, definite-length only
	self_describe bool // prepend tag 55799 (`d9 d9 f7`)
	// validate_utf8 makes encode[T] reject V `string` payloads that
	// contain non-UTF-8 bytes. Off by default to match the conventional
	// V invariant ("strings are UTF-8") and avoid paying for validation
	// on hot paths. Turn on at trust boundaries when callers may build
	// strings from raw bytes (e.g. `bytestr()`), so the wire stays
	// round-trip-safe against the strict-by-default decoder.
	validate_utf8 bool
}

EncodeOpts tunes the encoder. Defaults yield RFC 8949 preferred serialisation: floats shrink to the shortest IEEE 754 width that preserves their value, headers use the shortest length encoding.

Setting canonical = true additionally sorts map keys per RFC 8949 §4.2.1 (deterministic encoding) — useful for hashing/signing.

struct FloatNum #

struct FloatNum {
pub:
	value f64
	bits  FloatBits = .@none
}

FloatNum is a CBOR floating-point value (major type 7, additional info 25/26/27). bits records the wire width for fidelity on re-encoding; the default .@none lets the encoder pick the shortest IEEE 754 width that preserves the value (RFC 8949 §4.2.2 preferred serialisation), so hand-built FloatNum literals don't accidentally lock in 8-byte output.

struct IntNum #

struct IntNum {
pub:
	negative  bool
	magnitude u64
}

IntNum holds the full unsigned/negative CBOR integer range. CBOR allows values from -(2^64) to 2^64-1, which exceeds either i64 or u64 alone, so the sign bit is split out and the magnitude carried as u64.

for unsigned: negative=false, magnitude=value for negative: negative=true, magnitude=encoded_argument actual integer = -1 - i64(magnitude) (when it fits i64)

struct IntRangeError #

struct IntRangeError {
	Error
pub:
	pos    int
	target string
	value  string
}

IntRangeError fires when a decoded integer doesn't fit the target type.

fn (IntRangeError) msg #

fn (e &IntRangeError) msg() string

msg formats an IntRangeError for IError.msg().

struct InvalidUtf8Error #

struct InvalidUtf8Error {
	Error
pub:
	pos int
}

InvalidUtf8Error fires when a text-string payload isn't valid UTF-8 and the decoder is configured to validate strings.

fn (InvalidUtf8Error) msg #

fn (e &InvalidUtf8Error) msg() string

msg formats an InvalidUtf8Error for IError.msg().

struct MalformedError #

struct MalformedError {
	Error
pub:
	pos    int
	reason string
}

MalformedError fires when the byte stream violates RFC 8949 well-formedness.

fn (MalformedError) msg #

fn (e &MalformedError) msg() string

msg formats a MalformedError for IError.msg().

struct Map #

struct Map {
pub mut:
	pairs []MapPair
}

Map holds the ordered key/value pairs of a CBOR map (major type 5).

struct MapPair #

struct MapPair {
pub:
	key   Value
	value Value
}

MapPair represents one key/value entry in a CBOR map. CBOR allows any data item as a key, so we keep an ordered list of pairs rather than using V's map[K]V.

struct MaxDepthError #

struct MaxDepthError {
	Error
pub:
	pos       int
	max_depth int
}

MaxDepthError fires when nested arrays/maps exceed the configured cap.

fn (MaxDepthError) msg #

fn (e &MaxDepthError) msg() string

msg formats a MaxDepthError for IError.msg().

struct Null #

struct Null {}

Null is the wrapped form of CBOR null (simple value 22).

struct Packer #

struct Packer {
pub mut:
	buf  []u8
	opts EncodeOpts
mut:
	indef_string_open bool // top of the indef "stack" is text or bytes
	indef_other_depth int  // count of currently open indef arrays/maps
}

Packer accumulates CBOR bytes into an internal buffer. Use bytes() to retrieve the wire output, or reset() to reuse the buffer for the next message — that's the cheapest way to emit many small frames.

indef_string_open and indef_other_depth track open indefinite-length items so the encoder can reject malformed compositions: nested indef strings, indef array/map inside an indef string (RFC 8949 §3.2.3), or a stray break code.

fn (Packer) bytes #

fn (mut p Packer) bytes() []u8

bytes returns the encoded buffer. The returned slice aliases the Packer's storage — clone it if you keep using the Packer. This is a low-level accessor that does NOT verify the buffer holds a complete item; if you opened an indefinite-length container without closing it, the bytes will be malformed. Use pack_to (or encode[T]) for the validated path, or call is_complete() yourself.

fn (Packer) is_complete #

fn (p &Packer) is_complete() bool

is_complete reports whether the buffer holds a sequence of fully closed items. False while an indefinite-length array, map, text, or bytes container is still open (waiting for pack_break).

fn (Packer) pack #

fn (mut p Packer) pack[T](val T) !

pack encodes val into the packer's buffer using compile-time dispatch.

fn (Packer) pack_array_header #

fn (mut p Packer) pack_array_header(n u64)

pack_array_header writes the prefix for a definite-length array.

fn (Packer) pack_array_indef #

fn (mut p Packer) pack_array_indef() !

pack_array_indef opens an indefinite-length array. Close with pack_break.

fn (Packer) pack_bool #

fn (mut p Packer) pack_bool(v bool)

pack_bool emits the simple value 20 (false) or 21 (true).

fn (Packer) pack_break #

fn (mut p Packer) pack_break() !

pack_break writes the break stop code 0xff that terminates the most recently opened indefinite-length item. Errors when no item is open (the byte 0xff is otherwise reserved and emitting one would corrupt the stream).

fn (Packer) pack_bytes #

fn (mut p Packer) pack_bytes(b []u8)

pack_bytes writes a byte string (major type 2).

fn (Packer) pack_bytes_indef #

fn (mut p Packer) pack_bytes_indef() !

pack_bytes_indef opens an indefinite-length byte string. Each chunk must be a definite-length byte string; close with pack_break.

fn (Packer) pack_float #

fn (mut p Packer) pack_float(v f64)

pack_float emits the shortest IEEE 754 width that preserves the value, per RFC 8949 §4.2.2. NaN serialises as the canonical quiet NaN (0xf97e00), not the original payload.

fn (Packer) pack_float16_bits #

fn (mut p Packer) pack_float16_bits(bits u16)

pack_float16_bits always emits a 2-byte IEEE 754 float.

fn (Packer) pack_float32 #

fn (mut p Packer) pack_float32(v f32)

pack_float32 always emits a 4-byte IEEE 754 float.

fn (Packer) pack_float64 #

fn (mut p Packer) pack_float64(v f64)

pack_float64 always emits an 8-byte IEEE 754 float.

fn (Packer) pack_int #

fn (mut p Packer) pack_int(v i64)

pack_int picks the right major type for a signed integer. For values below i64.min that can still fit -1-u64, prefer pack_negative_arg.

fn (Packer) pack_map_header #

fn (mut p Packer) pack_map_header(n u64)

pack_map_header writes the prefix for a definite-length map. The argument is the number of pairs, not items.

fn (Packer) pack_map_indef #

fn (mut p Packer) pack_map_indef() !

pack_map_indef opens an indefinite-length map. Close with pack_break.

fn (Packer) pack_negative_arg #

fn (mut p Packer) pack_negative_arg(arg u64)

pack_negative_arg writes a major type 1 value where the encoded argument is arg and the represented integer is -1 - arg. Lets you emit values down to -2^64 (the lower bound of CBOR negative ints).

fn (Packer) pack_null #

fn (mut p Packer) pack_null()

pack_null emits CBOR null (simple value 22, byte 0xf6).

fn (Packer) pack_raw #

fn (mut p Packer) pack_raw(raw RawMessage) !

pack_raw appends a RawMessage's bytes to the Packer without re-encoding. An empty RawMessage is rejected: emitting zero bytes would silently drop the slot when the value is a struct field or array element, shifting every subsequent item by one — almost always a caller bug.

fn (Packer) pack_simple #

fn (mut p Packer) pack_simple(v u8) !

pack_simple emits a CBOR simple value. Values 0..23 use the inline form, values 32..255 use the 1-byte trailer form. Values 24..31 are not well-formed per RFC 8949 §3.3 and are rejected here.

fn (Packer) pack_tag #

fn (mut p Packer) pack_tag(number u64)

pack_tag writes a tag header (major type 6). The next packed item is the tag's content.

fn (Packer) pack_text #

fn (mut p Packer) pack_text(s string)

pack_text writes a UTF-8 text string (major type 3). Single-shot reservation: the head + payload bytes are appended via one capacity check and one memcpy.

fn (Packer) pack_text_indef #

fn (mut p Packer) pack_text_indef() !

pack_text_indef opens an indefinite-length text string. Each chunk must be a definite-length text string; close with pack_break.

fn (Packer) pack_to #

fn (mut p Packer) pack_to(mut w io.Writer) !

pack_to is the streaming sibling of encode_to, for users who built their payload manually via the Packer API. Errors if any indefinite-length container is still open — emitting half-closed CBOR would produce a payload no decoder can parse.

fn (Packer) pack_uint #

fn (mut p Packer) pack_uint(v u64)

pack_uint emits a CBOR unsigned-integer (major type 0). Covers the full u64 range, including values above i64.max.

fn (Packer) pack_undefined #

fn (mut p Packer) pack_undefined()

pack_undefined emits CBOR undefined (simple value 23, byte 0xf7).

fn (Packer) pack_value #

fn (mut p Packer) pack_value(v Value) !

pack_value emits an arbitrary Value tree, honouring the original float width hint. Map keys are sorted when opts.canonical is set. Returns an error if the tree is malformed (e.g. a Tag with no content) — silently emitting a placeholder would corrupt round-trips.

fn (Packer) reserve #

fn (mut p Packer) reserve(n int)

reserve grows the buffer's capacity by at least n bytes. Useful before a string/binary write of known length to skip per-byte growth.

fn (Packer) reset #

fn (mut p Packer) reset()

reset clears the buffer for reuse. The capacity is preserved, so this is the fast path for high-throughput senders.

struct RawMessage #

struct RawMessage {
pub mut:
	data []u8
}

RawMessage holds the byte-exact encoding of one CBOR data item as it appeared on the wire. Useful for caching/forwarding code that wants to defer decoding of a nested field.

struct Envelope { id int payload cbor.RawMessage // bytes preserved as-is }

raw := cbor.decodecbor.RawMessage! back := cbor.encode(raw, cbor.EncodeOpts{})! // identical bytes

struct Simple #

struct Simple {
pub:
	value u8
}

Simple is the catch-all for major type 7 simple values 0..255 not otherwise covered by Bool/Null/Undefined.

struct Tag #

struct Tag {
pub:
	number      u64
	content_box []Value
}

Tag wraps a tagged data item (major type 6). The content is stored in a one-element slice rather than as a &Value reference: V can box and recurse a sumtype through a slice, while a direct &Value field requires manual heap allocation. Use tag.content() to access it.

fn (Tag) content #

fn (t &Tag) content() Value

content returns the Value enclosed by a Tag, or Null{} if missing.

struct Text #

struct Text {
pub:
	value string
}

Text is a CBOR text string (major type 3, valid UTF-8).

struct TypeMismatchError #

struct TypeMismatchError {
	Error
pub:
	pos      int
	expected string
	got      u8 // initial byte
}

TypeMismatchError fires when a typed read finds a different major type.

fn (TypeMismatchError) msg #

fn (e &TypeMismatchError) msg() string

msg formats a TypeMismatchError for IError.msg().

struct Undefined #

struct Undefined {}

Undefined is the wrapped form of CBOR undefined (simple value 23).

struct UnexpectedEofError #

struct UnexpectedEofError {
	Error
pub:
	pos       int // position at which the read began
	need      i64 // bytes the decoder was trying to read
	remaining int // bytes actually available
}

UnexpectedEofError fires when the decoder runs past the end of its input. need is i64 so it can represent the full CBOR length range (which is u64 on the wire); huge values are clamped to i64::max for reporting.

fn (UnexpectedEofError) msg #

fn (e &UnexpectedEofError) msg() string

msg formats an UnexpectedEofError for IError.msg().

struct UnknownFieldError #

struct UnknownFieldError {
	Error
pub:
	pos  int
	name string
}

UnknownFieldError fires when a struct decoded with deny_unknown_fields encounters an unmapped key.

fn (UnknownFieldError) msg #

fn (e &UnknownFieldError) msg() string

msg formats an UnknownFieldError for IError.msg().

struct Unpacker #

struct Unpacker {
pub mut:
	data []u8
	pos  int
	opts DecodeOpts
}

Unpacker walks a CBOR byte slice. Operates non-allocating where possible; strings and bytes returned by unpack_text / unpack_bytes always own their storage so they outlive the input buffer.

fn (Unpacker) done #

fn (u &Unpacker) done() bool

done reports whether the unpacker has consumed every byte.

fn (Unpacker) expect_break #

fn (mut u Unpacker) expect_break() !

expect_break consumes a single 0xff break code; errors otherwise.

fn (Unpacker) peek_break #

fn (u &Unpacker) peek_break() bool

peek_break reports whether the next byte is the break stop code.

fn (Unpacker) peek_kind #

fn (u &Unpacker) peek_kind() !Kind

peek_kind classifies the next item without consuming any input.

fn (Unpacker) remaining #

fn (u &Unpacker) remaining() int

remaining returns the number of unread bytes.

fn (Unpacker) skip_value #

fn (mut u Unpacker) skip_value() !

skip_value advances past one complete CBOR value without allocating. Honours the depth cap so adversarial deeply-nested input cannot blow the stack.

fn (Unpacker) unpack #

fn (mut u Unpacker) unpack[T]() !T

unpack reads one CBOR value from the buffer and converts it to T.

fn (Unpacker) unpack_array_header #

fn (mut u Unpacker) unpack_array_header() !i64

unpack_array_header reads the prefix of an array. Returns the count for definite-length arrays, or -1 for indefinite-length arrays (the caller then loops until peek_kind() == .break_code and consumes the break with expect_break).

fn (Unpacker) unpack_bool #

fn (mut u Unpacker) unpack_bool() !bool

unpack_bool reads a CBOR boolean (simple 20/21). Position is rolled back on a type mismatch so callers can branch on peek_kind and try a different read.

fn (Unpacker) unpack_bytes #

fn (mut u Unpacker) unpack_bytes() ![]u8

unpack_bytes reads a definite or indefinite-length byte string. The returned slice is a clone, safe to retain after the unpacker is freed.

fn (Unpacker) unpack_float #

fn (mut u Unpacker) unpack_float() !f64

unpack_float reads a CBOR float of any width (half/single/double) and returns it as f64.

fn (Unpacker) unpack_int #

fn (mut u Unpacker) unpack_int() !i64

unpack_int reads any CBOR integer (major type 0 or 1) into i64. Errors when the magnitude exceeds i64 range; use unpack_int_full to pull values as u64 with a separate sign flag.

fn (Unpacker) unpack_int_full #

fn (mut u Unpacker) unpack_int_full() !(bool, u64)

unpack_int_full returns (negative, magnitude). For unsigned values negative=false and magnitude is the raw u64. For negative values negative=true and magnitude is the encoded argument (the integer itself is -1 - magnitude).

fn (Unpacker) unpack_map_header #

fn (mut u Unpacker) unpack_map_header() !i64

unpack_map_header reads the prefix of a map. Returns pair count or -1 for indefinite-length maps.

fn (Unpacker) unpack_null #

fn (mut u Unpacker) unpack_null() !

unpack_null consumes a CBOR null (0xf6) or errors with type mismatch. Position is rolled back on mismatch (same convention as unpack_bool).

fn (Unpacker) unpack_raw #

fn (mut u Unpacker) unpack_raw() !RawMessage

unpack_raw captures the bytes of the next value without building a Value tree. Returns an owned clone, safe to outlive the unpacker.

fn (Unpacker) unpack_simple #

fn (mut u Unpacker) unpack_simple() !u8

unpack_simple reads a simple value (0..255). Bool/null/undefined are also simple values; this method returns the raw u8.

fn (Unpacker) unpack_tag #

fn (mut u Unpacker) unpack_tag() !u64

unpack_tag reads a tag header and returns the tag number. The caller must follow up by reading the tag content. Position is rolled back on any error so callers can branch on peek_kind and try a different read.

fn (Unpacker) unpack_text #

fn (mut u Unpacker) unpack_text() !string

unpack_text reads a definite or indefinite-length text string. The returned string owns its bytes (it's a clone of the input slice). UTF-8 validation runs unless DecodeOpts.validate_utf8 is false.

fn (Unpacker) unpack_uint #

fn (mut u Unpacker) unpack_uint() !u64

unpack_uint reads a non-negative integer (major type 0). Errors on negatives, floats, or other major types. Position is rolled back on any error so callers can branch on peek_kind and try a different read (same convention as unpack_bool / unpack_text).

fn (Unpacker) unpack_value #

fn (mut u Unpacker) unpack_value() !Value

unpack_value materialises one CBOR data item as a Value.