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.
- C99
- No dynamic memory allocation
- Small code footprint
CBOR_ROOT ?= <THIRD_PARTY_DIR>/cbor
include $(CBOR_ROOT)/cbor.mk
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)
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.
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.
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]);
}
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));
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);
- The maximum item length is
size_t
because the interface return type issize_t
. The argument's value in the specification can go up touint64_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