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

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.