LIBCBOR(3)
π¦ libcbor β the LIBCBOR(3) manual page, but make it make sense π¦
okay so bestie, pull up a chair, grab a strawberry π, because we're about to get into libcbor and honestly? it's giving iconic.
π NAME π
libcbor β this is the libcbor documentation. yeah. that's the name. slay.
so like, this is the docs for version 0.13.0, updated on Jan 16, 2026 β¨
Git repo, if you wanna go lurk: <https://github.com/PJK/libcbor>
π OVERVIEW π
libcbor is a C library for parsing and generating CBOR <https://www.rfc-editor.org/info/std94> β and CBOR is the general-purpose, schema-less, binary data format. no schema, no problem, we just vibe π
β¨ Main features β¨
- Complete IETF RFC 8949 (STD 94) <https://www.rfc-editor.org/info/std94> conformance [1] π
- Robust C99 implementation
- Layered architecture offers both control and convenience
- Flexible memory management
- No shared global state β threading friendly [2] π§΅
- Proper handling of UTF-8
- Full support for streams & incremental processing
- Extensive documentation and test suite
- No runtime dependencies, small footprint π¦
[1] See IETF standard conformance. [2] With the exception of custom memory allocators (see Memory management and reference counting).
π CONTENTS π
π Getting started π
pre-built Linux packages are literally right there in most mainstream distributions, no cap:
Ubuntu, Debian, etc.:
apt-get install libcbor-dev
Fedora, openSUSE, etc.:
yum install libcbor-devel
OS X girlies can use Homebrew <http://brew.sh/>:
brew install libcbor
For other platforms, you will need to compile it from source. yeah. from scratch. it's fine. you got this. πͺ
π οΈ Building & installing libcbor π οΈ
Prerequisites, aka the vibe check before you start:
- C99 compiler
- CMake <http://cmake.org/> 2.8 or newer (might also be called
cmakesetup,cmake-guiorccmakedepending on the installed version and system) π - C build system CMake can target (
make, Apple Xcode, MinGW, ...)
βοΈ Configuration options βοΈ
a handful of configuration flags can be passed to cmake. here's the full rundown β libcbor compile-time directives plus several important generic flags:
CMAKE_C_COMPILERβ C compiler to use. Default:cc. Possible values:gcc,clang,clang-3.5, ...CMAKE_INSTALL_PREFIXβ Installation prefix. Default: System-dependent. Possible values:/usr/local/lib, ...CMAKE_INTERPROCEDURAL_OPTIMIZATIONβ Enable LTO (if supported). Default: System-dependent. Possible values:ON,OFFBUILD_SHARED_LIBSβ Build as a shared library. Default:OFF. Possible values:ON,OFFHUGE_FUZZβ Fuzz test with 8GB of data. Default:OFF. Possible values:ON,OFFSANE_MALLOCβ Assumemallocwill refuse unreasonable allocations. Default:OFF. Possible values:ON,OFFCOVERAGEβ Generate test coverage instrumentation. Default:OFF. Possible values:ON,OFFWITH_TESTSβ Build unit tests (see Development). Default:OFF. Possible values:ON,OFF
and the following configuration options will also be defined as macros [1] in <cbor/common.h> and can therefore be used in client code β so like, you can actually touch them from your own code, bestie:
CBOR_PRETTY_PRINTERβ Include a pretty-printing routine. Default:ON. Possible values:ON,OFFCBOR_BUFFER_GROWTHβ Factor for buffer growth & shrinking. Default:2. Possible values: Decimals > 1
[1]ON&OFFwill be translated to1and0usingcmakedefine<https://cmake.org/cmake/help/v3.2/command/configure_file.html?highlight=cmakedefine>. If you want to pass other custom configuration options, please refer to <http://www.cmake.org/Wiki/CMake_Useful_Variables>.
Note: WhenCMAKE_INTERPROCEDURAL_OPTIMIZATIONis enabled, the generated static library (libcbor.a) should be used with an LTO-enabled linker downstream. On LLVM toolchains without bitcode embedding (-fembed-bitcode), the archive will contain LLVM IR only and linking without LTO will not work <https://github.com/PJK/libcbor/issues/372>. π do not skip this one.
Warning: CBOR_CUSTOM_ALLOC has been removed <https://github.com/PJK/libcbor/pull/237>. Custom allocators (historically a controlled by a build flag) are always enabled. gone but never forgotten ποΈ
π§± Building using make π§±
CMake will generate a Makefile and other configuration files for the build. as a rule of thumb, you should configure the build outside of the source tree in order to keep different configurations isolated. if you are unsure where to execute the build, just use a temporary directory:
cd $(mktemp -d /tmp/cbor_build.XXXX)
Now, assuming you are in the directory where you want to build, build libcbor as a static library:
cmake -DCMAKE_BUILD_TYPE=Release path_to_libcbor_dir
make cbor
... or as a dynamic library:
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON path_to_libcbor_dir
make cbor
To install locally:
make install
Root permissions are required on most systems when using the default installation prefix. π
π Portability π
libcbor is highly portable and works on both little- and big-endian systems regardless of the operating system. after building on an exotic platform, you might wish to verify the result by running the test suite. if you encounter any problems, please report them to the issue tracker <https://github.com/PJK/libcbor/issues>. they wanna hear about it fr fr.
libcbor is known to successfully work on ARM Android devices. cross-compilation is possible with arm-linux-gnueabi-gcc. π€
π Linking with libcbor π
If you include and linker paths include the directories to which libcbor has been installed, compiling programs that uses libcbor requires no extra considerations. zero. none. it just works β¨
You can verify that everything has been set up properly by creating a file with the following contents:
#include <cbor.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
printf("Hello from libcbor %s\n", CBOR_VERSION);
}
and compiling it:
cc hello_cbor.c -lcbor -o hello_cbor
and that's just the beginning bestie, there is so much more coming π keep scrolling π
Ok so β libcbor also comes with pkg-config support π (yes, the fancy freedesktop one: https://wiki.freedesktop.org/www/Software/pkg-config/). And if you installed libcbor with a custom prefix and your compiler is being dramatic about where the headers live, bestie, pkg-config will just resolve the headers and objects for you π
cc $(pkg-config --cflags libcbor) hello_cbor.c $(pkg-config --libs libcbor) -o hello_cbor
β¨ A note on linkage β¨
Real talk: libcbor is primarily intended to be linked statically. That's the plan, that's the design, that's the whole moment π
The shared library versioning scheme generally follows SemVer (https://semver.org/), but β and this is the messy part β it's irregular for the 0.X.Y development branch, for historical reasons. π
The following version identifiers are used as a part of the SONAME (Linux) or the dylib "Compatibility version" (OS X) (see https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/CreatingDynamicLibraries.html):
0.Yfor the0.Y.Zbranch. Patches are backwards compatible, but minor releases are generally not and require re-compilation of any dependent code πXfor theX.Y.Zstable versions starting1.X.Y. All minor releases of the major version are backwards compatible π«Ά
β οΈ Warning: releases up to and includingv0.6.0may export a misleading.so/.dylibversion number (https://github.com/PJK/libcbor/issues/52). Not us being shady, just history being history.
π οΈ TROUBLESHOOTING π οΈ
cbor.hnot found β the headers directory is probably not in your include path π€ First, verify the installation location by checking the installation log. If you usedmake, it'll look something like
...
-- Installing: /usr/local/include/cbor
-- Installing: /usr/local/include/cbor/callbacks.h
-- Installing: /usr/local/include/cbor/encoding.h
...
Make sure that CMAKE_INSTALL_PREFIX (if you provided it) was correct. Including the path during compilation should suffice, e.g.:
cc -I/usr/local/include hello_cbor.c -lcbor -o hello_cbor
cannot find -lcborduring linking β most likely the same problem as before, bestie π« Include the installation directory in the linker shared path using-R, e.g.:
cc -Wl,-rpath,/usr/local/lib -lcbor -o hello_cbor
- shared library missing during execution β verify the linkage using
ldd,otool, or similar, and adjust the compilation directives accordingly:
β ldd hello_cbor
linux-vdso.so.1 => (0x00007ffe85585000)
libcbor.so => /usr/local/lib/libcbor.so (0x00007f9af69da000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9af65eb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9af6be9000)
- compilation failed β if your compiler supports
C99yet the compilation has failed, please report the issue to the issue tracker (https://github.com/PJK/libcbor/issues) π
πΈ TUTORIAL πΈ
libcbor is a C library to encode, decode, and manipulate CBOR data. It is to CBOR to what cJSON (https://github.com/DaveGamble/cJSON) is to JSON β like, that's the energy β¨
We assume you are already familiar with the CBOR standard. If notβ¦ we recommend cbor.io (http://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 (
https://github.com/PJK/libcbor/tree/master/examples) π - 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 and https://github.com/PJK/libcbor/blob/master/examples/streaming_parser.c β¨
π API π
The data API is centered around cbor_item_t, a generic handle for any CBOR item π There are functions to
- create items,
- set items' data,
- parse serialized data into items,
- manage, move, and link items together.
The single most important thing to keep in mind is: cbor_item_t is an opaque type and should only be manipulated using the appropriate functions! Think of it as an object π
The libcbor API closely follows the semantics outlined by the CBOR standard (https://tools.ietf.org/html/rfc7049). This part of the documentation provides a short overview of the CBOR constructs, as well as a general introduction to the libcbor API. The remaining reference can be found in the following files, structured by data types.
The API is designed to allow both very tight control & flexibility and general convenience with sane defaults. [1] For example, a client with very specific requirements (constrained environment, custom application protocol built on top of CBOR, etc.) may choose to take full control (and responsibility) of memory and data structures management by interacting directly with the decoder. Other clients might want to take control of specific aspects (streamed collections, hash maps storage), but leave other responsibilities to libcbor. More general clients might prefer to be abstracted away from all the aforementioned details and only be presented complete data structures β¨
libcbor provides
- stateless encoders and decoders
- encoding and decoding drivers, routines that coordinate encoding and decoding of complex structures
- data structures to represent and transform CBOR structures
- routines for building and manipulating these structures
- utilities for inspection and debugging
β¨ Types of items β¨
okay so every single cbor_item_t has a cbor_type attached to it, and those constants map one-to-one onto the types the CBOR standard defines π
enum cbor_type
Specifies the Major type of cbor_item_t.
Values:
CBOR_TYPE_UINTβ0β positive integers π’CBOR_TYPE_NEGINTβ1β negative integersCBOR_TYPE_BYTESTRINGβ2β byte strings π§CBOR_TYPE_STRINGβ3β stringsCBOR_TYPE_ARRAYβ4β arraysCBOR_TYPE_MAPβ5β maps πΊοΈCBOR_TYPE_TAGβ6β tags π·οΈCBOR_TYPE_FLOAT_CTRLβ7β decimals and special values (true,false,nil, ...) π
So to find out what type an item is, bestie, you literally just use this:
cbor_type cbor_typeof(const cbor_item_t *item)
- item β the item
- returns the type β¨
heads up! Please note the distinction between functions likecbor_isa_uint()andcbor_is_int(). The following functions work solely with the major type value.
π Binary queries
Alternatively β and this is lowkey the easier route β there are functions to query each particular type π
Warning: Passing an invalid cbor_item_t reference to any of these functions results in undefined behavior. not me finding that out the hard way π
bool cbor_isa_uint(const cbor_item_t *item)
Does the item have the appropriate major type?
- item β the item
- returns: Is the item an
CBOR_TYPE_UINT? π
bool cbor_isa_negint(const cbor_item_t *item)
Does the item have the appropriate major type?
- item β the item
- returns: Is the item a
CBOR_TYPE_NEGINT?
bool cbor_isa_bytestring(const cbor_item_t *item)
Does the item have the appropriate major type?
- item β the item
- returns: Is the item a
CBOR_TYPE_BYTESTRING? π§
bool cbor_isa_string(const cbor_item_t *item)
Does the item have the appropriate major type?
- item β the item
- returns: Is the item a
CBOR_TYPE_STRING? πΈ
bool cbor_isa_array(const cbor_item_t *item)
Does the item have the appropriate major type?
- item β the item
- returns: Is the item an
CBOR_TYPE_ARRAY?
bool cbor_isa_map(const cbor_item_t *item)
Does the item have the appropriate major type?
- item β the item
- returns: Is the item a
CBOR_TYPE_MAP? πΊοΈ
bool cbor_isa_tag(const cbor_item_t *item)
Does the item have the appropriate major type?
- item β the item
- returns: Is the item a
CBOR_TYPE_TAG? π·οΈ
bool cbor_isa_float_ctrl(const cbor_item_t *item)
Does the item have the appropriate major type?
- item β the item
- returns: Is the item a
CBOR_TYPE_FLOAT_CTRL? π
π§ Logical queries
These ones give you info about the item type from a higher-level perspective β it's giving β¨big pictureβ¨ energy π¦
bool cbor_is_int(const cbor_item_t *item)
Is the item an integer, either positive or negative?
- item β the item
- returns: Is the item an integer, either positive or negative? π’
bool cbor_is_float(const cbor_item_t *item)
Is the item an a floating point number?
- item β the item
- returns: Is the item a floating point number? π
bool cbor_is_bool(const cbor_item_t *item)
Is the item an a boolean?
- item β the item
- returns: Is the item a boolean? β
bool cbor_is_null(const cbor_item_t *item)
Does this item represent null.
Warning: This is in no way related to the value of the pointer. Passing a null pointer will most likely result in a crash π fr fr, don't do it.
- item β the item
- returns: Is the item (CBOR logical) null?
bool cbor_is_undef(const cbor_item_t *item)
Does this item represent undefined.
Warning: Care must be taken to distinguish nulls and undefined values in C. π they are not the same thing, bestie.
- item β the item
- returns: Is the item (CBOR logical) undefined?
πΎ Memory management and reference counting
So because of the nature of its domain, libcbor will need to work with heap memory. The stateless decoder and encoder doesn't allocate any memory at all π
If you have specific requirements, you should consider rolling your own driver for the stateless API.
π Using custom allocator
libcbor gives you the ability to provide your own implementations of malloc, realloc, and free. This can be useful if you are using a custom allocator throughout your application, or if you want to implement custom policies (e.g. tighter restrictions on the amount of allocated memory). obsessed, honestly π
cbor_set_allocs(malloc, realloc, free);
void cbor_set_allocs(_cbor_malloc_t custom_malloc, _cbor_realloc_t custom_realloc, _cbor_free_t custom_free)
Sets the memory management routines to use.
By default, libcbor will use the standard library malloc, realloc, and free.
Note:reallocimplementation must correctly supportNULLreallocation (see e.g. <http://en.cppreference.com/w/c/memory/realloc>)
Warning: This function modifies the global state and should therefore be used accordingly. Changing the memory handlers while allocated items exist will result in a free/malloc mismatch. This function is not thread safe with respect to both itself and all the other libcbor functions that work with the heap. π yeah. global state. iconic but dangerous.
- custom_malloc β
mallocimplementation - custom_realloc β
reallocimplementation - custom_free β
freeimplementation
π Reference counting
As CBOR items may require complex cleanups at the end of their lifetime, there is a reference counting mechanism in place. This also enables a very simple GC when integrating libcbor into a managed environment. Every item starts its life (by either explicit creation, or as a result of parsing) with reference count set to 1. When the refcount reaches zero, it will be destroyed π
Items containing nested items will be destroyed recursively β the refcount of every nested item will be decreased by one.
The destruction is synchronous and renders any pointers to items with refcount zero invalid immediately after calling cbor_decref().
cbor_item_t *cbor_incref(cbor_item_t *item)
Increases the item's reference count by one.
Constant complexity; items referring to this one or items being referred to are not updated.
This function can be used to extend reference counting to client code.
- item β Reference to an item
- returns: The input item π«Ά
void cbor_decref(cbor_item_t **item)
Decreases the item's reference count by one, deallocating the item if needed.
In case the item is deallocated, the reference count of all items this item references will also be cbor_decref'ed recursively.
- item β Reference to an item. Will be set to
NULLif deallocated
void cbor_intermediate_decref(cbor_item_t *item)
Decreases the item's reference count by one, deallocating the item if needed.
Convenience wrapper for cbor_decref when its set-to-null behavior is not needed.
- item β Reference to an item
size_t cbor_refcount(const cbor_item_t *item)
Get the item's reference count.
Todo: Add some inline examples for reference counting
Warning: This does not account for transitive references.
- item β the item
- returns: the reference count
cbor_item_t *cbor_move(cbor_item_t *item)
Provides CPP-like move construct.
Decreases the reference count by one, but does not deallocate the item even if its refcount reaches zero. This is useful for passing intermediate values to functions that increase reference count. Should only be used with functions that incref their arguments.
Warning: If the item is moved without correctly increasing the reference count afterwards, the memory will be leaked. π leak era. we don't want that.
- item β Reference to an item
- returns: the item with reference count decreased by one
cbor_item_t *cbor_copy(cbor_item_t *item)
Take a deep copy of an item. π¦
...and bestie, the way this copy works? chef's kiss π
All items this item points to β array and map members, string chunks, tagged items β get copied recursively using cbor_copy. The new item doesn't alias or point to anything from the original item, and all the reference counts in the new structure are set to one. Fresh start, no shared trauma, we love to see it. π«Ά
- item β the item to copy
- returns a reference to the new item, whose reference count is initialized to one
- returns
NULLif memory allocation fails π
cbor_item_t *cbor_copy_definite(cbor_item_t *item)
Copy the item with all items converted to definite length equivalents. Same deep copy semantics as cbor_copy above, no cap.
- item β the item to copy
- returns a reference to the new item, whose reference count is initialized to one
- returns
NULLif memory allocation fails π
π Decoding π
Okay so this diagram? It's giving β¨architectureβ¨. It shows how all the different parts of libcbor relate to each other from the decoding standpoint. Study it, bestie, it's the map:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β Client application β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββ
β β β
β β ββββββββββββββββββββββββββββββββββββββββββββ
β β β β
β β β Manipulation routines β
β β β β
β βββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββββββ
β β β β β β
β β β β βββββββββββββββ«βββββββββββ¬ββββββββββββββββββββ΄ββ
β β β CDS β β β β
β β β β PDS β PDS PDS
β β β β β β β β
β β βββββββββββββββββββ ββββββββββββββββββββββ ββββββββββββββββββββββββββββββ
β β β β β β β β
β β β Custom driver β β β Streaming driver β β β Default driver β β CD
β β β β β β β β
βββββββββββββ βββββββββββββββββββ ββββββββββββββββββββββ ββββββββββββββββββββββββββββββ
β β β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β Stateless eventβdriven decoder β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
(PSD = Provided Data Structures, CDS = Custom Data Structures)
So like, this section is dealing with the API labeled as the "Default driver" in that diagram. Translation: these are the routines that decode complete libcbor data items for you. π
cbor_item_t *cbor_load(cbor_data source, size_t source_size, struct cbor_load_result *result)
Loads a data item from a buffer. Yes queen, just hand it a buffer. π¦
- source β the buffer
- source_size β (the size of it, bestie)
- result β
[out]result indicator.CBOR_ERR_NONEon success β¨ - returns the decoded CBOR item, whose reference count is initialized to one
- returns
NULLon failure β and in that case,resultcontains the location and description of the error. It's telling on itself. π
π§° Associated data structures π§°
enum cbor_error_code
Possible decoding errors. The full roster:
CBOR_ERR_NONECBOR_ERR_NOTENOUGHDATACBOR_ERR_NODATACBOR_ERR_MALFORMATEDCBOR_ERR_MEMERRORβ memory error, item allocation failed. Is it too big for your allocator? π€ asking for a friendCBOR_ERR_SYNTAXERRORβ the stack parsing algorithm failed. Not me, not my stack, it was already like that. π
struct cbor_load_result
High-level decoding result. Its public members:
struct cbor_error errorβ the error indicatorsize_t readβ number of bytes read
struct cbor_error
High-level decoding error. Its public members:
size_t positionβ approximate positioncbor_error_code codeβ the description
π₯ Encoding π₯
The easiest way to encode data items is using the cbor_serialize() or cbor_serialize_alloc() functions. Slay, no stress:
size_t cbor_serialize(const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size)
Serialize the given item.
- item β a data item
- buffer β buffer to serialize to
- buffer_size β size of the buffer
- returns the length of the result, or
0on failure
size_t cbor_serialize_alloc(const cbor_item_t *item, unsigned char **buffer, size_t *buffer_size)
Serialize the given item, allocating buffers as needed. So convenient of it. π
Since libcbor v0.10, the return value is always the same as buffer_size (if provided, see https://github.com/PJK/libcbor/pull/251/). New clients should just ignore the return value, bestie, it's not the moment.
β οΈ Warning: it is the caller's responsibility to free the buffer using an appropriate free implementation. That's you. Yes, you. No takebacks. π
- item β a data item
- buffer β
[out]buffer containing the result - buffer_size β
[out]size of the buffer, or0on memory allocation failure - returns the length of the result in bytes
- returns
0on memory allocation failure, in which case buffer isNULL
To figure out how many bytes you need to serialize an item, use cbor_serialized_size():
size_t cbor_serialized_size(const cbor_item_t *item)
Compute the length (in bytes) of the item when serialized using cbor_serialize. Time complexity is proportional to the number of nested items, so keep that in mind before you go nesting like it's 2014 tumblr. π¦
- item β a data item
- returns the length (
>= 1) of the item when serialized. Returns0if the length overflowssize_t. π
π Type-specific serializers π
In case you already know the type of the item you want to serialize beforehand, you can use one of the type-specific serializers. iykyk. β¨
π¨ Note: unless compiled in debug mode, these do not verify the type. Passing an incorrect item will result in undefined behavior. Not a cute look, bestie, be careful.
size_t cbor_serialize_uint(const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size)
Serialize a uint.
- item β a
uint - buffer β
[out]buffer to serialize to - buffer_size β size of the buffer
- returns the length of the result
- returns
0if the buffer_size doesn't fit the result
size_t cbor_serialize_negint(const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size)
Serialize a negint.
- item β a
negint - buffer β
[out]buffer to serialize to - buffer_size β size of the buffer
- returns the length of the result
- returns
0if the buffer_size doesn't fit the result
size_t cbor_serialize_bytestring(const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size)
Serialize a bytestring.
- item β a
bytestring - buffer β
[out]buffer to serialize to - buffer_size β size of the buffer
- returns the length of the result
- returns
0if the buffer_size doesn't fit the result. Heads up: the buffer may still be modified. π
size_t cbor_serialize_string(const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size)
Serialize a string.
- item β a
string - buffer β [out] the buffer you're serializing into, bestie
- buffer_size β how big that buffer is
- Returns β the length of the result π«
- Returns β
0when the buffer_size doesn't fit the result. And heads up: the buffer may still be modified π
β¨ cbor_serialize_array()
size_t cbor_serialize_array(const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size)
Serializes an array.
- item β an array
- buffer β [out] buffer to serialize to
- buffer_size β size of the buffer
- Returns β length of the result
- Returns β
0if the buffer_size doesn't fit the result. The buffer may still be modified
β¨ cbor_serialize_map()
size_t cbor_serialize_map(const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size)
Serializes a map.
- item β a map
- buffer β [out] buffer to serialize to
- buffer_size β size of the buffer
- Returns β length of the result
- Returns β
0if the buffer_size doesn't fit the result. The buffer may still be modified
β¨ cbor_serialize_tag()
size_t cbor_serialize_tag(const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size)
Serializes a tag.
- item β a tag
- buffer β [out] buffer to serialize to
- buffer_size β size of the buffer
- Returns β length of the result
- Returns β
0if the buffer_size doesn't fit the result. The buffer may still be modified
β¨ cbor_serialize_float_ctrl()
size_t cbor_serialize_float_ctrl(const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size)
Serialize a.
(yes. that's the whole description. "Serialize a." that's it. the docs said what they said and we respect it π )
- item β a float or ctrl
- buffer β [out] buffer to serialize to
- buffer_size β size of the buffer
- Returns β length of the result
- Returns β
0if the buffer_size doesn't fit the result
ngl notice this one does not say the buffer may still be modified. that's not me forgetting, that's the actual doc. read it exactly as written π
π Streaming Decoding
okay so this is the fun part. libcbor exposes a stateless decoder that reads a stream of input bytes out of a buffer and invokes user-provided callbacks as it decodes the input π¦
struct cbor_decoder_result cbor_stream_decode(cbor_data source, size_t source_size, const struct cbor_callbacks *callbacks, void *context)
Stateless decoder.
It'll try parsing the source and will invoke the appropriate callback on success. Decodes one item at a time. No memory allocations occur. (iconic behavior honestly β¨)
- source β input buffer
- source_size β length of the buffer
- callbacks β the callback bundle
- context β an arbitrary pointer to allow for maintaining context
For example, when cbor_stream_decode() encounters a 1B unsigned integer, it will invoke the function pointer stored in cbor_callbacks.uint8. Complete usage example: examples/streaming_parser.c (https://github.com/PJK/libcbor/blob/master/examples/streaming_parser.c)
The callbacks are defined by
struct cbor_callbacks
Callback bundle β passed to the decoder.
Public Members:
cbor_int8_callback uint8β unsigned intcbor_int16_callback uint16β unsigned intcbor_int32_callback uint32β unsigned intcbor_int64_callback uint64β unsigned intcbor_int64_callback negint64β negative intcbor_int32_callback negint32β negative intcbor_int16_callback negint16β negative intcbor_int8_callback negint8β negative intcbor_simple_callback byte_string_startβ definite byte stringcbor_string_callback byte_stringβ indefinite byte string startcbor_string_callback stringβ definite stringcbor_simple_callback string_startβ indefinite string startcbor_simple_callback indef_array_startβ definite arraycbor_collection_callback array_startβ indefinite arraycbor_simple_callback indef_map_startβ definite mapcbor_collection_callback map_startβ indefinite mapcbor_int64_callback tagβ tagscbor_float_callback float2β half floatcbor_float_callback float4β single floatcbor_double_callback float8β double floatcbor_simple_callback undefinedβ undefcbor_simple_callback nullβ nullcbor_bool_callback booleanβ boolcbor_simple_callback indef_breakβ indefinite item break
When building custom sets of callbacks, feel free to start from
const struct cbor_callbacks cbor_empty_callbacks
Dummy callback bundle β does nothing. (the "respond to nothing, serve looks" bundle π«Ά)
π Callback types definition
typedef void (*cbor_int8_callback)(void*, uint8_t)
Callback prototype.
typedef void (*cbor_int16_callback)(void*, uint16_t)
Callback prototype.
typedef void (*cbor_int32_callback)(void*, uint32_t)
Callback prototype.
typedef void (*cbor_int64_callback)(void*, uint64_t)
Callback prototype.
typedef void (*cbor_simple_callback)(void*)
Callback prototype.
typedef void (*cbor_string_callback)(void*, cbor_data, uint64_t)
Callback prototype.
typedef void (*cbor_collection_callback)(void*, uint64_t)
Callback prototype.
typedef void (*cbor_float_callback)(void*, float)
Callback prototype.
typedef void (*cbor_double_callback)(void*, double)
Callback prototype.
typedef void (*cbor_bool_callback)(void*, bool)
Callback prototype.
π₯ Streaming Encoding
cbor/encoding.h (https://github.com/PJK/libcbor/blob/master/src/cbor/encoding.h) exposes a low-level encoding API to encode CBOR objects on the fly. Unlike cbor_serialize(), these functions take logical values (integers, floats, strings, etc.) instead of cbor_item_t. The client is responsible for constructing the compound types correctly (e.g. terminating arrays). no cap, that part is on you π
Streaming encoding is typically used to create an streaming (indefinite length) CBOR strings, byte strings, arrays, and maps. Complete example: examples/streaming_array.c (https://github.com/PJK/libcbor/blob/master/examples/streaming_array.c)
size_t cbor_encode_uint8(uint8_t value, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_uint16(uint16_t value, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_uint32(uint32_t value, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_uint64(uint64_t value, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_uint(uint64_t value, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_negint8(uint8_t value, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_negint16(uint16_t value, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_negint32(uint32_t value, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_negint64(uint64_t value, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_negint(uint64_t value, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_bytestring_start(size_t length, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_indef_bytestring_start(unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_string_start(size_t length, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_indef_string_start(unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_array_start(size_t length, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_indef_array_start(unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_map_start(size_t length, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_indef_map_start(unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_tag(uint64_t value, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_bool(bool value, unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_null(unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_undef(unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_half(float value, unsigned char *buffer, size_t buffer_size)
π cbor_encode_half()
Encodes a half-precision float.
Since there is no native representation or semantics for half floats in the language, we use single-precision floats, as every value that can be expressed as a half-float can also be expressed as a float.
This however means that not all floats passed to this function can be unambiguously encoded. The behavior is as follows:
- Infinity, NaN are preserved
- Zero is preserved
- Denormalized numbers keep their sign bit and 10 most significant bit of the significand
- All other numbers.
- If the logical value of the exponent is
< -24, the output is zero - If the logical value of the exponent is between
-23and-14, the output is cut off to represent the 'magnitude' of the input, by which we mean(-1)^{signbit} x 1.0e{exponent}. The value in the significand is lost. - In all other cases, the sign bit, the exponent, and 10 most significant bits of the significand are kept
- If the logical value of the exponent is
bestie that middle bullet is literally lossy β when the exponent lands between-23and-14your significand is gone gone. not my rules, that's just what it says π
Note: Signaling NaNs are encoded as a standard, "quiet" NaN.
size_t cbor_encode_single(float value, unsigned char *buffer, size_t buffer_size)
Encodes a single precision float. π«§
Note: Signaling NaNs are encoded as a standard, "quiet" NaN.
size_t cbor_encode_double(double value, unsigned char *buffer, size_t buffer_size)
Encodes a double precision float. β¨
Note: Signaling NaNs are encoded as a standard, "quiet" NaN.
size_t cbor_encode_break(unsigned char *buffer, size_t buffer_size)
size_t cbor_encode_ctrl(uint8_t value, unsigned char *buffer, size_t buffer_size)
Types 0 & 1 β Positive and negative integers</p> π’
CBOR has two types of integers β positive (which you can lowkey just treat as unsigned, bestie) and negative. And there are four possible widths for an integer: 1, 2, 4, or 8 bytes. These are represented by:
enum cbor_int_width
Possible widths of CBOR_TYPE_UINT <#data_8h_1a3a931b40fc31b51cccfd1bc3dc5fc5d6a2137d55d8028e37ff4319a633ef0634f> items.
Values:
enumerator CBOR_INT_8enumerator CBOR_INT_16enumerator CBOR_INT_32enumerator CBOR_INT_64
Type 0 - positive integers β
- Corresponding
cbor_type<#_CPPv49cbor_type>βCBOR_TYPE_UINT - Number of allocations β One per lifetime
- Storage requirements β
sizeof(cbor_item_t) + sizeof(uint*_t)
Note: once a positive integer has been created, its width cannot be changed. Periodt. π
Type 1 - negative integers π
- Corresponding
cbor_type<#_CPPv49cbor_type>βCBOR_TYPE_NEGINT - Number of allocations β One per lifetime
- Storage requirements β
sizeof(cbor_item_t) + sizeof(uint*_t)
Note: once a positive integer has been created, its width cannot be changed. (yes, same energy as above, we're just built like that) π
Type 0 & 1 π«Ά
Because their semantics are basically twins, the functions below work for both Type 0 and Type 1 items. You can convert between them freely using the conversion functions <#dealing-with-signedness>.
The actual type of the integer can be checked using the item types API <>.
An integer item gets created with one of the four widths. And because an integer's storage is bundled together with the handle </internal#c.cbor_item_t.data>, the width cannot be changed over its lifetime. Sorry bestie, no glow-up for widths. π
Warning: So here's the thing β CBOR negative integers represent integers in the range[-1, -2^N], which makes thecbor_set_uintAPI kinda counter-intuitive, because the resulting logical value is 1 less. This behavior is necessary so you can uniformly manipulate the full range of permitted values. For example, this snippet:c cbor_item_t * item = cbor_new_int8(); cbor_mark_negint(item); cbor_set_uint8(0);will produce an item with the logical value of-1. But ngl there's an upside too: there is only one representation of zero. iconic. π
Building new items π¨
cbor_item_t <#_CPPv411cbor_item_t> *cbor_build_uint8(uint8_t value)
Constructs a new positive integer.
- param
valueβ the value to use - return new positive integer or
NULLon memory allocation failure
cbor_item_t <#_CPPv411cbor_item_t> *cbor_build_uint16(uint16_t value)
Constructs a new positive integer.
- param
valueβ the value to use - return new positive integer or
NULLon memory allocation failure
cbor_item_t <#_CPPv411cbor_item_t> *cbor_build_uint32(uint32_t value)
Constructs a new positive integer.
- param
valueβ the value to use - return new positive integer or
NULLon memory allocation failure
cbor_item_t <#_CPPv411cbor_item_t> *cbor_build_uint64(uint64_t value)
Constructs a new positive integer.
- param
valueβ the value to use - return new positive integer or
NULLon memory allocation failure
Retrieving values π
uint8_t cbor_get_uint8(const cbor_item_t <#_CPPv411cbor_item_t> *item)
Extracts the integer value.
- param
itemβ positive or negative integer - return the value
uint16_t cbor_get_uint16(const cbor_item_t <#_CPPv411cbor_item_t> *item)
Extracts the integer value.
- param
itemβ positive or negative integer - return the value
uint32_t cbor_get_uint32(const cbor_item_t <#_CPPv411cbor_item_t> *item)
Extracts the integer value.
- param
itemβ positive or negative integer - return the value
uint64_t cbor_get_uint64(const cbor_item_t <#_CPPv411cbor_item_t> *item)
Extracts the integer value.
- param
itemβ positive or negative integer - return the value
Setting values βοΈ
void cbor_set_uint8(cbor_item_t <#_CPPv411cbor_item_t> *item, uint8_t value)
Assigns the integer value.
- param
itemβ positive or negative integer item - param
valueβ the value to assign. For negative integer, the logical value is-value - 1
void cbor_set_uint16(cbor_item_t <#_CPPv411cbor_item_t> *item, uint16_t value)
Assigns the integer value.
- param
itemβ positive or negative integer item - param
valueβ the value to assign. For negative integer, the logical value is-value - 1
void cbor_set_uint32(cbor_item_t <#_CPPv411cbor_item_t> *item, uint32_t value)
Assigns the integer value.
- param
itemβ positive or negative integer item - param
valueβ the value to assign. For negative integer, the logical value is-value - 1
void cbor_set_uint64(cbor_item_t <#_CPPv411cbor_item_t> *item, uint64_t value)
Assigns the integer value.
- param
itemβ positive or negative integer item - param
valueβ the value to assign. For negative integer, the logical value is-value - 1
Dealing with width π
cbor_int_width <#_CPPv414cbor_int_width> cbor_int_get_width(const cbor_item_t <#_CPPv411cbor_item_t> *item)
Queries the integer width.
- param
itemβ positive or negative integer item - return the width
Dealing with signedness π¦
void cbor_mark_uint(cbor_item_t <#_CPPv411cbor_item_t> *item)
Marks the integer item as a positive integer.
The data value is not changed.
- param
itemβ positive or negative integer item
void cbor_mark_negint(cbor_item_t <#_CPPv411cbor_item_t> *item)
Marks the integer item as a negative integer.
The data value is not changed.
- param
itemβ positive or negative integer item
Creating new items π±
cbor_item_t <#_CPPv411cbor_item_t> *cbor_new_int8(void)
Allocates new integer with 1B width.
The width cannot be changed once allocated.
- return new positive integer or
NULLon memory allocation failure. The value is not initialized
cbor_item_t <#_CPPv411cbor_item_t> *cbor_new_int16(void)
Allocates new integer with 2B width.
The width cannot be changed once allocated.
- return new positive integer or
NULLon memory allocation failure. The value is not initialized
cbor_item_t <#_CPPv411cbor_item_t> *cbor_new_int32(void)
Allocates new integer with 4B width.
The width cannot be changed once allocated.
- return new positive integer or
NULLon memory allocation failure. The value is not initialized
cbor_item_t <#_CPPv411cbor_item_t> *cbor_new_int64(void)
Allocates new integer with 8B width.
The width cannot be changed once allocated.
- return new positive integer or
NULLon memory allocation failure. The value is not initialized
Type 2 β Byte strings π§
CBOR byte strings are literally just an (ordered) series of bytes with no further interpretation (unless there's a tag <>). A byte string's length may or may not be known during encoding, and you can tell these two kinds apart with cbor_bytestring_is_definite() and cbor_bytestring_is_indefinite() respectively. π
If a byte string is indefinite, it gets encoded as a series of definite byte strings. Those are called "chunks". For example, the encoded item:
0xf5 Start indefinite byte string
0x41 Byte string (1B long)
0x00
0x41 Byte string (1B long)
0xff
0xff "Break" control token
β¦represents two bytes, 0x00 and 0xff. Okay so here's the tradeoff bestie: on one hand it enables streaming messages even before they are fully generated β¨, on the other hand it adds more complexity to the client code π. Iconic behaviour, honestly.
ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ
β Corresponding cbor_type <# β CBOR_TYPE_BYTESTRING β
β _CPPv49cbor_type> β β
ββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββ€
β Number of allocations β One plus any manipulations β
β (definite) β with the data β
ββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββ€
β Number of allocations β One plus logarithmically β
β (indefinite) β many reallocations β
β β relative to chunk count β
ββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββ€
β Storage requirements β sizeof(cbor_item_t) + β
β (definite) β length(handle) β
ββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββ€
β Storage requirements β sizeof(cbor_item_t) * (1 + β
β (indefinite) β chunk_count) + chunks β
ββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββ
πΈ Getting metadata
size_t cbor_bytestring_length(const cbor_item_t <#_CPPv411cbor_item_t> *item)β hands you the length of the binary data π . Definite byte strings only, no exceptions bestie.- item β a definite bytestring.
- Returns β length of the binary data. Zero if no chunk has been attached yet.
bool cbor_bytestring_is_definite(const cbor_item_t <#_CPPv411cbor_item_t> *item)β Is the byte string definite? π«Ά- item β a byte string.
- Returns β Is the byte string definite?
bool cbor_bytestring_is_indefinite(const cbor_item_t <#_CPPv411cbor_item_t> *item)β Is the byte string indefinite? π- item β a byte string.
- Returns β Is the byte string indefinite?
size_t cbor_bytestring_chunk_count(const cbor_item_t <#_CPPv411cbor_item_t> *item)β Get the number of chunks this string consist of π.- item β A indefinite bytestring.
- Returns β The chunk count. 0 for freshly created items.
π¦ Reading data
cbor_mutable_data cbor_bytestring_handle(const cbor_item_t <#_CPPv411cbor_item_t> *item)β Get the handle to the binary data β¨. Definite items only. Modifying the data is allowed β but in that case, the caller takes responsibility for the effect on items this item might be a part of π.- item β A definite byte string.
- Returns β The address of the underlying binary data.
- Returns β
NULLif no data have been assigned yet.
cbor_item_t <#_CPPv411cbor_item_t> **cbor_bytestring_chunks_handle(const cbor_item_t <#_CPPv411cbor_item_t> *item)β Get the handle to the array of chunks π.- Manipulations with the memory block (e.g. sorting it) are allowed, but the validity and the number of chunks must be retained.
- item β A indefinite byte string.
- Returns β array of
cbor_bytestring_chunk_count <#bytestrings_8h_1ad02102832d452fd5a5cfa96272b1095d>definite bytestrings.
π§ Creating new items
cbor_item_t <#_CPPv411cbor_item_t> *cbor_new_definite_bytestring(void)β Creates a new definite byte string π«. The handle is initialized toNULLand length to0.- Returns β Reference to the new bytestring item. The item's reference count is initialized to one.
- Returns β
NULLif memory allocation fails π.
cbor_item_t <#_CPPv411cbor_item_t> *cbor_new_indefinite_bytestring(void)β Creates a new indefinite byte string π. The chunks array is initialized toNULLand chunk count to0.- Returns β Reference to the new bytestring item. The item's reference count is initialized to one.
- Returns β
NULLif memory allocation fails π.
π Building items
cbor_item_t <#_CPPv411cbor_item_t> *cbor_build_bytestring(cbor_data handle, size_t length)β Creates a new byte string and initializes it π₯. The handle will be copied to a newly allocated block, so no sneaky aliasing.- handle β Block of binary data.
- length β Length of data.
- Returns β Reference to the new bytestring item. The item's reference count is initialized to one.
- Returns β
NULLif memory allocation fails.
π Manipulating existing items
void cbor_bytestring_set_handle(cbor_item_t <#_CPPv411cbor_item_t> *item, cbor_mutable_data data, size_t length)β Set the handle to the binary data.- item β A definite byte string.
- data β The memory block. The caller gives up the ownership of the block. libcbor will deallocate it when appropriate using the
freeimplementation configured usingcbor_set_allocs. - length β Length of the data block.
bool cbor_bytestring_add_chunk(cbor_item_t <#_CPPv411cbor_item_t> *item, cbor_item_t <#_CPPv411cbor_item_t> *chunk)β Appends a chunk to the bytestring πΈ. Indefinite byte strings only. May realloc the chunk storage (so, y'know, expect some movement).- item β An indefinite byte string.
- chunk β A definite byte string. Its reference count will be be increased by one. (yes, "be be", we said what we said π )
- Returns β
trueon success,falseon realloc failure. In that case, the refcount of chunk is not increased and the item is left intact.
π Type 3 β UTF-8 strings
CBOR strings have the same structure as Type 2 β Byte strings π. Literally the same blueprint, different vibe.
ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ
β Corresponding cbor_type <# β CBOR_TYPE_STRING β
β _CPPv49cbor_type> β β
ββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββ€
β Number of allocations β One plus any manipulations β
β (definite) β with the data β
ββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββ€
β Number of allocations β One plus logarithmically β
β (indefinite) β many reallocations β
β β relative to chunk count β
ββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββ€
β Storage requirements β sizeof(cbor_item_t) + β
β (definite) β length(handle) β
ββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββ€
β Storage requirements β sizeof(cbor_item_t) * (1 + β
β (indefinite) β chunk_count) + chunks β
ββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββ
β UTF-8 encoding validation
libcbor considers UTF-8 encoding validity to be a part of the well-formedness notion of CBOR, and therefore invalid UTF-8 strings will be rejected by the parser π«. Strings created by the user are not checked β so bestie, that one's on you.
πΈ Getting metadata
size_t cbor_string_length(const cbor_item_t <#_CPPv411cbor_item_t> *item)β Returns the length of the underlying string in bytes π. There can be fewer unicode character than bytes (seecbor_string_codepoint_count). For definite strings only.- item β a definite string.
- Returns β length of the string. Zero if no chunk has been attached yet.
bool cbor_string_is_definite(const cbor_item_t <#_CPPv411cbor_item_t> *item)β Is the string definite? π«Ά- item β a string.
- Returns β Is the string definite?
bool cbor_string_is_indefinite(const cbor_item_t <#_CPPv411cbor_item_t> *item)β Is the string indefinite? π- item β a string.
- Returns β Is the string indefinite?
size_t cbor_string_chunk_count(const cbor_item_t <#_CPPv411cbor_item_t> *item)β Get the number of chunks this string consist of π.- item β A indefinite string.
- Returns β The chunk count. 0 for freshly created items.
π¦ Reading data
cbor_mutable_data cbor_string_handle(const cbor_item_t <#_CPPv411cbor_item_t> *item)β Get the handle to the underlying string β¨. Definite items only. Modifying the data is allowed β but in that case, the caller takes responsibility for the effect on items this item might be a part of π.- item β A definite string.
- Returns β The address of the underlying string.
- Returns β
NULLif no data have been assigned yet.
cbor_item_t <#_CPPv411cbor_item_t> **cbor_string_chunks_handle(const cbor_item_t <#_CPPv411cbor_item_t> *item)β Get the handle to the array of chunks π.- Manipulations with the memory block (e.g. sorting it) are allowed, but the validity and the number of chunks must be retained.
- item β A indefinite string.
- Returns β array of
cbor_string_chunk_count <#strings_8h_1ad8062db21711a51ae520cc1e4f5fe5ab>definite strings.
β¨ Creating new items β¨
π± cbor_new_definite_string
cbor_item_t *cbor_new_definite_string(void)
Makes you a shiny new definite string. The handle gets initialized to NULL and the length to 0 β clean slate energy, we love to see it π
- return β reference to the new string item. The item's reference count is initialized to one.
- return β
NULLif memory allocation fails π
π± cbor_new_indefinite_string
cbor_item_t *cbor_new_indefinite_string(void)
Makes you a shiny new indefinite string. The chunks array gets initialized to NULL and chunkcount to 0.
- return β reference to the new string item. The item's reference count is initialized to one.
- return β
NULLif memory allocation fails
π οΈ Building items
π cbor_build_string
cbor_item_t *cbor_build_string(const char *val)
Creates a new string and initializes it β doing two things at once, main character behaviour honestly β¨
The data from val will be copied to a newly allocated memory block.
Heads up bestie: valid UTF-8 strings do not contain null bytes, so this routine is correct for all valid inputs. But if your input is not guaranteed to be valid, use cbor_build_stringn instead. Don't be messy, k? π
- param val β a null-terminated UTF-8 string
- return β reference to the new string item. The item's reference count is initialized to one.
- return β
NULLif memory allocation fails
π Manipulating existing items
cbor_string_set_handle
void cbor_string_set_handle(cbor_item_t *item, cbor_mutable_data data, size_t length)
Sets the handle to the underlying string.
The data is assumed to be a valid UTF-8 string. If the string is non-empty and invalid, cbor_string_codepoint_count will return 0.
β οΈ Warning: using a pointer to a stack allocated constant is a super common mistake. The lifetime of the string will expire when it goes out of scope and the CBOR item will be left inconsistent. It's giving dangling pointer, it's giving chaos. Don't. π
- param item β a definite string
- param data β the memory block. The caller gives up the ownership of the block. libcbor will deallocate it when appropriate using its free function
- param length β length of the data block
cbor_string_add_chunk
bool cbor_string_add_chunk(cbor_item_t *item, cbor_item_t *chunk)
Appends a chunk to the string. Indefinite strings only, okurrr π
May realloc the chunk storage.
- param item β an indefinite string
- param chunk β a definite string item. Its reference count will be increased by one.
- return β
trueon success.falseon memory allocation failure. In that case, the refcount ofchunkis not increased and theitemis left intact π«Ά
π’ Type 4 β Arrays
CBOR arrays, just like byte strings <> and strings <>, can be encoded either as definite, or as indefinite. Definite arrays have a fixed size which is stored in the header, whereas indefinite arrays do not and are terminated by a special "break" byte instead. Periodt.
Arrays are explicitly created or decoded as definite or indefinite and will be encoded using the corresponding wire representation, regardless of whether the actual size is known at the time of encoding.
π Note: indefinite arrays can be conveniently used with streaming decoding <> and encoding <>.
Okay so here's the stats breakdown, bestie β no table, just vibes and facts:
- Corresponding
cbor_typeβCBOR_TYPE_ARRAY - Number of allocations (definite) β Two plus any manipulations with the data
- Number of allocations (indefinite) β Two plus logarithmically many reallocations relative to additions
- Storage requirements (definite) β
(sizeof(cbor_item_t) + 1) * size - Storage requirements (indefinite) β
<= sizeof(cbor_item_t) + sizeof(cbor_item_t) * size * BUFFER_GROWTH
π Examples
0x9f Start indefinite array
0x01 Unsigned integer 1
0xff "Break" control token
0x9f Start array, 1B length follows
0x20 Unsigned integer 32
... 32 items follow
π Getting metadata
cbor_array_size
size_t cbor_array_size(const cbor_item_t *item)
Get the number of members.
- param item β an array
- return β the number of members
cbor_array_allocated
size_t cbor_array_allocated(const cbor_item_t *item)
Get the size of the allocated storage.
- param item β an array
- return β the size of the allocated storage (number of items)
cbor_array_is_definite
bool cbor_array_is_definite(const cbor_item_t *item)
Is the array definite?
- param item β an array
- return β is the array definite?
cbor_array_is_indefinite
bool cbor_array_is_indefinite(const cbor_item_t *item)
Is the array indefinite?
- param item β an array
- return β is the array indefinite?
π Reading data
cbor_array_handle
cbor_item_t **cbor_array_handle(const cbor_item_t *item)
Get the array contents.
The items may be reordered and modified as long as references remain consistent.
- param item β an array item
- return β an array of
cbor_item_tpointers of sizecbor_array_size
cbor_array_get
cbor_item_t *cbor_array_get(const cbor_item_t *item, size_t index)
Get item by index. Slay, indexing.
Increases the reference count of the underlying item. The returned reference must be released using cbor_decref.
- param item β an array
- param index β the index (zero-based)
- return β reference to the item, or
NULLin case of boundary violation π
β¨ Creating new items β¨
π± cbor_new_definite_array
cbor_item_t *cbor_new_definite_array(size_t size)
Create new definite array.
- param size β number of slots to preallocate
- return β reference to the new array item. The item's reference count is initialized to one.
- return β
NULLif memory allocation fails
π± cbor_new_indefinite_array
cbor_item_t *cbor_new_indefinite_array(void)
Create new indefinite array.
- return β reference to the new array item. The item's reference count is initialized to one.
- return β
NULLif memory allocation fails
π οΈ Modifying items
cbor_array_push
bool cbor_array_push(cbor_item_t *array, cbor_item_t *pushee)
Append to the end. Push it, push it π
For indefinite items, storage may be reallocated. For definite items, only the preallocated capacity is available.
- param array β an array
- param pushee β the item to push. Its reference count will be increased by one.
- return β
trueon success,falseon failure
cbor_array_replace
bool cbor_array_replace(cbor_item_t *item, size_t index, cbor_item_t *value)
Replace item at an index.
The reference to the item being replaced will be released using cbor_decref.
- param item β an array
- param value β the item to assign. Its reference count will be increased by one.
- param index β the index (zero-based)
- return β
trueon success,falseon allocation failure.
cbor_array_set
bool cbor_array_set(cbor_item_t *item, size_t index, cbor_item_t *value)
Set item by index.
If the index is out of bounds, the array is not modified and false is returned. Creating arrays with holes is not possible. No gap year for arrays, bestie π«
- param item β an array
- param value β the item to assign. Its reference count will be increased by one.
- param index β the index (zero-based)
- return β
trueon success,falseon allocation failure.
πΊοΈ Type 5 β Maps
CBOR maps are the plain old associative maps similar JSON objects or Python dictionaries. You know the vibe β keys and values, doing their little pair thing π
Definite maps have a fixed size which is stored in the header, whereas indefinite maps do not and are terminated by a special "break" byte instead.
Map are explicitly created or decoded as definite or indefinite and will be encoded using the corresponding wire representation, regardless of whether the actual size is known at the time of encoding.
π Note: indefinite maps can be conveniently used with streaming decoding <> and encoding <>. Keys and values can simply be output one by one, alternating keys and values. It's giving conveyor belt, it's giving iconic π
β οΈ Warning: literally any CBOR data item can be a legal map key, not just strings. bestie the keys are unhinged and free π
- Corresponding
cbor_typeβCBOR_TYPE_MAP - Number of allocations (definite) β Two plus any manipulations with the data
- Number of allocations (indefinite) β Two plus logarithmically many reallocations relative to additions
- Storage requirements (definite) β
sizeof(cbor_pair) * size + sizeof(cbor_item_t) - Storage requirements (indefinite) β
<= sizeof(cbor_item_t) + sizeof(cbor_pair) * size * BUFFER_GROWTH
β¨ Examples β¨
okay so here's the vibe in bytes π¦
0xbf Start indefinite map (represents {1: 2})
0x01 Unsigned integer 1 (key)
0x02 Unsigned integer 2 (value)
0xff "Break" control token
0xa0 Map of size 0
π Getting metadata π
size_t cbor_map_size(const cbor_item_t *item)
Get the number of pairs. periodt.
- item β A map
- returns β The number of pairs
size_t cbor_map_allocated(const cbor_item_t *item)
Get the size of the allocated storage. it's giving capacity, not count π
- item β A map
- returns β Allocated storage size (as the number of
cbor_pairitems)
bool cbor_map_is_definite(const cbor_item_t *item)
Is this map definite? the vibe check π
- item β A map
- returns β Is this map definite?
bool cbor_map_is_indefinite(const cbor_item_t *item)
Is this map indefinite? the other vibe check β¨
- item β A map
- returns β Is this map indefinite?
π Reading data π
struct cbor_pair *cbor_map_handle(const cbor_item_t *item)
Get the pairs storage. the actual goods π₯
- item β A map
- returns β Array of
cbor_map_sizepairs. Manipulation is possible as long as references remain valid.
π± Creating new items π±
cbor_item_t *cbor_new_definite_map(size_t size)
Create a new definite map. main character energy, no realloc arcs π
- size β The number of slots to preallocate
- returns β Reference to the new map item. The item's reference count is initialized to one.
- returns β
NULLif memory allocation fails π
cbor_item_t *cbor_new_indefinite_map(void)
Create a new indefinite map. it grows as it goes, lowkey iconic π§
- returns β Reference to the new map item. The item's reference count is initialized to one.
- returns β
NULLif memory allocation fails π
π οΈ Modifying items π οΈ
bool cbor_map_add(cbor_item_t *item, struct cbor_pair pair)
Add a pair to the map. slay.
For definite maps, items can only be added to the preallocated space. For indefinite maps, the storage will be expanded as needed.
- item β A map
- pair β The key-value pair to add. Reference count of the
cbor_pair.keyandcbor_pair.valuewill be increased by one. - returns β
trueon success,falseif memory allocation failed (indefinite maps) or the preallocated storage is full (definite maps)
π·οΈ Type 6 β Semantic tags π·οΈ
Tags are additional metadata that can be used to extend or specialize the meaning or interpretation of the other data items. it's giving sticky notes for your data fr fr
For example, one might tag an array of numbers to communicate that it should be interpreted as a vector. cute right?
Please consult the official IANA repository of CBOR tags at https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml for known registered values.
π Please note that libcbor does not understand the semantics of tags and will process all well-formed tags (as defined in https://datatracker.ietf.org/doc/html/rfc8949#name-terminology) regardless of whether they are valid with respect to the data they are applied to. no cap, it does not read the room.
- Corresponding
cbor_typeβCBOR_TYPE_TAG - Number of allocations β One plus any manipulations with the data reallocations relative to chunk count
- Storage requirements β
sizeof(cbor_item_t) + the tagged item
cbor_item_t *cbor_new_tag(uint64_t value)
Create a new tag. fresh tag, who dis πΈ
- value β The tag value (number).
- returns β Reference to the new tag. Its reference count is initialized to one and it points to a
NULLitem. - returns β
NULLif memory allocation fails.
cbor_item_t *cbor_tag_item(const cbor_item_t *tag)
Get the tagged item (what the tag points to). the item the tag is β¨obsessedβ¨ with
Increases the reference count of the underlying item. The returned reference must be released using cbor_decref.
- tag β A
CBOR_TYPE_TAGtag. - returns β Reference to the tagged item.
uint64_t cbor_tag_value(const cbor_item_t *tag)
Get the tag value. the number, not the thing π
- tag β A
CBOR_TYPE_TAGtag. - returns β The tag value (number).
void cbor_tag_set_item(cbor_item_t *tag, cbor_item_t *tagged_item)
Assign a tag to an item. matchmaker matchmaker π
If the tag already points to an item, the pointer will be replaced, without a reference count change on the previous item. TODO: Should we release the reference automatically?
- tag β A
CBOR_TYPE_TAGtag. - tagged_item β The item to tag. Its reference count will be increased by one.
π Type 7 β Floats & control tokens π
This type combines two completely unrelated types of items β floating point numbers and special values such as true, false, null, etc. We refer to these special values as 'control values' or 'ctrls' for short throughout the code. two things in one trench coat, highkey chaotic π
Just like integers, they have different possible width (resulting in different value ranges and precisions).
enum cbor_float_width
Possible widths of CBOR_TYPE_FLOAT_CTRL items.
Values:
CBOR_FLOAT_0β Internal use - ctrl and special values.CBOR_FLOAT_16β Half float.CBOR_FLOAT_32β Single float.CBOR_FLOAT_64β Double.- Corresponding
cbor_typeβCBOR_TYPE_FLOAT_CTRL - Number of allocations β One per lifetime
- Storage requirements β
sizeof(cbor_item_t) + 1/4/8
π Getting metadata π
bool cbor_float_ctrl_is_ctrl(const cbor_item_t *item)
Is this a ctrl value? is it a control token or just a float being a float π
- item β A float or ctrl item
- returns β Is this a ctrl value?
cbor_float_width cbor_float_get_width(const cbor_item_t *item)
Get the float width. how chunky is this float π
- item β A float or ctrl item
- returns β The width.
π Reading data π
okay bestie, first up: getting values OUT of the items you already have. And listen β these getters are picky about width, they will not do the work for you π
float cbor_float_get_float2(const cbor_item_t *item)β grab a half precision float πΈ- The item must have the corresponding width
- item: A half precision float
- returns: half precision value
float cbor_float_get_float4(const cbor_item_t *item)β grab a single precision float β¨- The item must have the corresponding width
- item: A single precision float
- returns: single precision value
double cbor_float_get_float8(const cbor_item_t *item)β grab a double precision float π- The item must have the corresponding width
- item: A double precision float
- returns: double precision value
double cbor_float_get_float(const cbor_item_t *item)β the universal girlie, gets the float value represented as double π- Can be used regardless of the width β she truly does not care
- item: Any float
- returns: double precision value
uint8_t cbor_ctrl_value(const cbor_item_t *item)β reads the control value π- item: A ctrl item
- returns: the simple value
bool cbor_get_bool(const cbor_item_t *item)β get value from a boolean ctrl item π¦- item: A ctrl item
- returns: boolean value
β¨ Creating new items β¨
brand new item, who dis? π«Ά these all hand you back a reference with the reference count already at one, and they all scream NULL if memory allocation fails.
heads up: the width cannot be changed once the item is created. So pick your width like you pick your fit β it's permanent, no cap.
cbor_item_t *cbor_new_ctrl(void)β constructs a new ctrl item π- returns: Reference to the new ctrl item. The item's reference count is initialized to one.
- returns:
NULLif memory allocation fails
cbor_item_t *cbor_new_float2(void)β constructs a new float item π- returns: Reference to the new float item. The item's reference count is initialized to one.
- returns:
NULLif memory allocation fails
cbor_item_t *cbor_new_float4(void)β constructs a new float item β¨- returns: Reference to the new float item. The item's reference count is initialized to one.
- returns:
NULLif memory allocation fails
cbor_item_t *cbor_new_float8(void)β constructs a new float item πΈ- returns: Reference to the new float item. The item's reference count is initialized to one.
- returns:
NULLif memory allocation fails
cbor_item_t *cbor_new_null(void)β constructs new null ctrl item π- returns: Reference to the new null item. The item's reference count is initialized to one.
- returns:
NULLif memory allocation fails
cbor_item_t *cbor_new_undef(void)β constructs new undef ctrl item π§- returns: Reference to the new undef item. The item's reference count is initialized to one.
- returns:
NULLif memory allocation fails
π οΈ Building items π οΈ
same energy as the new-item girlies, but these ones let you hand over the value right at birth. so efficient, we love her π
cbor_item_t *cbor_build_bool(bool value)β constructs new boolean ctrl item π- value: The value to use
- returns: Reference to the new boolean item. The item's reference count is initialized to one.
- returns:
NULLif memory allocation fails
cbor_item_t *cbor_build_ctrl(uint8_t value)β constructs a ctrl item π- value: the value to use
- returns: Reference to the new ctrl item. The item's reference count is initialized to one.
- returns:
NULLif memory allocation fails
cbor_item_t *cbor_build_float2(float value)β constructs a new float π¦- value: the value to use
- returns: Reference to the new float item. The item's reference count is initialized to one.
- returns:
NULLif memory allocation fails
cbor_item_t *cbor_build_float4(float value)β constructs a new float β¨- value: the value to use
- returns: Reference to the new float item. The item's reference count is initialized to one.
- returns:
NULLif memory allocation fails
cbor_item_t *cbor_build_float8(double value)β constructs a new float π₯- value: the value to use
- returns: Reference to the new float item. The item's reference count is initialized to one.
- returns:
NULLif memory allocation fails
πΈ Manipulating existing items πΈ
for when the item already exists and you just wanna change her mind.
void cbor_set_ctrl(cbor_item_t *item, uint8_t value)β assign a control value- item: A ctrl item
- value: The simple value to assign. Please consult the standard for allowed values
β οΈ Warning: It is possible to produce an invalid CBOR value by assigning an invalid value using this mechanism. Please consult the standard before use. like, fr fr, read the standard first, don't just yolo it π
void cbor_set_bool(cbor_item_t *item, bool value)β assign a boolean value to a boolean ctrl item π- item: A ctrl item
- value: The simple value to assign.
void cbor_set_float2(cbor_item_t *item, float value)β assigns a float value πΈ- item: A half precision float
- value: The value to assign
void cbor_set_float4(cbor_item_t *item, float value)β assigns a float value β¨- item: A single precision float
- value: The value to assign
void cbor_set_float8(cbor_item_t *item, double value)β assigns a float value π- item: A double precision float
- value: The value to assign
π§ Half floats π§
okay so CBOR supports two bytes wide ("half-precision") floats, and bestieβ¦ C literally does not have those. the C language said "no <3". so libcbor represents them using float values throughout the API instead, and that's just how it is π
Encoding will be performed by cbor_encode_half(), which will handle any values that cannot be represented as a half-float. she's got it covered, don't stress πΈ
π Signaling NaNs π
Signaling NaNs are always encoded as a standard, "quiet" NaN.
The reason for this simplification is that standard C does not offer a way to handle the signaling payload without assumptions about the host architecture. See https://github.com/PJK/libcbor/issues/336 for more context.
[1] http://softwareengineering.vazexqi.com/files/pattern.html
π§ͺ Tests π§ͺ
Unit tests
There is a comprehensive test suite employing CMocka (http://cmocka.org/). You can run all of them using ctest in the build directory. Individual tests are themselves runnable. Please refer to CTest (http://www.cmake.org/Wiki/CMake/Testing_With_CTest) documentation for detailed information on how to specify particular subset of tests. so many options, so much power π
Testing for memory leaks
Every release is tested for memory correctness π«Ά You can run these tests by passing the -T memcheck flag to ctest.
[1] Project should be configured with-DCMAKE_BUILD_TYPE=Debugto obtain meaningful description of location of the leak. You might also need--dsymutil=yeson OS X.
Code coverage
Every release is inspected using GCOV/LCOV (http://ltp.sourceforge.net/coverage/lcov.php). Platform-independent code should be fully covered by the test suite. Simply run
make coverage
or alternatively run lcov by hand using
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory out
Fuzz testing
Every release is tested using a fuzz test π In this test, a huge buffer filled with random data is passed to the decoder. We require that it either succeeds or fail with a sensible error, without leaking any memory. This is intended to simulate real-world situations where data received from the network are CBOR-decoded before any further processing. chaos but make it responsible β¨
π IETF standard conformance π
libcbor is, generally speaking, a very faithful implementation of IETF RFC 8949 (STD 94) (https://www.rfc-editor.org/info/std94). she ate, honestly. There are, however, some limitations related to the numerical range and precision available in portable C99. not her fault, it's the language's fault π
Bytestring length
There is no explicit limitation of indefinite length byte strings. libcbor will not handle byte strings with more chunks than the maximum value of size_t. On any sane platform, such string would not fit in the memory anyway. It is, however, possible to process arbitrarily long strings and byte strings using the streaming decoder π
[1] https://www.rfc-editor.org/rfc/rfc8949.html#section-3.2.3
"Half-precision" IEEE 754 floats
As of C99 and even C11, there is no standard implementation for 2 bytes floats. libcbor packs them as a float. When encoding, libcbor selects the appropriate wire representation based on metadata and the actual value. This applies both to canonical and normal mode.
For more information on half-float serialization, please refer to the section on Half floats. (scroll up bestie, it's right there π)
π§ Internal mechanics π§
Internal workings of libcbor are mostly derived from the specification. The purpose of this document is to describe technical choices made during design & implementation and to explicate the reasoning behind those choices. iykyk π
β¨ Terminology β¨
Okay bestie, vocab check before we go any further, this part matters:
- MTB β Major Type Byte (<https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1>)
- DST β Dynamically Sized Type. It's giving "type whose storage requirements cannot be determined during compilation" (and yeah, this term originated in the Rust <http://www.rust-lang.org/> community π¦)
π Conventions π
API symbols start with cbor_ or CBOR_ prefix, internal symbols have _cbor_ or _CBOR_ prefix. That's the whole rule. Icons only. π
π Inspiration & related projects π
Most of the API is largely modelled after existing JSON libraries, including
- Jansson <http://www.digip.org/jansson/>
- json-c <https://github.com/json-c/json-c>
- Gnome's JsonGlib <https://wiki.gnome.org/action/show/Projects/JsonGlib?action=show&redirect=JsonGlib>
and also borrowing from
- msgpack-c <https://github.com/msgpack/msgpack-c>
- Google Protocol Buffers <http://code.google.com/p/protobuf/>.
πΈ General notes on the API design πΈ
The API design has two main driving principles:
- Let the client manage the memory as much as possible
- Behave exactly as specified by the standard
Combining these two principles in practice turns out to be quite difficult. Indefinite-length strings, arrays, and maps require client to handle every fixed-size chunk explicitly in order to
- ensure the client never runs out of memory due to libcbor
- use
realloc()sparsely and predictably [1]- provide strong guarantees about its usage (to prevent latency spikes)
- provide APIs to avoid
realloc()altogether
- allow proper handling of (streamed) data bigger than available memory
[1] Reasonable handling of DSTs requires reallocation if the API is to remain sane.
π¦ Coding style π¦
This code loosely follows the Linux kernel coding style <https://www.kernel.org/doc/Documentation/CodingStyle>. Tabs are tabs, and they are 4 characters wide. No cap. π«Ά
π Memory layout π
CBOR is very dynamic in the sense that it contains many data elements of variable length, sometimes even indefinite length. This section describes internal representation of all CBOR data types.
Generally speaking, data items consist of three parts:
- a generic handle,
- the associated metadata,
- and the actual data
type cbor_item_t
Represents the item. Used as an opaque type
cbor_type <#_CPPv49cbor_type> typeβ Type discriminatorsize_t refcountβ Reference counter. Used bycbor_decref() <#_CPPv411cbor_decrefPP11cbor_item_t>,cbor_incref() <#_CPPv411cbor_increfP11cbor_item_t>union cbor_item_metadata <#_CPPv418cbor_item_metadata> metadataβ Union discriminated by type. Contains type-specific metadataunsigned char *dataβ Contains pointer to the actual data. Small, fixed size items (Types 0 & 1 β Positive and negative integers <>, Type 6 β Semantic tags <>, Type 7 β Floats & control tokens <>) are allocated as a single memory block.
Consider the following snippet
cbor_item_t * item = cbor_new_int8();
then the memory is laid out as follows
+-----------+---------------+---------------+-----------------------------------++-----------+
| | | | || |
| type | refcount | metadata | data || uint8_t |
| | | | (= item + sizeof(cbor_item_t)) || |
+-----------+---------------+---------------+-----------------------------------++-----------+
^ ^
| |
+--- item +--- item->data
Dynamically sized types (Type 2 β Byte strings <>, Type 3 β UTF-8 strings <>, Type 4 β Arrays <>, Type 5 β Maps <>) may store handle and data in separate locations. This enables creating large items (e.g byte strings <>) without realloc() or copying large blocks of memory. One simply attaches the correct pointer to the handle. Iconic behaviour honestly β¨
type cbor_item_metadata
Union type of the following members, based on the item type:
struct _cbor_int_metadata int_metadataβ Used both by both Types 0 & 1 β Positive and negative integers <>struct _cbor_bytestring_metadata bytestring_metadatastruct _cbor_string_metadata string_metadatastruct _cbor_array_metadata array_metadatastruct _cbor_map_metadata map_metadatastruct _cbor_tag_metadata tag_metadatastruct _cbor_float_ctrl_metadata float_ctrl_metadata
π₯ Decoding π₯
As outlined in API <>, there decoding is based on the streaming decoder. Essentially, the decoder is a custom set of callbacks for the streaming decoder. That's it, that's the vibe. π
π Changelog π
Template:
[Fix issue X in feature Y](<https://github.com/PJK/libcbor/pull/XXX>) (by [YYY](<https://github.com/YYY>))
β Next β
0.13.0 (2025-08-30)
- [Fix cbor_is_null, cbor_is_undef, cbor_is_bool assertion failing on non-ctrl floats in debug mode](<https://github.com/PJK/libcbor/issues/352>) (bug discovered by <<https://github.com/psturm-swift>>)
- [Add an example for handling of CBOR Sequences](<https://github.com/PJK/libcbor/pull/358>)
- [Use C23/c2x if available](<https://github.com/PJK/libcbor/pull/361>)
- libcbor remains C99 compatible
- When the compiler does not support new standard, C99 will be used, so the change should be backwards compatible
- [Improved introduction documentation and examples](<https://github.com/PJK/libcbor/pull/363>)
- [Add cbor_copy_definite to turn indefinite items into definite equivalents](<https://github.com/PJK/libcbor/pull/364/files>) (proposed by Jacob Teplitsky)
- BUILD BREAKING: [Minimum CMake version set to 3.5](<https://github.com/PJK/libcbor/pull/355>) to [be compatible with CMake 4](<https://github.com/eclipse-ecal/ecal/issues/2041>) ([suggestion](<https://github.com/PJK/libcbor/commit/1183292d4695300785b272532c1e02d68840e4b8#commitcomment-164507943>) by <<https://github.com/hnyman>>)
- See <<https://repology.org/project/cmake/versions>> for support; the vast majority of users should not be affected.
0.12.0 (2025-03-16)
- BUILD BREAKING: [Respect INTERPROCEDURAL_OPTIMIZATION and use the default value](<https://github.com/PJK/libcbor/issues/315>)
- BREAKING: Changes to NaN encoding
- [Fix NaN encoding on Windows](<https://github.com/PJK/libcbor/issues/271>)
- [Fix NaN encoding on mips/mipsel](<https://github.com/PJK/libcbor/issues/329>)
- [Signaling NaNs will from now on be encoded as canonical quiet NaNs](<https://github.com/PJK/libcbor/pull/335>). This was already the existing behavior for half-precision floats
- Decoding is unchanged
- Please note that this is an intermediate state and likely to be revisited (<https://github.com/PJK/libcbor/issues/336>)
- [Make build compatible with CMake FetchContent](<https://github.com/PJK/libcbor/pull/341>) (by [Jan200101](<https://github.com/Jan200101>))
- [Support Bzlmod for Bazel builds](<https://github.com/PJK/libcbor/pull/340>)
- This should significantly simplify including libcbor as a dependency/module in Bazel projects, see <<https://bazel.build/external/migration>>
- Code quality improvements
- [Fix compiler pragmas](<https://github.com/PJK/libcbor/pull/347>) (by [brooksdavis](<https://github.com/brooksdavis>))
- [Fix code style issues](<https://github.com/PJK/libcbor/pull/321>)
- [Fixed bug in cbor2cjson example](<https://github.com/PJK/libcbor/pull/338>) (by [whitehse](<https://github.com/whitehse>))
0.11.0 (2024-02-04)
- [Updated documentation to refer to RFC 8949](<https://github.com/PJK/libcbor/issues/269>)
- Improvements to cbor_describe
- [Bytestring data will now be printed as well](<https://github.com/PJK/libcbor/pull/281>) by [akallabeth](<https://github.com/akallabeth>)
- [Formatting consistency and clarity improvements](<https://github.com/PJK/libcbor/pull/285>)
- [Fix cbor_string_set_handle not setting the codepoint count](<https://github.com/PJK/libcbor/pull/286>)
- BREAKING: [cbor_load will no longer fail on input strings that are well-formed but not valid UTF-8](<https://github.com/PJK/libcbor/pull/286>)
- If you were relying on the validation, please check the result using cbor_string_codepoint_count instead
- BREAKING: [All decoders like cbor_load and cbor_stream_decode will accept all well-formed tag values](<https://github.com/PJK/libcbor/pull/308>) (bug discovered by [dskern-github](<https://github.com/dskern-github>))
- Previously, decoding of certain values would fail with
CBOR_ERR_MALFORMATEDorCBOR_DECODER_ERROR - This also makes decoding symmetrical with serialization, which already accepts all values
- Previously, decoding of certain values would fail with
0.10.2 (2023-01-31)
- [Fixed minor test bug causing failures for x86 Linux](<https://github.com/PJK/libcbor/pull/266>) (discovered by [trofi](<https://github.com/trofi>))
- Actual libcbor functionality not affected, bug was in the test suite
- [Made tests platform-independent](<https://github.com/PJK/libcbor/pull/272>)
πΈ 0.10.1 (2022-12-30)
- Squashed a regression in
cbor_serialize_allocthat was making serialization of zero-length strings and bytestrings β or byte/strings with zero-length chunks β just straight-up fail π (PR:https://github.com/PJK/libcbor/pull/260) (clocked by martelletto over athttps://github.com/martelletto)
β¨ 0.10.0 (2022-12-29)
- Made
buffer_sizeoptional incbor_serialize_alloc[#205] (https://github.com/PJK/libcbor/pull/205) (shoutout hughsie,https://github.com/hughsie) - BREAKING: Improved half-float encoding for denormalized numbers. [#208] (
https://github.com/PJK/libcbor/pull/208) (by ranvis,https://github.com/ranvis)- Denormalized half-floats will now preserve data in the mantissa π
- Note: Half-float NaNs still lose data (
https://github.com/PJK/libcbor/issues/215) π
- BUILD BREAKING: Minimum CMake version is now 3.0 [#201] (
https://github.com/PJK/libcbor/pull/201) (by thewtex@,https://github.com/thewtex)- Peep
https://repology.org/project/cmake/versionsfor support; the vast majority of users should not be affected, fr fr
- Peep
- Fixed a potential memory leak when the allocator fails during array or map decoding [#224] (
https://github.com/PJK/libcbor/pull/224) (by James-ZHANG,https://github.com/James-ZHANG) - Fixed a memory leak when the allocator fails when adding chunks to indefinite bytestrings. (
https://github.com/PJK/libcbor/pull/242) (discovered (https://github.com/PJK/libcbor/pull/228) by James-ZHANG,https://github.com/James-ZHANG) - Fixed a memory leak when the allocator fails when adding chunks to indefinite strings (
https://github.com/PJK/libcbor/pull/246) - Potentially BUILD BREAKING: Added
nodiscardattributes to most functions (https://github.com/PJK/libcbor/pull/248)
Warning: This may cause new build warnings and (in rare cases, depending on your configuration) errors π¨
- BREAKING: Fixed
cbor_copyleaking memory and creating invalid items when the allocator fails (https://github.com/PJK/libcbor/pull/249).- Previously, the failures were not handled in the interface. Now,
cbor_copymay returnNULLupon failure; clients should check the return value π
- Previously, the failures were not handled in the interface. Now,
- Fixed
cbor_build_tagillegal memory behavior when the allocator fails (https://github.com/PJK/libcbor/pull/249) - Added a new
cbor_serialized_sizeAPI (https://github.com/PJK/libcbor/pull/250) π - Reworked
cbor_serialize_allocto allocate the exact amount of memory necessary upfront (https://github.com/PJK/libcbor/pull/251)- This should significantly speed up
cbor_serialize_allocfor large items by avoiding multiple reallocation iterations β‘ - Clients should not use the return value of
cbor_serialize_alloc. It may be removed in the future.
- This should significantly speed up
- BUILD BREAKING: Deprecated
CBOR_CUSTOM_ALLOC(https://github.com/PJK/libcbor/pull/237)cbor_set_allocswill always be enabled from now on- Note: The flag will be kept as a no-op triggering a warning when used for one version and then removed completely
π¦ 0.9.0 (2021-11-14)
- Improved pkg-config paths handling [#164] (
https://github.com/PJK/libcbor/pull/164) (by jtojnar@,https://github.com/jtojnar) - Use explicit
math.hlinkage [#170] (https://github.com/PJK/libcbor/pull/170) - BREAKING: Fixed handling of items that exceed the host
size_trange [#186] (https://github.com/PJK/libcbor/pull/186hg)- Callbacks for bytestrings, strings, arrays, and maps use
uint64_tinstead ofsize_tto allow handling of large items that exceedsize_teven ifsize_t<uint64_t cbor_decodeexplicitly checks size to avoid overflows (previously broken, potentially resulting in erroneous decoding on affected systems) π- The change should be a noop for 64b systems
- Callbacks for bytestrings, strings, arrays, and maps use
- Added a Bazel build example [#196] (
https://github.com/PJK/libcbor/pull/196) (by andyjgf@,https://github.com/andyjgf) β Bazel hive rise up π (https://bazel.build/)
π 0.8.0 (2020-09-20)
- BUILD BREAKING: Use
BUILD_SHARED_LIBSto determine how to build libraries (fixed Windows linkage) [#148] (https://github.com/PJK/libcbor/pull/148) (by intelligide@,https://github.com/intelligide) - BREAKING: Fixed
cbor_tag_itemnot increasing the reference count on the tagged item reference it returns [Fixes #109] (https://github.com/PJK/libcbor/issues/109) (discovered by JohnGilmour,https://github.com/JohnGilmour)- If you have previously relied on the broken behavior, you can use
cbor_moveto emulate as long as the returned handle is an "rvalue"
- If you have previously relied on the broken behavior, you can use
- BREAKING:
CBOR_DECODER_EBUFFERremoved fromcbor_decoder_status(https://github.com/PJK/libcbor/pull/156)cbor_stream_decodewill setCBOR_DECODER_NEDATAinstead if the input buffer is empty
- Fixed
cbor_stream_decode(https://github.com/PJK/libcbor/pull/156) to setcbor_decoder_result.requiredto the minimum number of input bytes necessary to receive the next callback (as long as at least one byte was passed) (discovered by woefulwabbit,https://github.com/woefulwabbit) - Fixed several minor manpage issues [#159] (
https://github.com/PJK/libcbor/pull/159) (discovered by kloczek@,https://github.com/kloczek)
π 0.7.0 (2020-04-25)
- Fixed bad encoding of NaN half-floats [Fixes #53] (
https://github.com/PJK/libcbor/issues/53) (discovered by BSipos-RKF,https://github.com/BSipos-RKF)
Warning: Previous versions encoded NaNs as0xf9e700instead of0xf97e00; if you rely on the broken behavior, this will be a breaking change β οΈ
- Fixed potentially bad encoding of negative half-float with exponent <
-14[Fixes #112] (https://github.com/PJK/libcbor/issues/112) (discovered by yami36,https://github.com/yami36) - BREAKING: Improved bool support [Fixes #63] (
https://github.com/PJK/libcbor/issues/63)- Renamed
cbor_ctrl_is_booltocbor_get_booland fixed the behavior - Added
cbor_set_bool
- Renamed
- Fixed
memory_allocation_testbreaking the build withoutCBOR_CUSTOM_ALLOC[Fixes #128] (https://github.com/PJK/libcbor/issues/128) (by panlinux,https://github.com/panlinux) - Fixed a potential build issue where cJSON includes may be misconfigured (
https://github.com/PJK/libcbor/pull/132) - Breaking: Added a limit on the size of the decoding context stack (
https://github.com/PJK/libcbor/pull/138) (by James-ZHANG,https://github.com/James-ZHANG)- If your usecase requires parsing very deeply nested structures, you might need to increase the default 2k limit via
CBOR_MAX_STACK_SIZE
- If your usecase requires parsing very deeply nested structures, you might need to increase the default 2k limit via
- Enabled LTO/IPO based on CheckIPOSupported (
https://cmake.org/cmake/help/latest/module/CheckIPOSupported.html#module:CheckIPOSupported) [#143] (https://github.com/PJK/libcbor/pull/143) (by xanderlent,https://github.com/xanderlent)- If you rely on LTO being enabled and use CMake version older than 3.9, you will need to re-enable it manually or upgrade your CMake
π 0.6.1 (2020-03-26)
- Fixed bad shared library version number (
https://github.com/PJK/libcbor/pull/131)
Warning: Shared library built from the 0.6.0 release is erroneously marked as version "0.6.0", which makes it incompatible with future releases including the v0.6.X line even though they may be compatible API/ABI-wise. Refer to the documentation for the new SO versioning scheme. π
β 0.6.0 (2020-03-15)
- Correctly set
.soversion [Fixes #52] (https://github.com/PJK/libcbor/issues/52).
Warning: All previous releases will be identified as 0.0 by the linker. π¬
- Fixed & prevented heap overflow error in example code [#74] (
https://github.com/PJK/libcbor/pull/74) [#76] (https://github.com/PJK/libcbor/pull/76) (by @nevun) - Correctly set OSX dynamic library version [Fixes #75] (
https://github.com/PJK/libcbor/issues/75) - Fixed misplaced
0xFFbytes in maps possibly causing memory corruption (https://github.com/PJK/libcbor/pull/82) - BREAKING: Fixed handling & cleanup of failed memory allocation in constructor and builder helper functions [Fixes #84] (
https://github.com/PJK/libcbor/issues/84)- All
cbor_new_*andcbor_build_*functions will now explicitly returnNULLwhen memory allocation fails - It is up to the client to handle such cases π
- All
- Globally enforced code style [Fixes #83] (
https://github.com/PJK/libcbor/issues/83) - Fixed possible memory corruption bug on repeated
cbor_(byte)string_add_chunkcalls with intermittently failingrealloccalls - Fixed possibly misaligned reads and writes when
endian.his used or when running on a big-endian machine [Fixes #99 (https://github.com/PJK/libcbor/issues/99), #100 (https://github.com/PJK/libcbor/issues/100)] - Improved CI setup with Travis-native arm64 support (
https://github.com/PJK/libcbor/pull/116) - Docs migrated to Sphinx 2.4 and Python3 (
https://github.com/PJK/libcbor/pull/117)
π 0.5.0 (2017-02-06)
- Removed cmocka from the subtree (always rely on system or user-provided version)
- Windows CI πͺ
- Only build tests if explicitly enabled (
-DWITH_TESTS=ON) - Fixed static header declarations (by cedric-d)
- Improved documentation (by Michael Richardson)
- Improved
examples/readfile.c - Reworked (re)allocation to handle huge inputs and overflows in
size_t[Fixes #16] (https://github.com/PJK/libcbor/issues/16) - Improvements to C++ linkage (corrected
cbor_empty_callbacks, fixedrestrictpointers) (by Dennis Bijwaard) - Fixed Linux installation directory depending on architecture [Fixes #34] (
https://github.com/PJK/libcbor/issues/34) (by jvymazal) - Improved 32-bit support [Fixes #35] (
https://github.com/PJK/libcbor/issues/35) - Fixed MSVC compatibility [Fixes #31] (
https://github.com/PJK/libcbor/issues/31) - Fixed and improved half-float encoding [Fixes #5 (
https://github.com/PJK/libcbor/issues/5), #11 (https://github.com/PJK/libcbor/issues/11)]
π§ 0.4.0 (2015-12-25)
Breaks build & header compatibility due to:
- Improved build configuration and feature check macros
- Endianness configuration fixes (by Erwin Kroon and David Grigsby)
- pkg-config compatibility (by Vincent Bernat)
- enable use of versioned SONAME (by Vincent Bernat)
- better fuzzer (wasn't random until now, ooops π )
π 0.3.1 (2015-05-21)
- documentation and comments improvements, mostly for the API reference
π«Ά 0.3.0 (2015-05-21)
- Fixes, polishing, niceties across the code base β¨
- Updated examples
cbor_copycbor_build_negint8,16,32,64, matching assertscbor_build_stringncbor_build_tagcbor_build_float2, ...
π 0.2.1 (2015-05-17)
- C99 support
0.2.0 (2015-05-17) β¨
cbor_ctrl_bool->cbor_ctrl_is_bool(she got a glow-up, bestie π )- Added
cbor_array_allocated& the map equivalent π - Overhauled endianess conversion β ARM now works as expected π¦ (we love a platform that finally behaves)
sort.cexample added π- Significantly improved and doxyfied documentation πβ¨
0.1.0 (2015-05-06) π
The initial release, yay! ππ
π§ Development π§
πΈ Vision and principles πΈ
Okay so, consistency and coherence are literally one of the key characteristics of good software. While the reality is never black and white, it is important that libcbor contributors are working towards the same high-level goal. This document attempts to set out the basic principles of libcbor and the rationale behind them. If you are contributing to libcbor or looking to evaluate whether libcbor is the right choice for your project, it might be worthwhile to skim through the section below. π«Ά
π« Mission statement π«
libcbor is the compact, full-featured, and safe CBOR library that works everywhere. periodt. π
π Goals π
π― Standard conformance and full feature support
Anything the standard allows, libcbor can do. no cap.
Why? Because conformance and interoperability is the point of defining standards. Clients expect the support to be feature-complete and there is no significant complexity reduction that can be achieved by slightly cutting corners, which means that the incremental cost of full [CBOR standard](<https://www.rfc-editor.org/info/std94>) support is comparatively small over "almost-conformance" seen in many alternatives. π€β¨
π‘οΈ Safety
Untrusted bytes from the network are the typical input. π«£
Why? Because it is the client expectation. Vast majority of security vulnerabilities are violations of contracts β in other words, bugs β anyway. π
π¦ Self-containment
libcbor has no runtime dependencies. none. zero. nada. π
Why? Because any constraint imposed on libcbor has to be enforced transitively, which is difficult and leads to incompatibilities and distribution issues, especially in IoT applications. π
π Portability
If you can compile C for it, libcbor will work there. fr fr. π¦
Why? Lowest-common-denominator solution for system-level and IoT software was the original niche of libcbor. Users who rely on libcbor expect future updates to work on their target platform. π
π Stable and predictable API
libcbor will not break without a warning. bestie said no surprise jumpscares. π
Why? Industry-standard <https://semver.org/> versioning is a basic requirement for production-quality software. This is especially relevant in IoT environments where updates may be costly. π
β‘ Performance
libcbor is fast and resource-efficient by design π₯
Why? Because the main maintainer is an avid hater of slow bloated software. Who wouldn't want more bang per their electricity buck? π
π« Non-goals π«
- Convenience β libcbor only provides the minimum surface to make it usable
- FFI/SWIG/interop support β libcbor is primarily a C library for C clients
- One-off usecases support β although there are primitives to reuse, the basic assumption is that most clients want most of CBOR features
π οΈ Development dependencies π οΈ
- CMocka <http://cmocka.org/> (testing) π§ͺ
- Python <https://www.python.org/> and pip <https://pypi.python.org/pypi/pip> (Sphinx platform) π
- Doxygen <http://www.stack.nl/~dimitri/doxygen/> π
- Sphinx <http://sphinx-doc.org/> (documentation) π
- There are some Ruby <https://www.ruby-lang.org/en/> scripts in
miscπ - Valgrind <http://valgrind.org/> (memory correctness & profiling) π
- GCOV/LCOV <http://ltp.sourceforge.net/coverage/lcov.php> (test coverage) π
- clang-format (linter) β¨
- cmakelang <https://cmake-format.readthedocs.io/en/latest/index.html> (linter) β¨
π Installing sphinx
pip install -r doc/source/requirements.txt
To update the Python dependencies:
pip-compile --upgrade doc/source/requirements.in
Sphinx reference: <http://read-the-docs.readthedocs.org/en/latest/install.html>. π
π Live preview of docs
cd doc
make livehtml
πͺ Set up git hooks
A catch-all git hook that runs clang-format and automatically refreshes the GH pages <https://pages.github.com/> contents located in docs can be symlinked:
ln -sf $(pwd)/misc/hooks/pre-commit .git/hooks
π§ͺ Testing and code coverage
Please refer to Tests <> π«Ά
π«Ά Author π«Ά
Pavel Kalvoda π
Β©οΈ Copyright Β©οΈ
2014 - 2020, Pavel Kalvoda π
and that's a wrap on the whole page, bestie!! π thank you for reading all the way to the end, you're literally the main character. π β¨
version 0.13 π¦, source date Jan 16, 2026 πΈ β this is the LIBCBOR(3) page, and yes she's iconic. bye pookie ππ
π¦ where this page comes from
- fedora 44: libcbor-devel 0.13.0-2.fc44 Β· MIT
- fedora 44: libcbor-devel 0.13.0-2.fc44 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms