Skip to content

phil-base/libcfi

Repository files navigation

CFI — Common Format Interface

A self-contained C library for building and querying hierarchical key-value trees in memory. Nodes are addressed by slash-separated paths; each node holds named string attributes. No external dependencies.

CFI is well-suited to structured message or protocol data: anything where you have repeated, named sub-elements (headers, parts, recipients) that you want to address by path and enumerate without knowing the shape upfront.

Building

make          # builds cfi.a + example binaries
make check    # run the test suite
make install  # install cfi.a and headers to /usr/local (override PREFIX=)

CI runs on every push against Ubuntu and macOS.

Example — modelling a multi-part email

/* add three recipients as indexed child nodes under /headers */
char *ctx;

ctx = CfiIndexer("to", 0);
CfiAdd(h, ctx, 1);               /* create /headers/to[0] and go there */
CfiSet(h, "name",    "Bob Smith");
CfiSet(h, "address", "bob@example.com");
CfiGoto(h, "/headers");
free(ctx);

/* ... repeat for to[1], to[2] ... */

/* enumerate recipients without knowing the count upfront */
char **children;
CfiLook(h, "/headers", CFI_LOOK_CONTEXT, &children);
for (int i = 0; children[i]; i++) {
    if (strncmp(children[i], "to[", 3) != 0) continue;
    char path[128];
    snprintf(path, sizeof(path), "/headers/%s/name", children[i]);
    printf("%s\n", CfiValue(h, path, 1));
}
for (int i = 0; children[i]; i++) free(children[i]);
free(children);

/* serialize the whole message to JSON */
char *json = CfiToJson(h, "/");

A fully runnable version of this example is in examples/email.c. Build it with make examples (or make all) and run ./examples/email.

Quick start

Paths

Paths are slash-separated strings: /, /mail, /mail/headers/to[2].

  • Absolute paths start with /.
  • Relative paths resolve from the current default context (CfiGoto).
  • Indexed nodes use bracket notation: CfiIndexer("recipient", 2)"recipient[2]" (heap-allocated; caller frees).

Memory ownership

Function Who owns the returned memory
CfiGet / CfiValue Library — do not free. Copy if you need to keep the value across a mutating call.
CfiLook Caller — free each list[i], then free(list).
CfiIndexer Caller — free(result).
CfiToJson Caller — free(json).
All string parameters passed in Library copies them; caller retains ownership.

Walk API

Traverse a subtree without managing lists manually:

typedef struct { int nodes; int attrs; } Counts;

int my_cb(void *h, const char *path, const char *name,
          const char *value, int flags, void *ud)
{
    Counts *c = ud;
    if (flags == CFI_WALK_NODE)      c->nodes++;
    if (flags == CFI_WALK_ATTRIBUTE) c->attrs++;
    return 0;   /* non-zero stops traversal */
}

Counts c = {0, 0};
CfiWalk(h, "/", my_cb, &c);

JSON serialization and file I/O

/* export subtree → JSON string (caller frees) */
char *json = CfiToJson(h, "/");

/* import JSON into a new handle */
void *h2 = NULL;
CfiCreate(&h2, NULL);
CfiFromJson(h2, "/", json);
free(json);

/* save subtree to a file */
CfiSave(h, "/", "/var/lib/myapp/state.json");

/* load from file into an existing handle (at any context) */
CfiLoad(h2, "/", "/var/lib/myapp/state.json");

/* deep-copy a subtree between handles */
CfiCopy(src, "/section", dst, "/section");

JSON format: {"name":"…","attrs":{…},"children":[…]} — field order is fixed; only string values are used (matching the CFI attribute model).

API summary

Core

Function Description
CfiCreate(handle, syntax) Create an empty tree. Pass NULL for no schema.
CfiDestroy(handle) Free the tree and all its contents.
CfiGoto(handle, context) Set the default context for relative paths.
CfiAdd(handle, context, go_there) Add a node; optionally move default there.
CfiDelete(handle, context) Remove a node and all its descendants.
CfiSet(handle, context, value) Set (or replace) an attribute. NULL value deletes.
CfiExtend(handle, context, value) Append to an attribute (creates if absent).
CfiGet(handle, context, &value) Read an attribute; *value = NULL if absent.
CfiValue(handle, context, safe) Like CfiGet but returns the string directly.
CfiLook(handle, context, type, &list) List children and/or attributes.
CfiCount(handle, context, type) Count children and/or attributes.

Utilities

Function Description
CfiIndexer(name, index) Format "name[index]" (heap-allocated; caller frees).
CfiSetLong(handle, context, value) Store a long as a string attribute.
CfiValueLong(handle, context) Read an attribute and return it as long.
CfiWalk(handle, context, cb, ud) Recursive traversal with a callback.
CfiToJson(handle, context) Serialize subtree to JSON string (caller frees).
CfiFromJson(handle, context, json) Import JSON string into tree at context.
CfiSave(handle, context, path) Write subtree to a JSON file.
CfiLoad(handle, context, path) Read a JSON file into tree at context.
CfiCopy(src, src_ctx, dst, dst_ctx) Deep-copy a subtree.

Error handling

#include "cfi_error.h"

CfiSetErrorOutput(NULL);          /* silence all error output */
CfiSetErrorOutput(stderr);        /* explicit stderr (the default) */
CfiSetErrorOutput(some_file);     /* redirect to a FILE* */

const CfiLastError *e = CfiGetLastError();
/* e->code, e->message, e->function */

Return codes

Code Meaning
CFI_OK Success
CFI_NOMEMORY Allocation failure
CFI_HANDLE_INVALID NULL or corrupt handle
CFI_CONTEXT_NOSUCH Path invalid for the schema
CFI_CONTEXT_NOTFOUND Path not found
CFI_CONTEXT_FOUND Node already exists
CFI_CONTEXT_AMBIGUOUS Path ambiguous
CFI_CONTEXT_INDEXREQUIRED Index missing for multi-valued node
CFI_PARAMETER_INVALID Bad argument
CFI_ATTRIBUTE_NOTFOUND Attribute not found
CFI_ERROR Internal error

Thread safety

Error state (CfiGetLastError, CfiSetErrorOutput) is thread-local: each thread has its own independent last-error record and error output destination, initialised to the defaults on first use. This matches the errno model — threads do not interfere with each other's error reporting.

Individual handles are not thread-safe. Do not read or write the same handle from multiple threads without external synchronisation (e.g. a mutex). Creating separate handles per thread, or serialising access to a shared handle, are both valid patterns.

Requires C11 (-std=c11) for _Thread_local support.

License

MIT — see LICENSE.

About

In-memory hierarchical key-value tree library for C. Nodes addressed by slash-separated paths; JSON serialization; no external dependencies.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors