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
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
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
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
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
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
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.
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.