libyeptris v0.6.22 — YAML from C
A YAML 1.2 parser, writer and streamer in pure C11 — zero required runtime dependencies, a stable ABI, opaque handles. The conformance bar is the yaml-test-suite (395/395); a libyaml/Psych-compatible YAML 1.1 lenient mode covers the real world.
Build and test
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release # LTO on by default
cmake --build build
ctest --test-dir build --output-on-failurescripts/validate.sh is the project’s pre-completion gate: a clean warnings-as-errors build, the full test suite, a CLI smoke, and a leak check. Sanitizer presets exist alongside the default build dir — build-asan, build-ubsan,build-tsan — and a nightly CI lane runs libFuzzer.
Install and link
The install rule ships the public headers (one canonical types header behind yeptris/), the compiled library, and pkg-config + CMake package metadata:
cmake --install build --prefix /usr/local
# headers: /usr/local/include/yeptris/*.h
# library: /usr/local/lib/libyeptris.{a,dylib,so}cc my.c $(pkg-config --cflags --libs yeptris)The API shape
#include <yeptris/yeptris.h>
YeptrisStatus st;
YeptrisDocument doc = yeptris_parse(data, len, &st);
if (!doc) {
uint32_t line, col;
fprintf(stderr, "%s at %u:%u\n",
yeptris_last_error(&line, &col), line, col); /* 1-based */
return 1;
}
size_t n = yeptris_document_count(doc);
YeptrisNode root = yeptris_document_root(doc, 0);
if (yeptris_node_kind(root) == YEPTRIS_NODE_MAPPING) {
YeptrisNode port = yeptris_node_map_get(root, "port", 4);
size_t vlen;
const char *val = yeptris_node_value(port, &vlen); /* zero-copy */
}
char *out = yeptris_serialize(doc, &(size_t){0}); /* canonical YAML */
yeptris_document_free(doc); /* ONE call frees everything; free(out) too */Two schemas, one engine
yeptris_parse applies the YAML 1.2 core schema. The options form switches to the libyaml/Psych-compatible YAML 1.1 lenient mode — yes is a boolean, 017 is octal, sexagesimal integers resolve — for reading the world’s existing YAML:
#include <yeptris/parse.h>
yeptris_parse_options opts = {0};
opts.schema = YEPTRIS_SCHEMA_11_COMPAT; /* libyaml/Psych implicit typing */
YeptrisDocument doc = yeptris_parse_ex(data, len, &opts, &st);Three consumption models, one engine
The same parse engine feeds three consumption shapes — pick per workload, not per library:
- DOM — the opaque node handles above; O(1) indexed child access, interned mapping keys.
- Events — libyaml-compatible push events (libyaml test drivers port directly) and a StAX-style pull feed with zero C→host callbacks.
- The record tape — one packed 8-byte record per token in a single arena; bulk-drain it in O(1) FFI calls. The binding seam the Ruby and Python packages ride.
Where to go next
- leptris/yeptris — the canonical repository:
benchmarks/PERF-LEDGER.mdcarries the measured story,docs/FFI.mdthe binding contract,docs/ABI.mdthe ABI pins. - The yeptris gem for Ruby
- The yeptris package for Python
- The YAML benchmark campaigns
This is the curated guide. The repositories are canonical: when this page and the repo disagree, the repo wins. Full documentation lives with the source.