iczelia
Fast, header-only, public domain, 2500 LoC ANSI C89 implementation of RFC 8949 using arenas.
| 2026-08-12 21:42 | style refactor | Kamila Szewczyk |
| 2026-08-12 21:37 | loc | Kamila Szewczyk |
| 2026-08-12 21:27 | fuzzing oriented bug fixes, cc0 -> 0bsd, etc. | Kamila Szewczyk |
| 2026-01-26 01:09 | Update README.md | Kamila Szewczyk |
| 2026-01-26 00:59 | Add initial README for MiniCBOR library | Kamila Szewczyk |
| 2026-01-26 00:54 | clarification in the comment header | Kamila Szewczyk |
| 2026-01-26 00:54 | Add files via upload | Kamila Szewczyk |
| 2026-01-26 00:53 | Initial commit | Kamila Szewczyk |
| main (2026-08-12 21:42) |
no tags.
MiniCBOR is a header-only ANSI C89 implementation of RFC 8949 (CBOR). Synopsis:
malloc, we attain slightly better memory footprint and improved allocation/deallocation speed as compared to other libraries.#define MCBOR_IMPLEMENTATION
#include "../minicbor.h"
#include <stdio.h>
#include <string.h>
typedef struct {
const char * app_name;
const char * version;
int port;
int debug_enabled;
const char * allowed_hosts[4];
int num_hosts;
struct {
int max_connections;
int timeout_seconds;
double retry_factor;
} network;
} app_config_t;
static size_t encode_config(const app_config_t * cfg, unsigned char * buffer, size_t buflen) {
mcbor_encoder_t enc;
mcbor_encoder_init(&enc, buffer, buflen);
/* Root map with 6 keys */
mcbor_encode_map_start(&enc, 6);
/* "app_name": "MyService" */
mcbor_encode_text_cstr(&enc, "app_name");
mcbor_encode_text_cstr(&enc, cfg->app_name);
/* "version": "1.2.3" */
mcbor_encode_text_cstr(&enc, "version");
mcbor_encode_text_cstr(&enc, cfg->version);
/* "port": 8080 */
mcbor_encode_text_cstr(&enc, "port");
mcbor_encode_uint(&enc, cfg->port);
/* "debug": true/false */
mcbor_encode_text_cstr(&enc, "debug");
mcbor_encode_bool(&enc, cfg->debug_enabled);
/* "allowed_hosts": ["localhost", "example.com", ...] */
mcbor_encode_text_cstr(&enc, "allowed_hosts");
mcbor_encode_array_start(&enc, cfg->num_hosts);
for (int i = 0; i < cfg->num_hosts; i++) {
mcbor_encode_text_cstr(&enc, cfg->allowed_hosts[i]);
}
/* "network": { nested object } */
mcbor_encode_text_cstr(&enc, "network");
mcbor_encode_map_start(&enc, 3);
mcbor_encode_text_cstr(&enc, "max_connections");
mcbor_encode_uint(&enc, cfg->network.max_connections);
mcbor_encode_text_cstr(&enc, "timeout_seconds");
mcbor_encode_uint(&enc, cfg->network.timeout_seconds);
mcbor_encode_text_cstr(&enc, "retry_factor");
mcbor_encode_float64(&enc, cfg->network.retry_factor);
return mcbor_encoder_len(&enc);
}
/* Decode and print configuration from CBOR */
static int decode_and_print_config(const unsigned char * data, size_t len) {
mcbor_arena_t arena;
mcbor_arena_init(&arena);
mcbor_decoder_t dec;
mcbor_decoder_init(&dec, data, len, &arena);
mcbor_value_t root;
if (mcbor_decode(&dec, &root) != MCBOR_OK) {
mcbor_arena_destroy(&arena);
return -1;
}
/* Print diagnostic output. */
char diag[1024];
mcbor_to_diagnostic(&root, diag, sizeof(diag));
printf("Decoded CBOR (diagnostic notation):\n%s\n\n", diag);
/* Extract and print fields, */
mcbor_value_t * v;
printf("Decoded configuration:\n");
if ((v = mcbor_map_get_text(&root, "app_name")) && mcbor_is_text(v)) {
printf(" app_name: %s\n", mcbor_get_text(v, NULL));
}
if ((v = mcbor_map_get_text(&root, "version")) && mcbor_is_text(v)) {
printf(" version: %s\n", mcbor_get_text(v, NULL));
}
if ((v = mcbor_map_get_text(&root, "port")) && mcbor_is_uint(v)) {
printf(" port: %d\n", (int)mcbor_get_uint(v));
}
if ((v = mcbor_map_get_text(&root, "debug")) && mcbor_is_bool(v)) {
printf(" debug: %s\n", mcbor_get_bool(v) ? "true" : "false");
}
/* Decode allowed_hosts array */
if ((v = mcbor_map_get_text(&root, "allowed_hosts")) && mcbor_is_array(v)) {
printf(" allowed_hosts: [");
size_t num_hosts = mcbor_array_len(v);
for (size_t i = 0; i < num_hosts; i++) {
mcbor_value_t* host = mcbor_array_get(v, i);
if (host && mcbor_is_text(host)) {
printf("%s%s", mcbor_get_text(host, NULL), i < num_hosts - 1 ? ", " : "");
}
}
printf("]\n");
}
/* Decode nested network settings */
if ((v = mcbor_map_get_text(&root, "network")) && mcbor_is_map(v)) {
mcbor_value_t* nv;
if ((nv = mcbor_map_get_text(v, "max_connections")) && mcbor_is_uint(nv)) {
printf(" network.max_connections: %d\n", (int)mcbor_get_uint(nv));
}
if ((nv = mcbor_map_get_text(v, "timeout_seconds")) && mcbor_is_uint(nv)) {
printf(" network.timeout_seconds: %d\n", (int)mcbor_get_uint(nv));
}
if ((nv = mcbor_map_get_text(v, "retry_factor")) && mcbor_is_float(nv)) {
printf(" network.retry_factor: %.1f\n", mcbor_get_float(nv));
}
}
mcbor_arena_destroy(&arena); /* All objects die here! */
return 0;
}
int main(void) {
/* Create a sample configuration */
app_config_t config = {
.app_name = "MyApp",
.version = "1.2.3",
.port = 8080,
.debug_enabled = 1,
.allowed_hosts = {"localhost", "127.0.0.1", "example.com"},
.num_hosts = 3,
.network = {
.max_connections = 100,
.timeout_seconds = 30,
.retry_factor = 1.5
}
};
/* Encode to CBOR */
unsigned char buffer[512];
size_t encoded_len = encode_config(&config, buffer, sizeof(buffer));
/* Print hex dump */
for (size_t i = 0; i < encoded_len; i++) {
printf("%02x ", buffer[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
printf("\n\n");
/* Decode back and print */
decode_and_print_config(buffer, encoded_len);
return 0;
}Performance-wise:
Iterations per test: 100000
1. Integer Encoding (uint64):
minicbor : 6.501 ms total, 0.065014 us/op (min: 0.029996, max: 82.234010)
libcbor : 7.664 ms total, 0.076636 us/op (min: 0.029996, max: 39.744005)
Speedup: 1.18x
2. Integer Decoding (uint64):
minicbor : 7.336 ms total, 0.073358 us/op (min: 0.029996, max: 4.568011)
libcbor : 8.063 ms total, 0.080633 us/op (min: 0.029996, max: 95.828995)
Speedup: 1.10x
3. String Encoding (59 bytes):
minicbor : 7.189 ms total, 0.071887 us/op (min: 0.029996, max: 20.767987)
libcbor : 22.446 ms total, 0.224463 us/op (min: 0.169992, max: 79.477996)
Speedup: 3.12x
4. Array Encoding (100 integers):
minicbor : 11.802 ms total, 0.118022 us/op (min: 0.069991, max: 61.655000)
libcbor : 219.108 ms total, 2.191082 us/op (min: 1.962990, max: 110.256001)
Speedup: 18.56x
5. Map Encoding (20 key-value pairs):
minicbor : 17.906 ms total, 0.179063 us/op (min: 0.139996, max: 14.857009)
libcbor : 125.188 ms total, 1.251884 us/op (min: 1.101986, max: 83.055004)
Speedup: 6.99x
6. Nested Structure Encoding:
minicbor : 11.709 ms total, 0.117093 us/op (min: 0.079989, max: 6.462008)
libcbor : 46.466 ms total, 0.464659 us/op (min: 0.399992, max: 154.278010)
Speedup: 3.97x
7. Float Encoding (double):
minicbor : 5.952 ms total, 0.059523 us/op (min: 0.019997, max: 0.310004)
libcbor : 6.598 ms total, 0.065977 us/op (min: 0.029996, max: 48.250005)
Speedup: 1.11x
8. Nested Structure Decoding:
minicbor : 17.690 ms total, 0.176901 us/op (min: 0.129998, max: 42.589992)
libcbor : 44.875 ms total, 0.448750 us/op (min: 0.389993, max: 51.174998)
Speedup: 2.54xminicbor is consistently faster, especially for collections (arrays/maps) where libcbor's reference counting and per-item allocation overhead becomes significant.
Feature parity is at a decent level: both libraries fully implement the core RFC. minicbor doesn't provide interfaces for deep clones, explicit ownership transfer, definite length checking or UTF-8 codepoint utilities. It also doesn't support RFC 8742 (CBOR Sequences).
