CBOR

This is a minimalistic implementation for CBOR, the Concise Binary Object Representation. CBOR is defined by IETF RFC 8949, and Wikipedia has a good description.

Features

  • C99

  • No dynamic memory allocation

  • Small code footprint

Build

Make

CBOR_ROOT ?= <THIRD_PARTY_DIR>/cbor
include $(CBOR_ROOT)/cbor.mk

CMake

include(FetchContent)
FetchContent_Declare(cbor
                      GIT_REPOSITORY https://github.com/libmcu/cbor.git
                      GIT_TAG main
)
FetchContent_MakeAvailable(cbor)

or

set(CBOR_ROOT <THIRD_PARTY_DIR>/cbor)
include(${CBOR_ROOT}/cbor.cmake)

Usage

static void parse_cert(const cbor_reader_t *reader,
        const struct cbor_parser *parser,
        const cbor_item_t *item, void *arg) {
    struct your_data_type *out = arg;
    cbor_decode(reader, item, out->cert, sizeof(out->cert));
}
static void parse_key(const cbor_reader_t *reader,
        const struct cbor_parser *parser,
        const cbor_item_t *item, void *arg) {
    struct your_data_type *out = arg;
    cbor_decode(reader, item, out->key, sizeof(out->key));
}

static const struct cbor_parser parsers[] = {
    { .key = "certificate", .run = parse_cert },
    { .key = "privateKey",  .run = parse_key },
};

cbor_reader_t reader;
cbor_item_t items[MAX_ITEMS];

cbor_reader_init(&reader, items, sizeof(items) / sizeof(*items));
cbor_unmarshal(&reader, parsers, sizeof(parsers) / sizeof(*parsers),
        msg, msglen, &your_data_type);

...

Please refer to examples.

Option

  • CBOR_BIG_ENDIAN

    • Define the macro for big endian machine. The default is little endian.

  • CBOR_RECURSION_MAX_LEVEL

    • This is set to avoid stack overflow from recursion. The default maximum depth is 8.

Parser

The parser takes 626 bytes on ARM Cortex-M0 optimizing for code size -Os. arm-none-eabi-gcc 10-2020-q4-major was used for the check.

Stack usage per the major type functions:

Major type

Bytes

0: unsigned integer

12

1: negative integer

12

2: byte string

32

3: text string

32

4: array

32

5: map

32

6: tag(not implemented yet)

0

7: floating-point numbers, simple values and break

32

And the call stack for each recursion is 24 bytes.

cbor_reader_t reader;
cbor_item_t items[MAX_ITEMS];
size_t n;

cbor_reader_init(&reader, items, sizeof(items) / sizeof(*items));
cbor_parse(&reader, cbor_message, cbor_message_len, &n);

