Tutorial
libcbor is a C library to encode, decode, and manipulate CBOR data. It is to CBOR to what cJSON is to JSON. We assume you are familiar with the CBOR standard. If not, we recommend cbor.io.
Where to start
Skim through the Crash course section below.
Examples of how to read, write, manipulate, and translate data to and from JSON using libcbor are in the examples directory.
The API documentation is a complete reference of libcbor.
Crash course
CBOR data objects are cbor_item_t
void item_examples() {
// A cbor_item_t can contain any CBOR data type
cbor_item_t* float_item = cbor_build_float4(3.14f);
cbor_item_t* string_item = cbor_build_string("Hello World!");
cbor_item_t* array_item = cbor_new_indefinite_array();
// They can be inspected
assert(cbor_is_float(float_item));
assert(cbor_typeof(string_item) == CBOR_TYPE_STRING);
assert(cbor_array_is_indefinite(array_item));
assert(cbor_array_size(array_item) == 0);
// The data can be accessed
assert(cbor_float_get_float4(float_item) == 3.14f);
assert(memcmp(cbor_string_handle(string_item), "Hello World!",
cbor_string_length(string_item)) == 0);
// And they can be modified
assert(cbor_array_push(array_item, float_item));
assert(cbor_array_push(array_item, string_item));
assert(cbor_array_size(array_item) == 2);
// At the end of their lifetime, items must be freed
cbor_decref(&float_item);
cbor_decref(&string_item);
cbor_decref(&array_item);
}
Objects can be serialized and deserialized
void encode_decode() {
cbor_item_t* item = cbor_build_uint8(42);
// Serialize the item to a buffer (it will be allocated by libcbor)
unsigned char* buffer;
size_t buffer_size;
cbor_serialize_alloc(item, &buffer, &buffer_size);
assert(buffer_size == 2);
assert(buffer[0] == 0x18); // Encoding byte for uint8
assert(buffer[1] == 42); // The value itself
// And deserialize bytes back to an item
struct cbor_load_result result;
cbor_item_t* decoded_item = cbor_load(buffer, buffer_size, &result);
assert(result.error.code == CBOR_ERR_NONE);
assert(cbor_isa_uint(decoded_item));
assert(cbor_get_uint8(decoded_item) == 42);
// Free the allocated buffer and items
free(buffer);
cbor_decref(&decoded_item);
cbor_decref(&item);
}
Reference counting
void reference_counting() {
// cbor_item_t is a reference counted pointer under the hood
cbor_item_t* item = cbor_build_uint8(42);
// Reference count starts at 1
assert(cbor_refcount(item) == 1);
// Most operations have reference semantics
cbor_item_t* array_item = cbor_new_definite_array(1);
assert(cbor_array_push(array_item, item));
assert(cbor_refcount(item) == 2); // item and array_item reference it
cbor_item_t* first_array_element = cbor_array_get(array_item, 0);
assert(first_array_element == item); // same item under the hood
assert(cbor_refcount(item) ==
3); // and now first_array_element also points to it
// To release the reference, use cbor_decref
cbor_decref(&first_array_element);
// When reference count reaches 0, the item is freed
assert(cbor_refcount(array_item) == 1);
cbor_decref(&array_item);
assert(array_item == NULL);
assert(cbor_refcount(item) == 1);
// Be careful, loops leak memory!
// Deep copy copies the whole item tree
cbor_item_t* item_copy = cbor_copy(item);
assert(cbor_refcount(item) == 1);
assert(cbor_refcount(item_copy) == 1);
assert(item_copy != item);
cbor_decref(&item);
cbor_decref(&item_copy);
}
Moving intermediate values
void moving_values() {
{
// Move the "42" into an array.
cbor_item_t* array_item = cbor_new_definite_array(1);
// The line below leaks memory!
assert(cbor_array_push(array_item, cbor_build_uint8(42)));
cbor_item_t* first_array_element = cbor_array_get(array_item, 0);
assert(cbor_refcount(first_array_element) == 3); // Should be 2!
cbor_decref(&first_array_element);
cbor_decref(&array_item);
assert(cbor_refcount(first_array_element) == 1); // Shouldn't exist!
// Clean up
cbor_decref(&first_array_element);
}
{
// A correct way to move values is to decref them in the caller scope.
cbor_item_t* array_item = cbor_new_definite_array(1);
cbor_item_t* item = cbor_build_uint8(42);
assert(cbor_array_push(array_item, item));
assert(cbor_refcount(item) == 2);
// "Give up" the item
cbor_decref(&item);
cbor_decref(&array_item);
// item is a dangling pointer at this point
}
{
// cbor_move avoids the need to decref and the dangling pointer
cbor_item_t* array_item = cbor_new_definite_array(1);
assert(cbor_array_push(array_item, cbor_move(cbor_build_uint8(42))));
cbor_item_t* first_array_element = cbor_array_get(array_item, 0);
assert(cbor_refcount(first_array_element) == 2);
cbor_decref(&first_array_element);
cbor_decref(&array_item);
}
}
Ownership
// Refcount can be managed in conjunction with ownership
static cbor_item_t* global_item = NULL;
// This function takes shared ownership of the item
void borrow_item(cbor_item_t* item) {
global_item = item;
// Mark the extra reference
cbor_incref(item);
}
void return_item() {
cbor_decref(&global_item);
global_item = NULL;
}
void reference_ownership() {
cbor_item_t* item = cbor_build_uint8(42);
// Lend the item
borrow_item(item);
assert(cbor_refcount(item) == 2);
cbor_decref(&item);
// Release the shared ownership. return_item will deallocate the item.
return_item();
}
Streaming IO
See https://github.com/PJK/libcbor/blob/master/examples/streaming_array.c, https://github.com/PJK/libcbor/blob/master/examples/streaming_parser.c