constexpr-cbor


A CBOR decoder you can prove, not just test

A from-scratch, header-only C++20 codec for CBOR (RFC 8949) whose entire test suite runs consteval — inside a compile-time constant expression, where an out-of-bounds read or a narrowing conversion isn’t a bug waiting to be found, it’s a build that doesn’t compile. Decode and encode, with a no-malloc variant for targets that have no heap to give it.

What you get

Six things you can count on, none of which ask you to change how you build.

UB-freedom isn’t claimed, it’s compiled

consteval decode_value(...)

Every decoder is exercised inside a consteval function and static_assert’d. Out-of-bounds access and narrowing conversions inside a constant expression are hard compiler errors, not latent bugs — so a tested input path is provably free of that whole class of defect, by construction.

Every RFC 8949 Appendix A vector, round-tripped

104 official test vectors

All 104 rows of the spec’s own worked examples — every major type, definite and indefinite length, chunked strings, nested arrays and maps — decoded, checked, re-encoded canonically, and compared back against the official bytes.

No heap required

ConstexprCbor::Arena

A separate, independently includable no-malloc decoder/encoder for targets with no heap at all: a caller-sized, fixed-capacity node pool instead of std::vector, with the same refusal-not-crash contract on every length and overflow check.

Known hazard classes, closed

checked_node_count · bounded chunk sums

Length-field overflow, truncated-input OOB reads, and unbounded nesting recursion are the recurring causes behind real issues in QCBOR, tinycbor, zcbor, libcbor, and NanoCBOR. Each one is closed here structurally, not patched after a report.

Policy-based, not opinionated for you

Utf8Policy · KeyPolicy

UTF-8 validation (none, structural, or full RFC 3629) and duplicate-map-key handling (reject, keep-first, keep-last) are template policies you choose — both are explicitly implementation-defined in the spec, so the decoder doesn’t guess on your behalf.

An ergonomic interface, not just a raw variant

operator[] · .get<T>() · to_cbor/from_cbor

An nlohmann::json-style layer on top — operator[], is_*(), range-for iteration, brace-initializer construction, and ADL hooks for your own types — stays exception-free throughout, so it fits the same -fno-exceptions builds the raw decoder targets.

Why consteval, not just a test runner

A unit test that runs on the host proves a decoder behaved correctly that time, on that platform, under that sanitizer (or none). A static_assert over a consteval function proves something stronger: the compiler itself had to fully evaluate the decode as a constant expression to produce a binary at all, and the language forbids a constant expression from reading out of bounds or narrowing a value lossily. There is nothing to flash, no sanitizer to remember to enable, and no platform variance — the proof is the build.

No heap, no problem

constexpr_cbor_arena.hpp is a second, independently includable header: the same decode/encode contract, built on a caller-sized NodePool<MaxNodes, MaxBytes> instead of std::vector, for targets with no dynamic allocation at all. Running out of node or byte capacity is a refusal (std::nullopt), never a crash — proven at runtime, not just at compile time, where a transient allocation inside the compiler’s own constant-evaluation machinery would never touch real operator new anyway.

Works on the compilers and targets you actually ship

Header-only, MIT licensed, C++20 the only requirement. The real, checked-not-assumed floor is GCC 13+ or Clang 19+ — older versions in the compiler matrix are kept building on purpose, each expected to fail for its own documented reason (an incomplete C++20 constexpr-container implementation in that version's standard library), proving where the floor actually is rather than silently dropping them. Also compiles clean on ESP32 (Xtensa), STM32 (Cortex-M4), and RISC-V (rv32imac) bare-metal cross-compilers, and under -fno-exceptions -fno-rtti — the default on several embedded toolchains, ESP-IDF among them.

Install it

The repository is the maintained source of truth for install instructions — CMake’s FetchContent, or vendor the headers directly. Both are documented there. Its compile-time test suite is built on compile-time-unit-testing, the sibling library this author also maintains for turning constexpr checks into static_assert failures.

constexpr-cbor on GitHub →

Try it in the browser

The same decoder, compiled to WebAssembly, runs as an online CBOR editor: drop in a CBOR file to see it as JSON, edit the JSON, and download the result as CBOR.

Open the online editor →