for (i = 0; i < n; i++) {
    printf("item: %s, size: %zu\n",
            cbor_stringify_item(&items[i]),
            cbor_get_item_size(&items[i]);
}

Decoder

union {
    int8_t i8;
    int16_t i16;
    int32_t i32;
    int64_t i64;
    float f32;
    double f64;
    uint8_t s[MTU];
} val;

cbor_decode(&reader, &items[i], &val, sizeof(val));

Encoder

cbor_writer_t writer;

cbor_writer_init(&reader, buf, sizeof(buf));

cbor_encode_map(&writer, 2);
  /* 1st */
  cbor_encode_text_string(&writer, "key");
  cbor_encode_text_string(&writer, "value");
  /* 2nd */
  cbor_encode_text_string(&writer, "age");
  cbor_encode_negative_integer(&writer, -1);

Limitation

  • The maximum item length is size_t because the interface return type is size_t. The argument’s value in the specification can go up to uint64_t though

  • A negative integer ranges down to -2^63-1 other than -2^64 in the specification

  • Sorting of encoded map keys is not supported

  • Tag item is not implemented yet

  • cbor_unmarshal() only works on the major type 5: map with string key

API

Base

Functions

void cbor_reader_init(cbor_reader_t *reader, cbor_item_t *items, size_t maxitems)

Initialize the reader for CBOR encoded messages.

Parameters:
  • reader[inout] reader context for the actual encoded message

  • items[out] a pointer to item buffers

  • maxitems[in] the maximum number of items to be stored in items

void cbor_writer_init(cbor_writer_t *writer, void *buf, size_t bufsize)
size_t cbor_writer_len(cbor_writer_t const *writer)
uint8_t const *cbor_writer_get_encoded(cbor_writer_t const *writer)
cbor_item_data_t cbor_get_item_type(cbor_item_t const *item)
size_t cbor_get_item_size(cbor_item_t const *item)
uint8_t cbor_get_following_bytes(uint8_t additional_info)
size_t cbor_copy(uint8_t *dst, uint8_t const *src, size_t len)
size_t cbor_copy_be(uint8_t *dst, uint8_t const *src, size_t len)
struct cbor_item_t
#include <base.h>

Public Members

cbor_item_data_t type
size_t offset
size_t size

either of the length of value in bytes or the number of items in case of container type

struct cbor_reader_t
#include <base.h>

Public Members

uint8_t const *msg
size_t msgsize
size_t msgidx
cbor_item_t *items
size_t itemidx
size_t maxitems
struct cbor_writer_t
#include <base.h>

Public Members

uint8_t *buf
size_t bufsize
size_t bufidx

Enums

enum cbor_error_t

Values:

enumerator CBOR_SUCCESS

well-formed

enumerator CBOR_ILLEGAL

not well-formed

enumerator CBOR_INVALID

well-formed but invalid

enumerator CBOR_OVERRUN

more items than given buffer space

enumerator CBOR_BREAK
enumerator CBOR_EXCESSIVE

recursion more than CBOR_RECURSION_MAX_LEVEL

enum cbor_item_data_t

Values:

enumerator CBOR_ITEM_UNKNOWN
enumerator CBOR_ITEM_INTEGER

unsigned integer and negative integer

enumerator CBOR_ITEM_STRING

byte string and text string

enumerator CBOR_ITEM_ARRAY
enumerator CBOR_ITEM_MAP
enumerator CBOR_ITEM_FLOAT
enumerator CBOR_ITEM_SIMPLE_VALUE

Defines

CBOR_RECURSION_MAX_LEVEL 8
CBOR_INDEFINITE_VALUE (-1)
CBOR_RESERVED_VALUE (-2)
CBOR_ADDITIONAL_INFO_MASK 0x1fu /* the low-order 5 bits */
get_cbor_major_type(data_item) ((data_item) >> 5)
get_cbor_additional_info(major_type) ((major_type) & CBOR_ADDITIONAL_INFO_MASK)

Parser

Functions

cbor_error_t cbor_parse(cbor_reader_t *reader, void const *msg, size_t msgsize, size_t *nitems_parsed)

Parse the encoded CBOR messages into items.

Parameters:
  • reader[inout] reader context for the actual encoded message

  • msg[in] CBOR encoded message

  • msgsize[in] the msg size in bytes

  • nitems_parsed[out] the number of items parsed gets stored if not null

Returns:

a code of cbor_error_t

Encoder

Functions

cbor_error_t cbor_encode_unsigned_integer(cbor_writer_t *writer, uint64_t value)
cbor_error_t cbor_encode_negative_integer(cbor_writer_t *writer, int64_t value)
cbor_error_t cbor_encode_byte_string(cbor_writer_t *writer, uint8_t const *data, size_t datasize)
cbor_error_t cbor_encode_byte_string_indefinite(cbor_writer_t *writer)
cbor_error_t cbor_encode_text_string(cbor_writer_t *writer, char const *text, size_t textsize)
cbor_error_t cbor_encode_null_terminated_text_string(cbor_writer_t *writer, char const *text)
cbor_error_t cbor_encode_text_string_indefinite(cbor_writer_t *writer)
cbor_error_t cbor_encode_array(cbor_writer_t *writer, size_t length)
cbor_error_t cbor_encode_array_indefinite(cbor_writer_t *writer)
cbor_error_t cbor_encode_map(cbor_writer_t *writer, size_t length)
cbor_error_t cbor_encode_map_indefinite(cbor_writer_t *writer)
cbor_error_t cbor_encode_break(cbor_writer_t *writer)
cbor_error_t cbor_encode_simple(cbor_writer_t *writer, uint8_t value)
cbor_error_t cbor_encode_bool(cbor_writer_t *writer, bool value)
cbor_error_t cbor_encode_null(cbor_writer_t *writer)
cbor_error_t cbor_encode_undefined(cbor_writer_t *writer)
cbor_error_t cbor_encode_float(cbor_writer_t *writer, float value)
cbor_error_t cbor_encode_double(cbor_writer_t *writer, double value)

Decoder

Functions

cbor_error_t cbor_decode(cbor_reader_t const *reader, cbor_item_t const *item, void *buf, size_t bufsize)

Decode a CBOR data item

Parameters:
  • reader[in] reader context for the actual encoded message

  • item[in] meta data about the item to be decoded

  • buf[out] the buffer where decoded value to be written in

  • bufsize[in] the buffer size

Returns:

a code of cbor_error_t

void const *cbor_decode_pointer(cbor_reader_t const *reader, cbor_item_t const *item)

Get the pointer to an item value

Parameters:
  • reader[in] reader context for the actual encoded message

  • item[in] meta data about the item to be decoded

Returns:

the pointer to the item value

IEEE754

Functions

uint16_t ieee754_convert_single_to_half(float value)
double ieee754_convert_half_to_double(uint16_t value)
bool ieee754_is_shrinkable_to_half(float value)
bool ieee754_is_shrinkable_to_single(double value)
union ieee754_half_t
#include <ieee754.h>

Public Members

uint16_t value
uint32_t m
uint32_t e
uint32_t sign
struct ieee754_half_t components
union ieee754_single_t
#include <ieee754.h>

Public Members

float value
uint32_t m
uint32_t e
uint32_t sign
struct ieee754_single_t components
union ieee754_double_t
#include <ieee754.h>

Public Members

double value
uint64_t m
uint64_t e
uint64_t sign
struct ieee754_double_t components

Helper

Functions

bool cbor_unmarshal(cbor_reader_t *reader, const struct cbor_parser *parsers, size_t nr_parsers, const void *msg, size_t msglen, void *arg)
size_t cbor_iterate(const cbor_reader_t *reader, const cbor_item_t *parent, void (*callback_each)(const cbor_reader_t *reader, const cbor_item_t *item, const cbor_item_t *parent, void *arg), void *arg)
const char *cbor_stringify_error(cbor_error_t err)
const char *cbor_stringify_item(cbor_item_t *item)
struct cbor_parser
#include <helper.h>

Public Members

const void *key
size_t keylen
void (*run)(const cbor_reader_t *reader, const struct cbor_parser *parser, const cbor_item_t *item, void *arg)