Decoding

libcbor provides two decoding interfaces:

Default

Streaming

Function

cbor_load()

cbor_stream_decode()

Returns

A fully-built cbor_item_t tree.

Nothing. Fires a callback for each decoded value.

Callbacks

None. Results available after the call returns.

One callback per primitive, string chunk, or collection boundary (e.g. array_start, uint8, string_chunk).

Nesting

Handled by the library.

Caller’s responsibility. Nested structures (e.g. arrays containing maps) require the caller to maintain an explicit stack to track the current depth and accumulate children.

Memory

Allocates the cbor_item_t tree. Free with cbor_decref().

No library allocations.

Best for

  • Trusted or size-bounded inputs.

  • General data manipulation and inspection.

  • Simple use cases.

  • Large or unbounded inputs.

  • Memory-constrained environments.

  • Mapping to custom data structures.

  • Known, restricted schemas.

cbor_load() is implemented on top of cbor_stream_decode() — it installs its own internal callbacks that build the cbor_item_t tree as events arrive.

digraph decoding {
    graph [rankdir=TB, nodesep=1.4, ranksep=0.8, fontname="Helvetica"]
    node  [shape=box, style="filled,rounded", fontname="Helvetica", margin="0.3,0.15"]
    edge  [color="#555555", fontname="Helvetica", fontsize=10]

    client    [label="Client application",                   fillcolor="#AED6F1"]
    cbor_load [label="cbor_load",                            fillcolor="#FAD7A0"]
    streaming [label="cbor_stream_decode",                   fillcolor="#FAD7A0"]
    item      [label="cbor_item_t",                          fillcolor="#A9DFBF"]
    manip     [label="Manipulation routines\n(cbor_item_t API)", fillcolor="#D2B4DE"]

    { rank=same; cbor_load; streaming }
    { rank=same; item; manip }

    client -> cbor_load [label=" bytes "]
    client -> streaming [label=" bytes + callbacks "]

    cbor_load -> streaming [label=" internal callbacks ",
                            style=dashed, constraint=false]
    cbor_load -> item

    item:e -> manip:w [style=dashed, dir=both, constraint=false,
                       label=" use items "]
}

This section covers the Default driver — cbor_load() and related routines that decode a complete CBOR input into a cbor_item_t tree in one call.

Well-formedness and validity

RFC 8949 distinguishes two levels of correctness for a CBOR data item:

  • Well-formed: the item follows the syntactic structure of CBOR — the initial bytes, argument, and any implied byte strings or nested data items are all present and consistent (Section 3).

  • Valid: the item is well-formed and satisfies the semantic restrictions of Section 5.3, e.g. text strings are valid UTF-8, maps have no duplicate keys, and tag content has the type the tag requires.

Both libcbor decoders — cbor_load() and cbor_stream_decode() — check well-formedness only. Any well-formed input is decoded successfully; validity is left to the application, which is the only party that knows the protocol’s requirements (Section 5.4). Concretely, the decoders will accept:

Input

RFC 8949

How to check

Text string that is not valid UTF-8, e.g. 0x62 0xC0 0xAE

5.3.1

cbor_string_codepoint_count() returns 0; see Type 3 – UTF-8 strings

Map with duplicate keys, e.g. {1: 2, 1: 3}

5.3.1

Iterate cbor_map_handle() and compare keys with cbor_structurally_equal(); see Type 5 – Maps

Tag whose content has an inadmissible type, e.g. tag 1 (epoch date) applied to a map

5.3.2

Inspect cbor_tag_value() and cbor_tag_item(); see Type 6 – Semantic tags

Inputs that are not well-formed — truncated data, reserved additional information values, unexpected break codes, indefinite-length strings with chunks of the wrong type, etc. — are rejected with a cbor_error_code describing the problem.

Note that the terminology in the CBOR working group’s test vectors and similar suites does not always make this distinction. Test cases that expect a decoder to fail on well-formed but invalid input are not applicable to libcbor’s decoders as designed.

Warning

cbor_load allocates memory sized by lengths declared in the CBOR header before reading the corresponding data:

  • Definite-length arrays and maps — storage for the declared element count.

  • Definite-length strings and bytestrings — a buffer for the declared byte length.

All of these lengths are encoded as a 64-bit integer, so cbor_load may attempt an allocation of up to 264−1 bytes before any payload data is read. malloc will normally refuse such a request and cbor_load will return CBOR_ERR_MEMERROR, but on platforms with memory overcommit (Linux by default) the allocation may silently appear to succeed.

Mitigations:

  • Install a capping allocator via cbor_set_allocs() to bound total memory consumption (see examples/capped_alloc.c for a self-contained example).

  • Use the streaming decoder (Streaming Decoding), which gives the application full control over memory allocation for each decoded item.

cbor_item_t *cbor_load(cbor_data source, size_t source_size, struct cbor_load_result *result)

Loads data item from a buffer.

param source:

The buffer

param source_size:

param result:

[out] Result indicator. CBOR_ERR_NONE on success

return:

Decoded CBOR item. The item’s reference count is initialized to one.

return:

NULL on failure. In that case, result contains the location and description of the error.

Associated data structures

enum cbor_error_code

Possible decoding errors.

Values:

enumerator CBOR_ERR_NONE
enumerator CBOR_ERR_NOTENOUGHDATA
enumerator CBOR_ERR_NODATA
enumerator CBOR_ERR_MALFORMATED
enumerator CBOR_ERR_MEMERROR

Memory error - item allocation failed.

Is it too big for your allocator?

enumerator CBOR_ERR_SYNTAXERROR

Stack parsing algorithm failed.

struct cbor_load_result

High-level decoding result.

Public Members

struct cbor_error error

Error indicator.

size_t read

Number of bytes read.

struct cbor_error

High-level decoding error.

Public Members

size_t position

Approximate position.

cbor_error_code code

Description.