AxlData — Data Structures
Core collection types, string utilities, and message digest checksums.
Headers:
<axl/axl-hash-table.h>— Hash table with string keys (FNV-1a, chained)<axl/axl-array.h>— Dynamic array (auto-growing, index access)<axl/axl-list.h>— Doubly-linked list<axl/axl-slist.h>— Singly-linked list<axl/axl-queue.h>— FIFO queue<axl/axl-radix-tree.h>— Radix tree (compact prefix tree, longest-prefix lookup)<axl/axl-ring-buf.h>— Ring buffer (circular byte buffer, zero-copy, overwrite mode)<axl/axl-digest.h>— Message digest checksums (MD5, SHA-1, SHA-256)<axl/axl-sidecar.h>— Common JSON5 sidecar loader (used by AxlPciIds / AxlPciClassDb / AxlSpdIds / AxlUsbIds; see its dedicated module page for the open / schema-check / singleton- with-atexit pattern)
Choosing a Collection
Type |
Best for |
Lookup |
Insert/Remove |
|---|---|---|---|
AxlHashTable |
Key-value mapping |
O(1) by key |
O(1) amortized |
AxlArray |
Indexed, sorted data |
O(1) by index |
O(1) append |
AxlList |
Frequent insert/remove |
O(n) by value |
O(1) at position |
AxlSList |
Simple linked sequences |
O(n) |
O(1) prepend |
AxlQueue |
FIFO/LIFO patterns |
O(1) head/tail |
O(1) push/pop |
AxlRadixTree |
Prefix-match routing |
O(k) by key |
O(k) insert |
AxlRingBuf |
Streaming I/O, pipes |
O(1) push/pop |
O(1) |
AxlHashTable
GLib-style hash table with FNV-1a hashing, chained collision resolution, and automatic resize at 75% load factor. Supports generic key types via user-provided hash/equal callbacks.
Ownership
All three constructors allocate the AxlHashTable struct. They differ
in how the table treats the content (key/value pointers passed to
insert/replace):
Constructor |
Keys |
Values |
|---|---|---|
|
Copied (strdup’d) — table owns + frees |
Borrowed |
|
Borrowed |
Borrowed |
|
Owned if |
Owned if |
“Owned” means the table calls the destroy callback when the entry is removed or the table is freed. “Borrowed” means the caller is responsible for the lifetime; the table never copies and never frees.
new_str is the “make this go away easily” choice for literal/borrowed
string keys. new_full is for caller-allocated keys/values where the
caller wants to transfer ownership at insert time. new is for
borrowed-on-both-sides scenarios (e.g. integer keys cast to void *,
values that outlive the table).
Convenience Constructor (string keys)
axl_hash_table_new_str() creates a table with string keys that are copied
internally. Values are opaque pointers (not freed).
AXL_AUTOPTR(AxlHashTable) h = axl_hash_table_new_str();
axl_hash_table_insert(h, "name", "AXL");
axl_hash_table_insert(h, "version", "0.1.0");
const char *name = axl_hash_table_lookup(h, "name"); // "AXL"
size_t count = axl_hash_table_size(h); // 2
axl_hash_table_remove(h, "version");
axl_hash_table_foreach(h, my_callback, user_data);
Full Constructor (generic keys, owned entries)
axl_hash_table_new_full(hash, equal, key_free, value_free) creates a
table with custom hash/equal functions and optional destructors. Keys
are NOT copied — the table takes ownership.
// Owned string keys and values
AxlHashTable *h = axl_hash_table_new_full(
NULL, NULL, // NULL = axl_str_hash/axl_str_equal
axl_free_impl, // key destructor
axl_free_impl); // value destructor
axl_hash_table_replace(h, axl_strdup("key"), axl_strdup("value"));
axl_hash_table_replace(h, axl_strdup("key"), axl_strdup("new")); // old key+value freed
axl_hash_table_free(h); // remaining entries freed
Pointer Keys
Use axl_direct_hash/axl_direct_equal for pointer or integer keys:
AxlHashTable *h = axl_hash_table_new_full(
axl_direct_hash, axl_direct_equal, NULL, NULL);
axl_hash_table_insert(h, (void *)42, my_data);
void *data = axl_hash_table_lookup(h, (void *)42);
contains / steal / foreach_remove
// contains: distinguishes NULL-valued key from absent key
axl_hash_table_contains(h, "key"); // true even if value is NULL
// steal: remove without calling destructors (caller takes ownership)
axl_hash_table_steal(h, "key");
// foreach_remove: bulk conditional removal
size_t n = axl_hash_table_foreach_remove(h, predicate, data);
Iterator
Safe removal during iteration:
AxlHashTableIter iter;
void *key, *value;
axl_hash_table_iter_init(&iter, h);
while (axl_hash_table_iter_next(&iter, &key, &value)) {
if (should_remove(key, value)) {
axl_hash_table_iter_remove(&iter); // calls destructors
}
}
AxlArray
Dynamic array that stores elements by value (not pointers). Auto-grows on append. Supports indexed access, sorting, and removal. Matches GLib’s GArray.
AXL_AUTOPTR(AxlArray) a = axl_array_new(sizeof(int));
int values[] = {50, 20, 40, 10, 30};
for (int i = 0; i < 5; i++) {
axl_array_append(a, &values[i]);
}
axl_array_sort(a, int_compare);
axl_array_remove_index(a, 0); // remove first element
axl_array_remove_index_fast(a, 1); // O(1) swap-with-last removal
axl_array_set_size(a, 10); // grow (zero-initialized)
AxlList
GLib-style doubly-linked list. Each node has data, next, and
prev pointers. Functions return the new head (which may change after
prepend, remove, or sort). Matches GLib’s GList.
AxlList *list = NULL;
list = axl_list_append(list, "first");
list = axl_list_append(list, "second");
list = axl_list_prepend(list, "zeroth");
// Insert relative to a node
AxlList *node = axl_list_find(list, "first");
list = axl_list_insert_before(list, node, "half");
// Remove all matching, deep copy, context-aware sort
list = axl_list_remove_all(list, "first");
AxlList *copy = axl_list_copy_deep(list, my_copy_func, NULL);
axl_list_free(list);
AxlSList
Singly-linked list — lighter than AxlList (no prev pointer).
Use when you only traverse forward. Matches GLib’s GSList. Same
operations as AxlList: insert_before, remove_all, remove_link,
sort_with_data, copy_deep.
AxlQueue
FIFO queue with push/pop at both ends and peek. Can also be used as a stack (push/pop from the same end). Matches GLib’s GQueue. Supports find, remove, and stack-allocated initialization.
AxlQueue q = AXL_QUEUE_INIT; // stack-allocated
axl_queue_push_tail(&q, "first");
axl_queue_push_tail(&q, "second");
axl_queue_push_tail(&q, "first"); // duplicate
axl_queue_remove(&q, "first"); // removes first match
axl_queue_remove_all(&q, "first"); // removes all matches
AxlList *node = axl_queue_find(&q, "second");
AxlStr — String Utilities
String utilities operating on UTF-8 char * strings. Includes length,
copy, compare, search, split, join, and case-insensitive operations (ASCII
fold only). UCS-2 helpers at the bottom are for UEFI internal use.
All allocated results are freed with axl_free().
Header: <axl/axl-str.h>
Overview
AXL uses UTF-8 (char *) throughout its public API. UEFI firmware
uses UCS-2 (unsigned short *) internally, but AXL handles the
conversion transparently — you never need to deal with UCS-2 unless
making direct UEFI protocol calls.
UTF-8 vs UCS-2
Use UTF-8 (
char *) for all application code. Allaxl_str*functions operate on UTF-8.UCS-2 functions (
axl_wcslen,axl_wcscmp,axl_str_to_w,axl_str_from_w) exist only for UEFI interop. Consumer code should not need them.
Case-Insensitive Operations
axl_strcasecmp, axl_strcasestr, and axl_strncasecmp fold
ASCII letters only (A-Z -> a-z). They do not handle full Unicode
case mapping. This is sufficient for UEFI identifiers, HTTP headers,
and file extensions.
Common Patterns
#include <axl.h>
// Split a string
char **parts = axl_strsplit("a,b,c", ',');
for (int i = 0; parts[i] != NULL; i++) {
axl_printf(" %s\n", parts[i]);
}
axl_strfreev(parts); // frees the array AND each string
// Join strings
const char *items[] = {"one", "two", "three", NULL};
char *joined = axl_strjoin(", ", items);
axl_printf("%s\n", joined); // "one, two, three"
axl_free(joined);
// Search
if (axl_str_has_prefix(path, "fs0:")) { ... }
if (axl_str_has_suffix(name, ".efi")) { ... }
const char *found = axl_strcasestr(header, "content-type");
Memory Ownership
Functions that return char * allocate new memory. The caller
must free with axl_free():
axl_strdup,axl_strndupaxl_strsplit(free withaxl_strfreev)axl_strjoin,axl_strstripaxl_str_to_w,axl_str_from_w
Functions that return const char * or char * pointing into
the input string do NOT allocate:
axl_strstr_len,axl_strrstr(return pointer into haystack)axl_strchr(return pointer into string)
AxlStrReader — Cursor-Based String Parser
Symmetric counterpart to AxlString (the builder). A reader BORROWS a
const char * (no allocation, no ownership) and tracks a cursor with
a sticky-error flag. Operations short-circuit when ok is false, so
chains compose naturally without per-call error checking:
AxlStrReader r;
uint64_t v;
axl_str_reader_init(&r, "N[03A8]");
axl_str_reader_consume_char(&r, 'N');
axl_str_reader_consume_char(&r, '[');
axl_str_reader_take_u64(&r, 16, &v);
axl_str_reader_consume_char(&r, ']');
if (!r.ok || !axl_str_reader_eof(&r)) { /* parse failed */ }
Header: <axl/axl-str.h> (alongside the legacy string utilities and
axl_sscanf).
Primitives: init / init_n, eof, peek, remaining, skip_ws,
consume_char, consume_str, take_until, take_while, take_u64,
take_ident. No allocation; *out slices point directly into the
input buffer.
For one-shot fixed-pattern parses (IP addresses, MAC addresses,
ASCII numerics with separators), axl_sscanf is built on top of the
same primitives and reads cleaner:
unsigned a, b, c, d;
int consumed;
if (axl_sscanf(str, "%u.%u.%u.%u%n", &a, &b, &c, &d, &consumed) != 4
|| str[consumed] != '\0') {
return -1; /* malformed */
}
Supports the C99 conversions consumers actually need: `%c %d %i %u %o
hh h l ll z j, and * assignment suppression.
AxlString — String Builder
Mutable auto-growing string builder, like GLib’s GString. All strings
are UTF-8. Supports append, prepend, insert, printf-style formatting, and
truncation.
Header: <axl/axl-string.h>
Overview
Use AxlString when you need to build a string incrementally (in a
loop, with formatting, from multiple sources). For simple one-shot
formatting, use axl_asprintf instead.
AXL_AUTOPTR(AxlString) s = axl_string_new("Hello");
axl_string_append(s, " ");
axl_string_append_printf(s, "world #%d", 42);
axl_string_append_c(s, '!');
axl_printf("%s\n", axl_string_str(s)); // "Hello world #42!"
axl_printf("length: %zu\n", axl_string_len(s));
Stealing the Buffer
Transfer ownership of the internal buffer to avoid a copy:
AXL_AUTOPTR(AxlString) b = axl_string_new(NULL);
axl_string_append_printf(b, "key=%s", value);
char *result = axl_string_steal(b); // b is now empty
// caller owns 'result', must free with axl_free()
The builder can be reused after stealing — it starts empty with its allocated buffer released.
Error Handling
All mutation functions (append, printf, etc.) return int:
0 on success, -1 if the internal realloc fails. This matches the
convention used by axl_array_append, axl_hash_table_insert, etc.
AxlJson — JSON / JSON5
JSON reader (jsmn-based) and writer. Parse JSON strings into a token
tree, query values by key, and build JSON documents incrementally over
an AxlString. A separate colored UEFI-console pretty-printer is
provided for debug output.
The reader also accepts the JSON5 grammar superset — comments, trailing commas, single-quoted strings, unquoted keys, hex numbers — via an opt-in flag (see the JSON5 Support section below). The writer can emit trailing commas and JSON5 comments via additional opt-in flags.
Header: <axl/axl-json.h>
Overview
AXL provides three independent JSON APIs:
Reader (
AxlJsonReader) — parse a JSON string, extract values by key, iterate arrays.Writer (
AxlJsonWriter) — build JSON into an auto-growingAxlString. Orthogonal calls (containers, keys, atoms) with a state machine that handles comma placement and string escaping. Optional pretty-print mode with 2-space indent.Console printer (
axl_json_console_print) — colored, attribute-based pretty output to the UEFI console. Distinct from the writer’s pretty-print flag (which produces buffer output, no colors).
Reading JSON
const char *json = "{\"name\":\"AXL\",\"version\":1,\"debug\":true}";
AxlJsonReader r;
if (!axl_json_parse(json, axl_strlen(json), &r)) {
axl_printerr("invalid JSON\n");
return -1;
}
char name[64];
int64_t version;
bool debug;
if (!axl_json_get_string(&r, "name", name, sizeof(name)) ||
!axl_json_get_int (&r, "version", &version) ||
!axl_json_get_bool (&r, "debug", &debug)) {
axl_printerr("missing or wrong-type field\n");
axl_json_free(&r);
return -1;
}
axl_printf("name=%s version=%lld debug=%s\n",
name, (long long)version, debug ? "true" : "false");
axl_json_free(&r);
axl_json_parse returns false on invalid syntax, unbalanced braces,
or token-array allocation failure. Each axl_json_get_* returns
false if the key is missing, the value has the wrong type, or (for
strings) the caller buffer is too small. String values are copied into
the caller buffer — no zero-copy lifetime concerns. Always call
axl_json_free on the reader; the token array is heap-allocated.
Writing JSON
The writer builds into a caller-owned AxlString and grows on demand.
Comma placement, string escaping, and (optional) indentation are
handled internally. Containers, keys, and atoms are independent calls
— a single state machine knows whether the current container is an
object or an array.
AXL_AUTOPTR(AxlString) out = axl_string_new(NULL);
AxlJsonWriter w;
axl_json_writer_init(&w, out, AXL_JSON_WRITER_DEFAULT);
axl_json_obj_begin(&w);
axl_json_kv_str (&w, "name", "AXL");
axl_json_kv_uint(&w, "version", 1);
axl_json_kv_bool(&w, "debug", true);
axl_json_key(&w, "tags");
axl_json_arr_begin(&w);
axl_json_str(&w, "uefi");
axl_json_str(&w, "embedded");
axl_json_arr_end(&w);
axl_json_obj_end(&w);
axl_json_writer_finish(&w);
if (!axl_json_writer_error(&w)) {
axl_printf("%s\n", axl_string_str(out));
// {"name":"AXL","version":1,"debug":true,"tags":["uefi","embedded"]}
}
For convenience, axl_json_kv_* collapses a key + atomic value into
one call (the dominant shape). Use axl_json_key followed by an atom
or container when the value is a nested object/array.
Pretty Printing
Pass AXL_JSON_WRITER_PRETTY at init for 2-space-indent output with
newlines at every container and member boundary:
axl_json_writer_init(&w, out, AXL_JSON_WRITER_PRETTY);
// ... same writer calls ...
// {
// "name": "AXL",
// "version": 1,
// "tags": [
// "uefi",
// "embedded"
// ]
// }
Iterating Arrays
// Parse: {"items":["a","b","c"]}
AxlJsonArrayIter iter;
if (axl_json_array_begin(&r, "items", &iter)) {
AxlJsonReader elem;
while (axl_json_array_next(&iter, &elem)) {
char value[32];
axl_json_get_string(&elem, NULL, value, sizeof(value));
axl_printf(" %s\n", value);
}
}
For root-level arrays ([{...}, {...}]) use axl_json_root_array_begin
instead. Element readers borrow the parent’s token array — do not call
axl_json_free on them.
Round-Trip Transforms
axl_json_write_token splices an already-parsed token into the
writer’s output. The bridge writes string and key bytes verbatim from
the source — jsmn keeps escape sequences in source form, so this
preserves \uXXXX, escaped quotes, etc. without re-escaping. Useful
for parse → mutate → re-emit flows:
AxlJsonReader r;
axl_json_parse(input, input_len, &r);
AXL_AUTOPTR(AxlString) out = axl_string_new(NULL);
AxlJsonWriter w;
axl_json_writer_init(&w, out, AXL_JSON_WRITER_PRETTY);
axl_json_obj_begin(&w);
axl_json_kv_str(&w, "wrapped_in", "envelope");
axl_json_key(&w, "original");
axl_json_write_token(&w, &r, 0); /* splice the entire input doc */
axl_json_obj_end(&w);
axl_json_writer_finish(&w);
axl_json_free(&r);
Error Handling
The writer uses a sticky error flag. The first failure latches it;
subsequent calls become no-ops; one check after axl_json_writer_finish
is sufficient.
Sources of writer error:
AxlString OOM — auto-grow failed.
Structural misuse the writer can detect:
emit a bare-primitive root (only objects and arrays are valid JSON root values; matches
axl_json_parse’s contract)emit a value outside any container after the root has closed
emit a key inside an array
emit a value when a key was expected in object context
axl_json_obj_endon an open array (or vice versa)close when nothing is open
mismatched begin/end counts at
axl_json_writer_finish
What the writer does not catch:
Duplicate keys in the same object — JSON technically allows them; the writer doesn’t track emitted keys.
Non-UTF-8 bytes in
axl_json_str— passed through escaping unchanged.The contents of
axl_json_raw(&w, fragment)— the caller asserts the fragment is valid JSON; the writer splices it as-is.
JSON5 Support
JSON5 is a strict superset of JSON aimed at human-edited config files. AXL accepts the JSON5 grammar on the reader side via an opt-in flag, and emits JSON5-flavored extras (trailing commas, comments) on the writer side via additional flags. Strict callers see no behavior change.
Reader — opt in with AXL_JSON_PARSER_JSON5:
const char *cfg =
"// jedec.json5 — vendor codes per JEDEC JEP-106\n"
"{\n"
" vendors: [\n"
" { code: 0x802C, name: 'Micron' },\n"
" { code: 0x80AD, name: 'Hynix' }, // trailing comma below\n"
" { code: 0x80CE, name: 'Samsung' },\n"
" ],\n"
"}\n";
AxlJsonReader r;
if (!axl_json_parse_flags(cfg, axl_strlen(cfg),
AXL_JSON_PARSER_JSON5, &r)) {
/* parse error */
}
/* All the standard accessors (axl_json_get_string,
axl_json_array_begin, axl_json_array_next, ...) work
unchanged — JSON5 is normalized at parse time. */
axl_json_free(&r);
For sidecar files there’s a one-shot:
AxlJsonReader r;
void *raw;
size_t raw_len;
if (axl_json_load_file_flags("jedec.json5", AXL_JSON_PARSER_JSON5,
&r, &raw, &raw_len)) {
/* ... use r ... */
axl_json_free(&r);
axl_free(raw);
}
JSON5 features the parser accepts:
Line comments (
//) and block commentsTrailing commas in objects and arrays
Single-quoted strings (
'text')Unquoted (identifier-name) object keys
Hex number literals (
0x...) and+/-number prefixExtended string escapes:
\',\v,\0,\x##, line continuations
Strict callers (axl_json_parse, no flags) still go through the
existing jsmn-based path and reject JSON5 input. The JSON5 path is
strictly opt-in.
Writer — emit JSON5 extras with AXL_JSON_WRITER_TRAILING_COMMAS
and axl_json_comment:
AXL_AUTOPTR(AxlString) out = axl_string_new(NULL);
AxlJsonWriter w;
axl_json_writer_init(&w, out,
AXL_JSON_WRITER_PRETTY | AXL_JSON_WRITER_TRAILING_COMMAS);
axl_json_obj_begin(&w);
axl_json_comment(&w, "generated — do not edit by hand");
axl_json_kv_str(&w, "name", "AXL");
axl_json_kv_int(&w, "version", 1);
axl_json_obj_end(&w);
axl_json_writer_finish(&w);
/* {
* // generated — do not edit by hand
* "name": "AXL",
* "version": 1,
* }
*/
Comment behavior:
Pretty mode emits
// texton its own line at the current indent.Compact mode emits an inline
/* text */block; embedded close-comment sequences are split so the comment can’t terminate early.Embedded newlines truncate the comment.
Comments don’t disturb the writer’s container state — interleave them freely between values, between key+value pairs, or as the first/last item in a container.
The writer deliberately does not emit unquoted object keys or single-quoted strings. Those are JSON5 input-side conveniences only; emitting them adds escape-correctness footguns with no consumer benefit.
JSON5 conformance notes
AXL’s JSON5 support is scoped to the features that matter for
firmware-edited sidecar configs (the in-tree consumer is
tools/memspd.c reading share/jedec.json5). The following parts
of the json5.org spec are intentionally
not supported:
Infinity,-Infinity,NaN— AXL is freestanding UEFI with nolibm. There’s noaxl_json_get_doubleaccessor, so IEEE 754 special values would be lex-only and unretrievable.axl_json_get_inton a fractional or scientific-notation token truncates at the first non-digit character (pre-existing strict-mode behavior, unchanged):{x: 1.5}returns1.Unicode
IdentifierNamefor unquoted keys — only the ASCII subset ([A-Za-z_$][A-Za-z0-9_$]*) is recognized. Keys with Unicode letters, combining marks, ZWNJ, or ZWJ require quoting (single or double quotes both work).Unicode whitespace (U+00A0 NBSP, U+FEFF BOM, U+2028 LS, U+2029 PS, and other
Space_Separatorcharacters) — only the ASCII whitespace set plus\vand\fis treated as insignificant. Documents using Unicode separators as whitespace will fail to parse.
These gaps are deliberate — adding lex support for tokens that can’t be retrieved (floats) or for grammar that no firmware tool actually authors (Unicode identifier keys) would expand the attack surface and surprise consumers without unlocking new use cases. Open an issue if a real consumer needs any of these and they can be revisited.
Console Output
const char *body = http_response_body;
axl_json_console_print(body, axl_strlen(body));
Writes to the UEFI console with cyan keys, green strings, yellow numbers, magenta booleans. Distinct from the writer’s pretty-print flag, which emits to a buffer without color.
AxlXml — Streaming XML writer + pull-token reader
Streaming XML writer over an AxlString plus a pull-token reader
over a byte buffer. Caller manages namespaces (qnames like
D:multistatus are opaque to the writer; namespace declarations
are normal attributes). Out of scope: DTD validation, XSD, RelaxNG,
XPath, XSLT, XML signatures. UTF-8 only.
Header: <axl/axl-xml.h>
Overview
Two independent APIs:
Writer (
AxlXmlWriter) — value-typed state machine that builds XML into a caller-ownedAxlString. MirrorsAxlJsonWriter’s shape: init takes flags bitmask, emitters return void, errors are sticky and checked at_finish. Auto-escapes<>&in body text and<&"in attribute values. Tag balance and start-tag-open windows are enforced; misuse sets the sticky error flag.Reader (
AxlXmlReader) — opaque pull-token reader. OneAXL_XML_TOKEN_{START_ELEMENT, END_ELEMENT, TEXT, END_DOCUMENT}peraxl_xml_reader_nextcall. Attribute lookup viaaxl_xml_reader_attrwhile positioned at aSTART_ELEMENT. Entity decoding (5 named +&#NNN;/&#xHH;) into a reader-owned scratch buffer reused per token. CDATA passes through withis_cdataset. Comments, processing instructions, and DOCTYPE declarations are skipped silently.
Writing XML
AXL_AUTOPTR(AxlString) out = axl_string_new(NULL);
AxlXmlWriter w;
axl_xml_writer_init(&w, out, AXL_XML_WRITER_DEFAULT);
axl_xml_writer_prologue(&w);
axl_xml_writer_start_element(&w, "D:multistatus");
axl_xml_writer_attribute(&w, "xmlns:D", "DAV:");
axl_xml_writer_start_element(&w, "D:response");
axl_xml_writer_start_element(&w, "D:href");
axl_xml_writer_text(&w, "/dav/file.txt"); // auto-escaped
axl_xml_writer_end_element(&w);
axl_xml_writer_end_element(&w);
axl_xml_writer_end_element(&w);
axl_xml_writer_finish(&w);
if (!axl_xml_writer_error(&w)) {
axl_printf("%s\n", axl_string_str(out));
}
Pass AXL_XML_WRITER_PRETTY at init for 2-space indent + newlines
between child elements; text-only elements stay on one line.
Reading XML
const char *xml = "<root><greet lang=\"en\">hello & world</greet></root>";
AxlXmlReader *r = axl_xml_reader_new(xml, axl_strlen(xml));
AxlXmlToken t;
while (axl_xml_reader_next(r, &t)) {
switch (t.type) {
case AXL_XML_TOKEN_START_ELEMENT:
axl_printf("START: %.*s\n", (int)t.name_len, t.name);
const char *lang = axl_xml_reader_attr(r, "lang");
if (lang != NULL) {
axl_printf(" lang=%s\n", lang);
}
break;
case AXL_XML_TOKEN_TEXT:
axl_printf("TEXT: %.*s\n", (int)t.text_len, t.text);
break;
case AXL_XML_TOKEN_END_ELEMENT:
axl_printf("END: %.*s\n", (int)t.name_len, t.name);
break;
case AXL_XML_TOKEN_END_DOCUMENT:
break;
}
}
uint32_t line, col;
const char *msg;
if (axl_xml_reader_error(r, &line, &col, &msg)) {
axl_printerr("parse error at %u:%u: %s\n", line, col, msg);
}
axl_xml_reader_free(r);
Token name / text pointers reference reader-owned storage and
are invalidated on the next axl_xml_reader_next call. Copy out
anything you need to keep.
Entity decoding + safety
The reader decodes the five named entities (& < >
" ') plus decimal and hex numeric character
references. UTF-8 encoded for codepoints ≥ 0x80. Per XML 1.0 §4.1:
references to U+0000 and to UTF-16 surrogates (U+D800..U+DFFF) are
rejected as parse errors — both would otherwise corrupt downstream
C-string handling or produce invalid UTF-8.
DOCTYPE declarations are tokenized and skipped, but the reader
balances [ and ] brackets across the declaration so any
internal entity definitions are NEVER processed. This closes the
billion-laughs / external-entity attack classes without any
content-side checks.
Well-formedness
Strict by construction:
Tag balance enforced (mismatched end tag → error).
Exactly one root element (content after root → error).
Non-whitespace content before / after the root → error.
Tag nesting capped at 64 levels in both writer and reader.
Status code
X1 (writer) shipped 2026-05-10. X2 (reader) shipped 2026-05-10.
WebDAV axl_http_server_add_webdav’s PROPFIND emit migrated to
AxlXmlWriter in X3 the same day; class-2 verb bodies
(PROPPATCH / LOCK / UNLOCK) become implementable via the reader
when a consumer asks.
AxlCache — TTL Cache
TTL cache with LRU eviction. Fixed-size slots, string keys, opaque fixed-size values. Designed for single-threaded UEFI use.
Header: <axl/axl-cache.h>
Overview
AxlCache is a simple key-value store with time-based expiration and least-recently-used eviction when full. Values are copied into fixed-size slots (not stored as pointers).
Use cases: DNS resolution caching, HTTP response caching, SMBIOS lookup caching.
// Cache up to 16 entries, each 4 bytes (e.g., IPv4 addresses)
AXL_AUTOPTR(AxlCache) cache = axl_cache_new(16, sizeof(uint32_t), 60000);
// ^ ^ ^
// slots value size TTL (ms)
// Store a value
uint32_t addr = 0xC0A80101; // 192.168.1.1
axl_cache_put(cache, "gateway", &addr);
// Retrieve
uint32_t result;
if (axl_cache_get(cache, "gateway", &result) == 0) {
// hit -- result contains the cached value
}
// Invalidate a specific entry
axl_cache_invalidate(cache, "gateway");
// Invalidate all entries
axl_cache_invalidate_all(cache);
AxlRadixTree — Radix Tree
Compact prefix tree (radix tree) with string keys. Supports exact lookup, longest-prefix lookup, insert with automatic edge splitting, remove with node collapse, and depth-first iteration. Lookup is O(k) where k is the key length, independent of the number of entries.
Header: <axl/axl-radix-tree.h>
Overview
Use AxlRadixTree when you need longest-prefix matching — finding the
best match for a key that may not be an exact entry. The canonical use
case is URL route matching: given routes /api/users and /api, a
lookup for /api/users/42 returns the /api/users handler.
AxlRadixTree *tree = axl_radix_tree_new();
axl_radix_tree_insert(tree, "/api/users", handler_users);
axl_radix_tree_insert(tree, "/api", handler_api);
axl_radix_tree_insert(tree, "/css/", handler_static);
// Exact lookup
void *h = axl_radix_tree_lookup(tree, "/api/users"); // handler_users
// Longest-prefix lookup (the key feature)
const char *suffix;
h = axl_radix_tree_lookup_prefix(tree, "/api/users/42", &suffix);
// h = handler_users, suffix = "/42"
h = axl_radix_tree_lookup_prefix(tree, "/css/style.css", &suffix);
// h = handler_static, suffix = "style.css"
// Iterate all entries
axl_radix_tree_foreach(tree, print_entry, NULL);
axl_radix_tree_free(tree);
Value Destructor
Use axl_radix_tree_new_full(value_free) to auto-free values on
removal or tree destruction:
AxlRadixTree *tree = axl_radix_tree_new_full(axl_free);
axl_radix_tree_insert(tree, "key", axl_strdup("owned value"));
axl_radix_tree_free(tree); // value freed automatically
AxlRingBuf — Ring Buffer
Byte-oriented circular buffer with power-of-2 sizing and three API layers. Inspired by Linux kfifo: monotonically increasing indices with mask-based wrapping, using all buffer slots with no wasted-slot ambiguity.
Header: <axl/axl-ring-buf.h>
Overview
AxlRingBuf provides three layers, each building on the one below:
Layer 1 (Bytes): raw byte stream — push, pop, peek, discard, zero-copy scatter/gather regions
Layer 2 (Messages): variable-size length-prefixed frames – push_msg, pop_msg, peek_msg, peek_msg_size
Layer 3 (Elements): fixed-size typed entries — push_elem, pop_elem, peek_elem, peek/set_nth_elem
Supports reject-on-full (default) and overwrite-on-full modes.
// Heap-allocated ring buffer
AxlRingBuf *rb = axl_ring_buf_new(1024);
// Layer 1: raw bytes
axl_ring_buf_push(rb, "hello", 5);
char buf[32];
uint32_t n = axl_ring_buf_pop(rb, buf, sizeof(buf)); // n = 5
// Layer 2: variable-size messages
axl_ring_buf_push_msg(rb, "short", 5);
axl_ring_buf_push_msg(rb, "a longer message", 16);
uint32_t actual;
axl_ring_buf_pop_msg(rb, buf, sizeof(buf), &actual); // "short", actual=5
axl_ring_buf_free(rb);
// Layer 3: fixed-size elements (use new_fixed constructor)
AxlRingBuf *erb = axl_ring_buf_new_fixed(256, sizeof(int), 0);
int vals[] = {10, 20, 30};
for (int i = 0; i < 3; i++) {
axl_ring_buf_push_elem(erb, &vals[i]);
}
int out;
axl_ring_buf_pop_elem(erb, &out); // out = 10 (FIFO)
axl_ring_buf_free(erb);
Overwrite Mode
In overwrite mode, writes always succeed by discarding the oldest data:
AxlRingBuf *rb = axl_ring_buf_new_full(8, AXL_RING_BUF_OVERWRITE);
axl_ring_buf_push(rb, "12345678", 8); // full
axl_ring_buf_push(rb, "AB", 2); // discards "12", buffer has "345678AB"
axl_ring_buf_free(rb);
Embedded (Stack/Static) Usage
The struct is exposed, so it can be embedded in other structs or allocated on the stack with no heap allocation:
uint8_t buf[256];
AxlRingBuf rb;
axl_ring_buf_init(&rb, buf, sizeof(buf), 0, NULL);
axl_ring_buf_push(&rb, "no heap!", 8);
axl_ring_buf_deinit(&rb);
Zero-Copy Access
For high-performance I/O, access the buffer directly without copying:
AxlRingBufRegion regions[2];
uint32_t count = axl_ring_buf_peek_regions(rb, regions);
for (uint32_t i = 0; i < count; i++) {
process(regions[i].data, regions[i].len);
}
axl_ring_buf_pop_advance(rb, total_processed);
Push Statistics
Cumulative byte counters track every push attempt, including the ones that don’t survive:
uint64_t pushed = axl_ring_buf_pushes_total(rb); // bytes attempted
uint64_t lost = axl_ring_buf_pushes_lost(rb); // bytes invisible to consumer
pushes_lost covers (a) bytes rejected when reject-mode pushes
exceed available space, (b) bytes dropped from the front of an
oversized input in overwrite mode, and (c) older bytes displaced
by a new overwrite-mode push. Both counters reset on
axl_ring_buf_clear and on init.
Element-mode buffers translate trivially: divide by elem_size to
get element counts. The kernel POC’s reqlog server uses exactly
that pattern to surface “received” and “dropped” totals on its
/ endpoint.
API Reference
AxlHashTable
Typedefs
-
typedef struct AxlHashTable AxlHashTable
axl-hash-table.h:
GLib-style hash table with generic keys. FNV-1a hashing, chained collision resolution, automatic resize at 75% load factor.
API mirrors GLib’s GHashTable (g_hash_table_*). See GLib documentation for detailed usage patterns. Key differences:
axl_hash_table_new_str() is an AXL convenience (copies string keys)
Insert/replace return a tri-state int (1=new, 0=replaced, -1=OOM) instead of GLib’s bool, since GLib aborts on OOM and AXL recovers.
No reference counting (use axl_hash_table_free, not unref)
Ownership of keys and values
All three constructors allocate the AxlHashTable struct itself. They differ in how the table treats the key and value pointers passed to insert/replace:
axl_hash_table_new_str() Keys are COPIED internally via strdup; the table owns the copy and frees it on remove/free. Values are borrowed — caller manages their lifetime.
axl_hash_table_new(hash, equal) Keys and values are BORROWED. Caller manages all lifetimes. The table never copies or frees either.
axl_hash_table_new_full(hash, equal, key_destroy, value_destroy) Keys are NOT copied. The table TAKES OWNERSHIP if the corresponding destroy callback is non-NULL — on remove/free it calls the destroy callback on the pointer it stored. If a destroy callback is NULL, that side is treated as borrowed.
Insert vs. replace differ on key collision when ownership is in play (see axl_hash_table_insert / axl_hash_table_replace docs):
insert: keep the OLD key, destroy the NEW key
replace: destroy the OLD key, keep the NEW key Both destroy the old value either way.
-
typedef size_t (*AxlHashFunc)(const void *key)
Hash function: compute a hash value from a key.
-
typedef bool (*AxlEqualFunc)(const void *a, const void *b)
Equality function: return true if two keys are equal.
-
typedef void (*AxlHashTableForeachFunc)(const void *key, void *value, void *data)
Callback for axl_hash_table_foreach (GLib: GHFunc).
-
typedef bool (*AxlHashTableForeachRemoveFunc)(const void *key, void *value, void *data)
Predicate for axl_hash_table_foreach_remove (GLib: GHRFunc). Return true to remove the entry.
Enums
-
enum AxlHashTableInsertResult
Outcome of axl_hash_table_insert / axl_hash_table_replace.
Three distinguishable outcomes — the operation either added a new entry, replaced an existing one, or failed allocation. Follows the AxlSidecarStatus precedent for typed multi-outcome status enums.
Numeric values are part of the contract: callers comparing against the named constants AND against literal
1/0/-1both work. New codes only ever extend the negative range.Values:
-
enumerator AXL_HASH_TABLE_NEW
new entry was added
-
enumerator AXL_HASH_TABLE_REPLACED
existing entry was replaced
-
enumerator AXL_HASH_TABLE_ERR
allocation failure (also logged)
-
enumerator AXL_HASH_TABLE_NEW
Functions
-
size_t axl_direct_hash(const void *key)
Hash a pointer value directly. (GLib: g_direct_hash)
-
bool axl_direct_equal(const void *a, const void *b)
Pointer equality. (GLib: g_direct_equal)
-
AxlHashTable *axl_hash_table_new_str(void)
Create a string-keyed hash table that COPIES its keys.
Allocates the AxlHashTable struct. Each call to insert/replace also strdup’s the key into a heap-allocated copy that the table owns; that copy is freed on remove and on axl_hash_table_free. Values are borrowed — caller retains ownership and lifetime responsibility.
AXL extension; no GLib equivalent. Use this when keys are borrowed or literal strings and you want zero ceremony around key lifetime.
- Returns:
new AxlHashTable, or NULL on allocation failure.
-
AxlHashTable *axl_hash_table_new(AxlHashFunc hash_func, AxlEqualFunc equal_func)
Create a hash table that BORROWS keys and values.
Allocates the AxlHashTable struct. Keys and values are stored as raw pointers — the table never copies and never frees either side. Caller manages all lifetimes.
Matches g_hash_table_new().
- Parameters:
hash_func – hash function
equal_func – equality function
- Returns:
new AxlHashTable, or NULL on allocation failure.
-
AxlHashTable *axl_hash_table_new_full(AxlHashFunc hash_func, AxlEqualFunc equal_func, AxlDestroyNotify key_destroy, AxlDestroyNotify value_destroy)
Create a hash table that TAKES OWNERSHIP of keys/values.
Allocates the AxlHashTable struct. Keys and values are stored by pointer (NOT copied); the table calls
key_destroy/value_destroyon remove/free for any side whose destroy callback is non-NULL. Pass NULL for a side to leave it borrowed (caller retains ownership of that side).Pass NULL for
hash_funcandequal_functo default to axl_str_hash / axl_str_equal.Matches g_hash_table_new_full().
- Parameters:
hash_func – hash function, or NULL for axl_str_hash
equal_func – equality function, or NULL for axl_str_equal
key_destroy – key destructor, or NULL to borrow keys
value_destroy – value destructor, or NULL to borrow values
- Returns:
new AxlHashTable, or NULL on allocation failure.
-
void axl_hash_table_free(AxlHashTable *h)
Free a hash table and all entries.
Calls key_destroy and value_destroy on each entry (if set). Equivalent to g_hash_table_destroy().
- Parameters:
h – hash table (NULL-safe)
-
AxlHashTableInsertResult axl_hash_table_insert(AxlHashTable *h, const void *key, void *value)
Insert a key-value pair, keeping the OLD key on collision.
If the key already exists: the new key is freed via key_destroy (if set), the old key is kept, and the old value is freed via value_destroy (if set). Matches g_hash_table_insert().
For tables created with axl_hash_table_new_str() (copy_keys mode), insert and replace behave identically.
- Parameters:
h – hash table
key – key (copied for new() tables, owned for new_full())
value – value pointer
- Returns:
one of the AxlHashTableInsertResult values.
-
AxlHashTableInsertResult axl_hash_table_replace(AxlHashTable *h, const void *key, void *value)
Insert a key-value pair, keeping the NEW key on collision.
If the key already exists: the old key is freed via key_destroy (if set), the new key is kept, and the old value is freed via value_destroy (if set). Matches g_hash_table_replace().
For tables created with axl_hash_table_new_str() (copy_keys mode), insert and replace behave identically.
- Parameters:
h – hash table
key – key (copied for new() tables, owned for new_full())
value – value pointer
- Returns:
one of the AxlHashTableInsertResult values.
-
void *axl_hash_table_lookup(AxlHashTable *h, const void *key)
Look up a key.
Matches g_hash_table_lookup().
- Parameters:
h – hash table
key – key to look up
- Returns:
value pointer, or NULL if not found.
-
bool axl_hash_table_contains(AxlHashTable *h, const void *key)
Check if a key exists in the table.
Unlike lookup, this distinguishes between a key with a NULL value and a missing key. Matches g_hash_table_contains().
- Parameters:
h – hash table
key – key to check
- Returns:
true if the key exists, false otherwise.
-
bool axl_hash_table_remove(AxlHashTable *h, const void *key)
Remove an entry, calling key_destroy and value_destroy.
Matches g_hash_table_remove().
- Parameters:
h – hash table
key – key to remove
- Returns:
true if removed, false if not found.
-
bool axl_hash_table_steal(AxlHashTable *h, const void *key)
Remove an entry WITHOUT calling destructors.
The caller takes ownership of the value. For copy_keys tables, the internal key copy is freed (since the caller never had it). Matches g_hash_table_steal().
- Parameters:
h – hash table
key – key to steal
- Returns:
true if removed, false if not found.
-
size_t axl_hash_table_size(AxlHashTable *h)
Get the number of entries.
Matches g_hash_table_size().
- Parameters:
h – hash table
- Returns:
entry count.
-
bool axl_hash_table_owns_entries(AxlHashTable *h)
Predicate: does this table take ownership of (and free) any pointer key + value passed to insert / replace?
Returns true iff the table satisfies all of:
copy_keys == false— caller-provided keys are stored as-is (so aaxl_strdup’d key handed to insert isn’t redundantly re-strdup’d).key_destroy != NULL— the table will free those keys on remove / replace / table free.value_destroy != NULL— the table will likewise free stored values.
Tables built via
satisfy this. Tables built viaaxl_hash_table_new_full(..., axl_free_impl,
axl_free_impl)
axl_hash_table_new_str()(copy_keys=true, both destroys NULL) do NOT — passing strdup’d keys leaks the caller’s copy AND the value isn’t freed on table free.Useful for helpers that lazy-allocate or compose with a caller’s pre-existing table and want to verify the destroy-func contract before inserting heap-owned entries (e.g. the
axl_http_response_set_content_rangehelper that strdups both sides of theContent-Rangeheader).- Parameters:
h – hash table
- Returns:
true if both keys and values will be freed on remove and copy_keys is false (so caller-provided pointer keys are stored without redundant strdup); false otherwise.
-
void axl_hash_table_foreach(AxlHashTable *h, AxlHashTableForeachFunc func, void *data)
Iterate all entries. Order is undefined.
Matches g_hash_table_foreach().
- Parameters:
h – hash table
func – callback
data – opaque data passed to callback
-
size_t axl_hash_table_foreach_remove(AxlHashTable *h, AxlHashTableForeachRemoveFunc func, void *data)
Remove entries matching a predicate.
Calls
funcfor each entry. If it returns true, the entry is removed (key_destroy and value_destroy are called). Matches g_hash_table_foreach_remove().- Parameters:
h – hash table
func – predicate
data – opaque data
- Returns:
number of entries removed.
-
void axl_hash_table_iter_init(AxlHashTableIter *iter, AxlHashTable *h)
Initialize an iterator over a hash table.
Matches g_hash_table_iter_init().
- Parameters:
iter – iterator to initialize
h – hash table to iterate
-
bool axl_hash_table_iter_next(AxlHashTableIter *iter, void **key, void **value)
Advance to the next entry.
keyandvalueare optional (pass NULL to ignore). Matches g_hash_table_iter_next().- Parameters:
iter – iterator
key – receives key pointer, or NULL
value – receives value pointer, or NULL
- Returns:
true if an entry was returned, false if exhausted.
-
void axl_hash_table_iter_remove(AxlHashTableIter *iter)
Remove the current entry, calling destructors.
Must be called after a successful axl_hash_table_iter_next(). Matches g_hash_table_iter_remove().
- Parameters:
iter – iterator
-
void axl_hash_table_iter_steal(AxlHashTableIter *iter)
Remove the current entry WITHOUT calling destructors.
Must be called after a successful axl_hash_table_iter_next(). Matches g_hash_table_iter_steal().
- Parameters:
iter – iterator
-
struct AxlHashTableIter
- #include <axl-hash-table.h>
Stack-allocated iterator. Matches GHashTableIter. Fields prefixed with _ are private.
AxlArray
Typedefs
Functions
-
AxlArray *axl_array_new(size_t element_size)
Create a new dynamic array for value-mode storage.
- Parameters:
element_size – size of each element in bytes
- Returns:
new AxlArray, or NULL on failure. Free with axl_array_free().
-
void axl_array_free(AxlArray *a)
Free a dynamic array and its internal buffer.
- Parameters:
a – array (NULL-safe)
-
int axl_array_append(AxlArray *a, const void *element)
Append an element (value mode).
- Parameters:
a – array
element – pointer to element data to copy (element_size bytes)
- Returns:
AXL_OK on success, AXL_ERR on allocation failure.
-
void *axl_array_get(AxlArray *a, size_t index)
Get a pointer to the element at
index(value mode).- Parameters:
a – array
index – element index
- Returns:
pointer into internal buffer, or NULL if out of range.
-
size_t axl_array_len(AxlArray *a)
Get the number of elements in the array.
- Parameters:
a – array
- Returns:
number of elements.
-
void axl_array_clear(AxlArray *a)
Remove all elements. Does not free the internal buffer.
- Parameters:
a – array
-
int axl_array_append_ptr(AxlArray *a, void *ptr)
Append a pointer (pointer mode).
- Parameters:
a – array
ptr – pointer to store
- Returns:
AXL_OK on success, AXL_ERR on allocation failure.
-
void *axl_array_get_ptr(AxlArray *a, size_t index)
Get stored pointer at
index(pointer mode).- Parameters:
a – array
index – element index
- Returns:
stored pointer, or NULL if out of range.
-
void axl_array_sort(AxlArray *a, AxlCompareFunc compare)
Sort array elements in place using insertion sort.
- Parameters:
a – array
compare – comparison function (qsort-compatible)
-
int axl_array_remove_index(AxlArray *a, size_t index)
Remove the element at
index, shifting remaining elements left.- Parameters:
a – array
index – element index to remove
- Returns:
AXL_OK on success, AXL_ERR if index is out of range.
-
int axl_array_remove_index_fast(AxlArray *a, size_t index)
Remove the element at
indexby swapping with the last element.O(1) but does not preserve order.
- Parameters:
a – array
index – element index to remove
- Returns:
AXL_OK on success, AXL_ERR if index is out of range.
-
int axl_array_remove_range(AxlArray *a, size_t index, size_t len)
Remove
lenelements starting atindex, shifting remaining left.- Parameters:
a – array
index – first element to remove
len – number of elements to remove
- Returns:
AXL_OK on success, AXL_ERR if the range is out of bounds.
-
int axl_array_set_size(AxlArray *a, size_t len)
Resize the array to exactly
lenelements.If growing, new elements are zero-initialized. If growing beyond capacity, the internal buffer is reallocated. If shrinking, length is reduced without reallocating.
- Parameters:
a – array
len – desired number of elements
- Returns:
AXL_OK on success, AXL_ERR on allocation failure.
AxlList
Typedefs
-
typedef void *(*AxlCopyFunc)(const void *src, void *user_data)
axl-list.h:
Doubly-linked list. GLib GList equivalent. Node struct is exposed for direct traversal: for (AxlList *l = list; l; l = l->next) { use(l->data); }
Functions that modify the head return the new head pointer. Always assign back: list = axl_list_append(list, data);
Also defines shared callback types used by AxlSList and AxlQueue. AxlCopyFunc:
Deep copy function (GCopyFunc equivalent). Returns a newly allocated copy of
src.
Functions
-
AxlList *axl_list_append(AxlList *list, void *data)
Append data to the end of the list. O(n).
- Parameters:
list – current head (NULL for empty list)
data – data pointer to store
- Returns:
new head of the list.
-
AxlList *axl_list_prepend(AxlList *list, void *data)
Prepend data to the front of the list. O(1).
- Parameters:
list – current head (NULL for empty list)
data – data pointer to store
- Returns:
new head of the list.
-
AxlList *axl_list_insert(AxlList *list, void *data, int position)
Insert data at the given position. O(n).
Negative or out-of-range position appends to the end.
- Parameters:
list – current head
data – data pointer to store
position – 0-based insert position
- Returns:
new head of the list.
-
AxlList *axl_list_insert_sorted(AxlList *list, void *data, AxlCompareFunc func)
Insert data in sorted order. O(n).
The list must already be sorted by the same comparator.
- Parameters:
list – current head (sorted)
data – data pointer to insert
func – comparison function
- Returns:
new head of the list.
-
AxlList *axl_list_remove(AxlList *list, const void *data)
Remove the first element matching data. O(n).
Compares by pointer equality. Frees the node, not the data.
- Parameters:
list – current head
data – data pointer to find and remove
- Returns:
new head of the list.
-
AxlList *axl_list_reverse(AxlList *list)
Reverse the list in place. O(n).
- Parameters:
list – current head
- Returns:
new head (was the tail).
-
AxlList *axl_list_concat(AxlList *list1, AxlList *list2)
Concatenate two lists. O(n) in length of list1.
list2 is appended to list1. Both must be distinct lists.
- Parameters:
list1 – first list
list2 – second list (appended)
- Returns:
head of the combined list.
-
AxlList *axl_list_sort(AxlList *list, AxlCompareFunc func)
Sort the list using merge sort. O(n log n), stable.
- Parameters:
list – current head
func – comparison function
- Returns:
new head of the sorted list.
-
AxlList *axl_list_copy(AxlList *list)
Shallow copy of the list. O(n).
Copies node structure; data pointers are shared, not duplicated.
- Parameters:
list – list to copy
- Returns:
head of the new list, or NULL on failure.
-
void axl_list_free(AxlList *list)
Free all nodes. Does not free element data.
- Parameters:
list – head (NULL-safe)
-
void axl_list_free_full(AxlList *list, AxlDestroyNotify free_func)
Free all nodes, calling free_func on each element’s data.
- Parameters:
list – head (NULL-safe)
free_func – called on each node’s data
-
size_t axl_list_length(AxlList *list)
Count elements. O(n).
- Parameters:
list – head
- Returns:
number of elements.
-
AxlList *axl_list_nth(AxlList *list, size_t n)
Get the nth node. O(n).
- Parameters:
list – head
n – 0-based index
- Returns:
node at position n, or NULL if out of range.
-
void *axl_list_nth_data(AxlList *list, size_t n)
Get data from the nth node. O(n).
- Parameters:
list – head
n – 0-based index
- Returns:
data pointer, or NULL if out of range.
-
AxlList *axl_list_first(AxlList *list)
Get the first node (rewind to head). O(n).
Useful when you have a pointer to a middle node.
- Parameters:
list – any node in the list
- Returns:
the first node, or NULL if list is empty.
-
AxlList *axl_list_last(AxlList *list)
Get the last node. O(n).
- Parameters:
list – head
- Returns:
the last node, or NULL if list is empty.
-
AxlList *axl_list_find(AxlList *list, const void *data)
Find the first node with matching data (pointer equality). O(n).
- Parameters:
list – head
data – data pointer to find
- Returns:
matching node, or NULL.
-
AxlList *axl_list_find_custom(AxlList *list, const void *data, AxlCompareFunc func)
Find using a custom comparator. O(n).
Calls func(node->data, data) for each node. Returns the first node where func returns 0.
- Parameters:
list – head
data – data to compare against
func – comparison function
- Returns:
matching node, or NULL.
-
void axl_list_foreach(AxlList *list, AxlFunc func, void *user_data)
Call func for each element. O(n).
- Parameters:
list – head
func – callback
user_data – passed to callback
-
AxlList *axl_list_insert_before(AxlList *list, AxlList *sibling, void *data)
Insert data before the given sibling node. O(1).
If sibling is NULL, appends to the end.
- Parameters:
list – current head
sibling – node to insert before (NULL = append)
data – data pointer to store
- Returns:
new head of the list.
-
AxlList *axl_list_insert_after(AxlList *list, AxlList *sibling, void *data)
Insert data after the given sibling node. O(1).
If sibling is NULL, prepends to the front.
- Parameters:
list – current head
sibling – node to insert after (NULL = prepend)
data – data pointer to store
- Returns:
new head of the list.
-
AxlList *axl_list_remove_all(AxlList *list, const void *data)
Remove ALL nodes matching data (pointer equality). O(n).
Frees the nodes, not the data.
- Parameters:
list – current head
data – data pointer to match
- Returns:
new head of the list.
-
AxlList *axl_list_remove_link(AxlList *list, AxlList *link)
Unlink a specific node without freeing it. O(1).
The caller is responsible for freeing the unlinked node. The node’s prev/next pointers are set to NULL after unlinking.
- Parameters:
list – current head
link – node to unlink
- Returns:
new head of the list.
-
AxlList *axl_list_sort_with_data(AxlList *list, AxlCompareDataFunc func, void *user_data)
Sort with context-aware comparator. O(n log n), stable.
- Parameters:
list – current head
func – comparison function with user_data
user_data – passed to every compare call
- Returns:
new head of the sorted list.
-
AxlList *axl_list_copy_deep(AxlList *list, AxlCopyFunc func, void *user_data)
Deep copy using a copy function. O(n).
Calls func(node->data, user_data) for each node to produce the data pointer for the new list.
- Parameters:
list – list to copy
func – copy function for each element
user_data – passed to func
- Returns:
head of the new list, or NULL on failure.
-
struct AxlList
AxlSList
Functions
-
AxlSList *axl_slist_append(AxlSList *list, void *data)
Append data to the end. O(n).
- Parameters:
list – current head (NULL for empty)
data – data pointer
- Returns:
new head.
-
AxlSList *axl_slist_prepend(AxlSList *list, void *data)
Prepend data to the front. O(1).
- Parameters:
list – current head (NULL for empty)
data – data pointer
- Returns:
new head.
-
AxlSList *axl_slist_insert(AxlSList *list, void *data, int position)
Insert at position. O(n).
- Parameters:
list – current head
data – data pointer
position – 0-based position
- Returns:
new head.
-
AxlSList *axl_slist_insert_sorted(AxlSList *list, void *data, AxlCompareFunc func)
Insert in sorted order. O(n).
- Parameters:
list – current head (sorted)
data – data to insert
func – comparison function
- Returns:
new head.
-
AxlSList *axl_slist_remove(AxlSList *list, const void *data)
Remove first match by pointer equality. O(n).
- Parameters:
list – current head
data – data to find and remove
- Returns:
new head.
-
AxlSList *axl_slist_reverse(AxlSList *list)
Reverse in place. O(n).
- Parameters:
list – current head
- Returns:
new head (was tail).
-
AxlSList *axl_slist_concat(AxlSList *list1, AxlSList *list2)
Concatenate two lists. O(n) in list1.
- Parameters:
list1 – first list
list2 – appended to list1
- Returns:
head of combined list.
-
AxlSList *axl_slist_sort(AxlSList *list, AxlCompareFunc func)
Sort using merge sort. O(n log n), stable.
- Parameters:
list – current head
func – comparison function
- Returns:
new head.
-
AxlSList *axl_slist_copy(AxlSList *list)
Shallow copy. O(n).
- Parameters:
list – list to copy
- Returns:
new list head, or NULL on failure.
-
void axl_slist_free(AxlSList *list)
Free all nodes. Does not free element data.
- Parameters:
list – head (NULL-safe)
-
void axl_slist_free_full(AxlSList *list, AxlDestroyNotify free_func)
Free all nodes, calling free_func on each element’s data.
- Parameters:
list – head (NULL-safe)
free_func – called on each data
-
size_t axl_slist_length(AxlSList *list)
Count elements. O(n).
- Parameters:
list – head
- Returns:
number of elements.
-
AxlSList *axl_slist_nth(AxlSList *list, size_t n)
Get nth node. O(n).
- Parameters:
list – head
n – 0-based index
- Returns:
node, or NULL if out of range.
-
void *axl_slist_nth_data(AxlSList *list, size_t n)
Get data from nth node. O(n).
- Parameters:
list – head
n – 0-based index
- Returns:
data pointer, or NULL if out of range.
-
AxlSList *axl_slist_last(AxlSList *list)
Get last node. O(n).
- Parameters:
list – head
- Returns:
last node, or NULL.
-
AxlSList *axl_slist_find(AxlSList *list, const void *data)
Find by pointer equality. O(n).
- Parameters:
list – head
data – data to find
- Returns:
matching node, or NULL.
-
AxlSList *axl_slist_find_custom(AxlSList *list, const void *data, AxlCompareFunc func)
Find with custom comparator. O(n).
- Parameters:
list – head
data – data to compare against
func – comparison function
- Returns:
first node where func(node->data, data) == 0, or NULL.
-
void axl_slist_foreach(AxlSList *list, AxlFunc func, void *user_data)
Call func for each element. O(n).
- Parameters:
list – head
func – callback
user_data – passed to callback
-
AxlSList *axl_slist_insert_before(AxlSList *list, AxlSList *sibling, void *data)
Insert data before the given sibling node. O(n).
If sibling is NULL, appends to the end.
- Parameters:
list – current head
sibling – node to insert before (NULL = append)
data – data pointer to store
- Returns:
new head of the list.
-
AxlSList *axl_slist_remove_all(AxlSList *list, const void *data)
Remove ALL nodes matching data (pointer equality). O(n).
Frees the nodes, not the data.
- Parameters:
list – current head
data – data pointer to match
- Returns:
new head of the list.
-
AxlSList *axl_slist_remove_link(AxlSList *list, AxlSList *link)
Unlink a specific node without freeing it. O(n).
The caller is responsible for freeing the unlinked node. The node’s next pointer is set to NULL after unlinking.
- Parameters:
list – current head
link – node to unlink
- Returns:
new head of the list.
-
AxlSList *axl_slist_sort_with_data(AxlSList *list, AxlCompareDataFunc func, void *user_data)
Sort with context-aware comparator. O(n log n), stable.
- Parameters:
list – current head
func – comparison function with user_data
user_data – passed to every compare call
- Returns:
new head of the sorted list.
-
AxlSList *axl_slist_copy_deep(AxlSList *list, AxlCopyFunc func, void *user_data)
Deep copy using a copy function. O(n).
Calls func(node->data, user_data) for each node to produce the data pointer for the new list.
- Parameters:
list – list to copy
func – copy function for each element
user_data – passed to func
- Returns:
head of the new list, or NULL on failure.
-
struct AxlSList
- #include <axl-slist.h>
axl-slist.h:
Singly-linked list. GLib GSList equivalent. Node struct is exposed for direct traversal: for (AxlSList *l = list; l; l = l->next) { use(l->data); }
Functions that modify the head return the new head pointer. Always assign back: list = axl_slist_prepend(list, data);
Note: axl_slist_append is O(n). For frequent appends, use axl_slist_prepend + axl_slist_reverse, or use AxlList instead.
AxlQueue
Defines
-
AXL_QUEUE_INIT
Static initializer for stack-allocated queues.
Functions
-
AxlQueue *axl_queue_new(void)
Allocate a new empty queue.
- Returns:
new queue, or NULL on failure. Free with axl_queue_free().
-
void axl_queue_init(AxlQueue *queue)
Initialize a stack-allocated queue.
- Parameters:
queue – queue to initialize
-
void axl_queue_free(AxlQueue *queue)
Free queue and all nodes. Does not free element data.
- Parameters:
queue – queue (NULL-safe)
-
void axl_queue_free_full(AxlQueue *queue, AxlDestroyNotify free_func)
Free queue, calling free_func on each element’s data.
- Parameters:
queue – queue (NULL-safe)
free_func – called on each data
-
void axl_queue_clear(AxlQueue *queue)
Remove all elements. Queue itself is not freed.
- Parameters:
queue – queue
-
bool axl_queue_is_empty(AxlQueue *queue)
Check if the queue is empty.
- Parameters:
queue – queue
- Returns:
true if empty.
-
size_t axl_queue_get_length(AxlQueue *queue)
Get the number of elements. O(1).
- Parameters:
queue – queue
- Returns:
element count.
-
int axl_queue_push_head(AxlQueue *queue, void *data)
Push data to the front. O(1).
- Parameters:
queue – queue
data – data pointer
- Returns:
AXL_OK on success, AXL_ERR on allocation failure.
-
int axl_queue_push_tail(AxlQueue *queue, void *data)
Push data to the back. O(1).
- Parameters:
queue – queue
data – data pointer
- Returns:
AXL_OK on success, AXL_ERR on allocation failure.
-
void *axl_queue_pop_head(AxlQueue *queue)
Remove and return the front element. O(1).
- Parameters:
queue – queue
- Returns:
data pointer, or NULL if empty.
-
void *axl_queue_pop_tail(AxlQueue *queue)
Remove and return the back element. O(1).
- Parameters:
queue – queue
- Returns:
data pointer, or NULL if empty.
-
void *axl_queue_peek_head(AxlQueue *queue)
Peek at the front element without removing. O(1).
- Parameters:
queue – queue
- Returns:
data pointer, or NULL if empty.
-
void *axl_queue_peek_tail(AxlQueue *queue)
Peek at the back element without removing. O(1).
- Parameters:
queue – queue
- Returns:
data pointer, or NULL if empty.
-
void *axl_queue_peek_nth(AxlQueue *queue, size_t n)
Peek at the nth element. O(n).
- Parameters:
queue – queue
n – 0-based index from head
- Returns:
data pointer, or NULL if out of range.
-
void axl_queue_foreach(AxlQueue *queue, AxlFunc func, void *user_data)
Call func for each element, head to tail. O(n).
- Parameters:
queue – queue
func – callback
user_data – passed to callback
-
AxlQueue *axl_queue_copy(AxlQueue *queue)
Shallow copy. O(n).
- Parameters:
queue – queue to copy
- Returns:
new queue, or NULL on failure.
-
void axl_queue_reverse(AxlQueue *queue)
Reverse the queue in place. O(n).
- Parameters:
queue – queue
-
void axl_queue_sort(AxlQueue *queue, AxlCompareFunc func)
Sort the queue using merge sort. O(n log n).
- Parameters:
queue – queue
func – comparison function
-
AxlList *axl_queue_find(AxlQueue *queue, const void *data)
Find the first node matching data (pointer equality). O(n).
- Parameters:
queue – queue
data – data pointer to find
- Returns:
matching AxlList node, or NULL.
-
AxlList *axl_queue_find_custom(AxlQueue *queue, const void *data, AxlCompareFunc func)
Find using a custom comparator. O(n).
Returns the first node where func(node->data, data) == 0.
- Parameters:
queue – queue
data – data to compare against
func – comparison function
- Returns:
matching AxlList node, or NULL.
AxlRadixTree
Typedefs
-
typedef struct AxlRadixTree AxlRadixTree
axl-radix-tree.h:
Radix tree (compact prefix tree) with string keys. Supports exact lookup, longest-prefix lookup, insert, remove, and iteration.
Ideal for URL routing, path matching, and any scenario where longest-prefix matching is needed. Lookup is O(k) where k is the key length, independent of the number of entries.
API mirrors GLib/AXL conventions (axl_radix_tree_*).
Functions
-
AxlRadixTree *axl_radix_tree_new(void)
Create a new radix tree.
Values are not freed on removal or tree destruction.
- Returns:
new AxlRadixTree, or NULL on allocation failure.
-
AxlRadixTree *axl_radix_tree_new_full(AxlDestroyNotify value_free)
Create a new radix tree with a value destructor.
- Parameters:
value_free – value destructor, or NULL
- Returns:
new AxlRadixTree, or NULL on allocation failure.
-
void axl_radix_tree_free(AxlRadixTree *tree)
Free a radix tree and all entries.
Calls value_free on each entry’s value (if set).
- Parameters:
tree – radix tree (NULL-safe)
-
int axl_radix_tree_insert(AxlRadixTree *tree, const char *key, void *value)
Insert or replace a key-value pair.
If the key already exists, the old value is freed via value_free (if set) and replaced with the new value.
- Parameters:
tree – radix tree
key – string key (copied internally)
value – value pointer
- Returns:
AXL_OK on success, AXL_ERR on allocation failure.
-
void *axl_radix_tree_lookup(AxlRadixTree *tree, const char *key)
Exact lookup of a key.
- Parameters:
tree – radix tree
key – key to look up
- Returns:
value pointer, or NULL if not found.
-
void *axl_radix_tree_lookup_prefix(AxlRadixTree *tree, const char *key, const char **suffix)
Longest-prefix lookup.
Finds the longest inserted key that is a prefix of
keyand returns its value. Sets*suffixto point intokeyat the first character after the matched prefix. The caller can compute the prefix length as*suffix-key.On no match,
*suffixis not modified. Pass NULL forsuffixif the suffix pointer is not needed.- Parameters:
tree – radix tree
key – key to match against
suffix – receives pointer into key after matched prefix
- Returns:
value pointer, or NULL if no prefix matches.
-
bool axl_radix_tree_remove(AxlRadixTree *tree, const char *key)
Remove a key.
Calls value_free on the value (if set). Collapses intermediate nodes with a single child.
- Parameters:
tree – radix tree
key – key to remove
- Returns:
true if removed, false if not found.
-
size_t axl_radix_tree_size(AxlRadixTree *tree)
Get the number of entries.
- Parameters:
tree – radix tree
- Returns:
entry count.
-
void axl_radix_tree_foreach(AxlRadixTree *tree, AxlHashTableForeachFunc func, void *data)
Iterate all entries in depth-first order.
The key passed to
funcis a reconstructed NUL-terminated string valid only for the duration of the callback.- Parameters:
tree – radix tree
func – callback(key, value, data)
data – opaque data passed to callback
AxlRingBuf
Defines
-
AXL_RING_BUF_OVERWRITE
Overwrite oldest data when buffer is full.
axl-ring-buf.h:
Byte-oriented ring buffer (circular buffer) with power-of-2 sizing. Three API layers, each building on the one below:
Layer 1 (Bytes): push, pop, peek, discard, regions Layer 2 (Messages): push_msg, pop_msg, peek_msg (variable-size) Layer 3 (Elements): push_elem, pop_elem, peek_elem (fixed-size)
Supports partial writes, peek without consuming, zero-copy scatter/gather, optional overwrite-on-full, and user-provided backing buffers for embedded/stack use.
Naming follows GLib’s GQueue conventions: push (produce), pop (consume), peek (read without consuming), peek_nth (indexed access).
Inspired by Linux kfifo: monotonically increasing indices with mask-based wrapping. Uses all buffer slots — no wasted-slot ambiguity.
Functions
-
int axl_ring_buf_init(AxlRingBuf *rb, void *buf, uint32_t size, uint32_t flags, void (*buf_free)(void*))
Initialize an embedded ring buffer (byte mode).
No heap allocation. The caller owns both the AxlRingBuf struct and the backing buffer. Pass a
buf_freefunction to have deinit free the buffer, or NULL if the caller manages the buffer lifetime.- Parameters:
rb – caller-allocated struct
buf – backing buffer (must be power-of-2 sized)
size – buffer size in bytes (must be power of 2)
flags – AXL_RING_BUF_OVERWRITE or 0
buf_free – buffer deallocator, or NULL
- Returns:
AXL_OK on success, AXL_ERR if
sizeis not a power of 2 or args NULL.
-
int axl_ring_buf_init_fixed(AxlRingBuf *rb, void *buf, uint32_t size, uint32_t elem_size, uint32_t flags, void (*buf_free)(void*))
Initialize an embedded ring buffer (fixed-element mode).
Same as axl_ring_buf_init but enables Layer 3 element functions (push_elem, pop_elem, peek_elem, peek_nth_elem, set_nth_elem).
- Parameters:
rb – caller-allocated struct
buf – backing buffer (must be power-of-2 sized)
size – buffer size in bytes (must be power of 2)
elem_size – fixed element size in bytes
flags – AXL_RING_BUF_OVERWRITE or 0
buf_free – buffer deallocator, or NULL
- Returns:
AXL_OK on success, AXL_ERR if
sizeis not a power of 2 or args NULL.
-
void axl_ring_buf_deinit(AxlRingBuf *rb)
Release resources for an initialized ring buffer.
Calls the buf_free function (if set) to free the backing buffer. Does NOT free the AxlRingBuf struct itself (use axl_ring_buf_free for heap-allocated ring buffers).
- Parameters:
rb – ring buffer (NULL-safe)
-
AxlRingBuf *axl_ring_buf_new(uint32_t min_size)
Create a ring buffer in byte mode.
Capacity is rounded up to the next power of 2. Rejects pushes when full (use axl_ring_buf_new_full for overwrite mode).
- Parameters:
min_size – minimum capacity in bytes (rounded up to power of 2)
- Returns:
new AxlRingBuf, or NULL on allocation failure.
-
AxlRingBuf *axl_ring_buf_new_full(uint32_t min_size, uint32_t flags)
Create a ring buffer in byte mode with flags.
- Parameters:
min_size – minimum capacity in bytes (rounded up to power of 2)
flags – AXL_RING_BUF_OVERWRITE or 0
- Returns:
new AxlRingBuf, or NULL on allocation failure.
-
AxlRingBuf *axl_ring_buf_new_fixed(uint32_t min_size, uint32_t elem_size, uint32_t flags)
Create a ring buffer in fixed-element mode.
Enables Layer 3 element functions (push_elem, pop_elem, etc.).
- Parameters:
min_size – minimum capacity in bytes (rounded up to power of 2)
elem_size – fixed element size in bytes
flags – AXL_RING_BUF_OVERWRITE or 0
- Returns:
new AxlRingBuf, or NULL on allocation failure.
-
AxlRingBuf *axl_ring_buf_new_with_buffer(void *buf, uint32_t size, uint32_t flags)
Create a ring buffer using a caller-provided backing buffer.
The struct is heap-allocated but the backing buffer is owned by the caller. axl_ring_buf_free will NOT free the buffer.
- Parameters:
buf – caller-provided buffer (must be power-of-2 sized)
size – buffer size in bytes (must be power of 2)
flags – AXL_RING_BUF_OVERWRITE or 0
- Returns:
new AxlRingBuf, or NULL on failure.
-
void axl_ring_buf_free(AxlRingBuf *rb)
Free a heap-allocated ring buffer.
Calls buf_free on the backing buffer if set, then frees the struct. For embedded ring buffers, use axl_ring_buf_deinit instead.
- Parameters:
rb – ring buffer (NULL-safe)
-
uint32_t axl_ring_buf_push(AxlRingBuf *rb, const void *data, uint32_t len)
Push bytes into the ring buffer.
In reject mode (default), pushes up to the available space and returns the number of bytes actually written (may be less than
len). In overwrite mode, always pushes all bytes, advancing the read position to discard the oldest data as needed.- Parameters:
rb – ring buffer
data – source data
len – number of bytes to push
- Returns:
number of bytes pushed.
-
uint32_t axl_ring_buf_pop(AxlRingBuf *rb, void *dest, uint32_t len)
Pop bytes from the ring buffer.
- Parameters:
rb – ring buffer
dest – destination buffer
len – maximum bytes to pop
- Returns:
number of bytes popped (may be less than
len).
-
uint32_t axl_ring_buf_peek(AxlRingBuf *rb, void *dest, uint32_t len)
Peek at bytes without consuming them.
- Parameters:
rb – ring buffer
dest – destination buffer
len – maximum bytes to peek
- Returns:
number of bytes copied to
dest.
-
uint32_t axl_ring_buf_discard(AxlRingBuf *rb, uint32_t len)
Discard bytes from the read side without copying.
- Parameters:
rb – ring buffer
len – maximum bytes to discard
- Returns:
number of bytes discarded.
-
uint32_t axl_ring_buf_peek_regions(AxlRingBuf *rb, AxlRingBufRegion regions[2])
Get contiguous readable regions for zero-copy access.
Returns up to 2 regions covering all readable data. After processing, call axl_ring_buf_pop_advance to consume.
- Parameters:
rb – ring buffer
regions – receives up to 2 regions
- Returns:
number of regions (0, 1, or 2).
-
uint32_t axl_ring_buf_push_regions(AxlRingBuf *rb, AxlRingBufRegion regions[2])
Get contiguous writable regions for zero-copy access.
Returns up to 2 regions covering all writable space. After filling, call axl_ring_buf_push_advance to commit.
- Parameters:
rb – ring buffer
regions – receives up to 2 regions
- Returns:
number of regions (0, 1, or 2).
-
void axl_ring_buf_pop_advance(AxlRingBuf *rb, uint32_t len)
Advance the read position after zero-copy peek.
- Parameters:
rb – ring buffer
len – bytes consumed
-
void axl_ring_buf_push_advance(AxlRingBuf *rb, uint32_t len)
Advance the write position after zero-copy push.
- Parameters:
rb – ring buffer
len – bytes produced
-
int axl_ring_buf_push_msg(AxlRingBuf *rb, const void *data, uint32_t len)
Push a length-prefixed message atomically.
Stores [uint32_t len][data] in the ring buffer. The push is all-or-nothing in reject mode. In overwrite mode, the push always succeeds but may corrupt the oldest message framing.
- Parameters:
rb – ring buffer
data – message payload
len – payload length in bytes
- Returns:
AXL_OK on success, AXL_ERR if not enough space.
-
int axl_ring_buf_pop_msg(AxlRingBuf *rb, void *dest, uint32_t max_len, uint32_t *actual_len)
Pop the next length-prefixed message.
Consumes the length header and payload. If
max_lenis too small for the message, returns -1 without consuming.- Parameters:
rb – ring buffer
dest – destination buffer
max_len – destination buffer size
actual_len – receives actual message length (may be NULL)
- Returns:
AXL_OK on success, AXL_ERR if no message or buffer too small.
-
int axl_ring_buf_peek_msg(AxlRingBuf *rb, void *dest, uint32_t max_len, uint32_t *actual_len)
Peek at the next message without consuming.
Same as pop_msg but leaves the message in the buffer.
- Parameters:
rb – ring buffer
dest – destination buffer
max_len – destination buffer size
actual_len – receives actual message length (may be NULL)
- Returns:
AXL_OK on success, AXL_ERR if no message or buffer too small.
-
uint32_t axl_ring_buf_peek_msg_size(AxlRingBuf *rb)
Peek at the size of the next message without consuming.
- Parameters:
rb – ring buffer
- Returns:
message payload size, or 0 if no complete header available.
-
int axl_ring_buf_push_elem(AxlRingBuf *rb, const void *elem)
Push a fixed-size element (all-or-nothing).
Requires elem_size set at creation (new_fixed or init_fixed). In reject mode, fails if insufficient space. In overwrite mode, always succeeds by discarding the oldest data.
- Parameters:
rb – ring buffer (must be in element mode)
elem – element to push
- Returns:
AXL_OK on success, AXL_ERR if not enough space or not in element mode.
-
int axl_ring_buf_pop_elem(AxlRingBuf *rb, void *elem)
Pop a fixed-size element (all-or-nothing).
- Parameters:
rb – ring buffer (must be in element mode)
elem – receives the element
- Returns:
AXL_OK on success, AXL_ERR if not enough data or not in element mode.
-
int axl_ring_buf_peek_elem(AxlRingBuf *rb, void *dest)
Peek at the head element without consuming.
- Parameters:
rb – ring buffer (must be in element mode)
dest – receives the element
- Returns:
AXL_OK on success, AXL_ERR if empty or not in element mode.
-
int axl_ring_buf_peek_nth_elem(AxlRingBuf *rb, uint32_t index, void *dest)
Peek at an element by index without consuming.
Index 0 is the oldest element, get_length - 1 is the newest.
- Parameters:
rb – ring buffer (must be in element mode)
index – element index (0 = oldest)
dest – receives the element
- Returns:
AXL_OK on success, AXL_ERR if index out of range or not in element mode.
-
int axl_ring_buf_set_nth_elem(AxlRingBuf *rb, uint32_t index, const void *src)
Overwrite an element by index.
Index 0 is the oldest element, get_length - 1 is the newest.
- Parameters:
rb – ring buffer (must be in element mode)
index – element index (0 = oldest)
src – element data to write
- Returns:
AXL_OK on success, AXL_ERR if index out of range or not in element mode.
-
uint32_t axl_ring_buf_get_length(AxlRingBuf *rb)
Get the number of elements (element mode) or bytes (byte mode).
In element mode (elem_size > 0): returns readable / elem_size. In byte mode (elem_size == 0): returns readable byte count.
- Parameters:
rb – ring buffer
- Returns:
element count or byte count.
-
uint32_t axl_ring_buf_get_readable(AxlRingBuf *rb)
Get the number of readable bytes.
- Parameters:
rb – ring buffer
- Returns:
byte count available for reading.
-
uint32_t axl_ring_buf_get_writable(AxlRingBuf *rb)
Get the number of writable bytes.
- Parameters:
rb – ring buffer
- Returns:
byte count available for writing.
-
uint32_t axl_ring_buf_get_capacity(AxlRingBuf *rb)
Get the total buffer capacity.
- Parameters:
rb – ring buffer
- Returns:
capacity in bytes (always a power of 2).
-
bool axl_ring_buf_is_empty(AxlRingBuf *rb)
Check if the ring buffer is empty.
- Parameters:
rb – ring buffer
- Returns:
true if no data is available to read.
-
bool axl_ring_buf_is_full(AxlRingBuf *rb)
Check if the ring buffer is full.
- Parameters:
rb – ring buffer
- Returns:
true if no space is available for writing.
-
void axl_ring_buf_clear(AxlRingBuf *rb)
Discard all data and reset to empty.
Also resets the cumulative push counters (pushes_total / pushes_lost).
- Parameters:
rb – ring buffer
-
uint64_t axl_ring_buf_pushes_total(const AxlRingBuf *rb)
Cumulative bytes the producer has attempted to push.
Increments on every push call (push, push_msg, push_elem, push_advance) by the number of bytes the producer asked to commit — including bytes that were rejected (reject mode) or dropped because the input exceeded ring capacity. Reset to 0 by axl_ring_buf_clear and on init.
Unit is BYTES regardless of mode. For element-mode buffers, divide by the element size to get an element count. For message-mode buffers each message contributes (sizeof(uint32_t) + payload_len) bytes (the header counts).
- Parameters:
rb – ring buffer
- Returns:
cumulative attempted push bytes since last init/clear.
-
uint64_t axl_ring_buf_pushes_lost(const AxlRingBuf *rb)
Cumulative bytes the consumer cannot see due to overflow.
Counts:
bytes from new pushes that were rejected (reject mode)
bytes dropped from the front of an oversized input (overwrite mode, when a single push exceeds ring capacity)
bytes of older data displaced by new pushes (overwrite mode)
Reset to 0 by axl_ring_buf_clear and on init. Unit is BYTES.
Note: pushes_total - pushes_lost is the cumulative byte count the consumer was at any point able to observe; it is NOT a proxy for “currently in the ring” (that’s axl_ring_buf_get_readable).
- Parameters:
rb – ring buffer
- Returns:
cumulative lost bytes since last init/clear.
-
struct AxlRingBuf
- #include <axl-ring-buf.h>
Ring buffer with power-of-2 sizing and monotonic indices.
Can be heap-allocated (via axl_ring_buf_new) or embedded in another struct/stack (via axl_ring_buf_init). Fields are private — use the API functions, not direct access.
Public Members
-
uint8_t *buf
backing buffer
-
uint32_t size
capacity in bytes (power of 2)
-
uint32_t mask
size - 1
-
uint32_t read_pos
monotonically increasing read index
-
uint32_t write_pos
monotonically increasing write index
-
uint32_t flags
AXL_RING_BUF_OVERWRITE etc.
-
uint32_t elem_size
fixed element size (0 = byte mode)
-
uint64_t pushes_total
cumulative bytes the producer attempted to push (private)
-
uint64_t pushes_lost
cumulative bytes invisible to consumer (private)
-
void (*buf_free)(void*)
buffer deallocator, or NULL if caller-owned
-
uint8_t *buf
-
struct AxlRingBufRegion
- #include <axl-ring-buf.h>
Contiguous memory region for zero-copy access.
Ring buffer data may span two regions (before and after the internal wrap point). Use with peek_regions / push_regions.
AxlDigest
Message digest checksums (MD5, SHA-1, SHA-256). Standalone
implementations — available even without AXL_TLS=1.
Typedefs
-
typedef struct AxlChecksum AxlChecksum
Enums
-
enum AxlChecksumType
Hash algorithm selector.
axl-digest.h:
Message digest checksums: MD5, SHA-1, SHA-256. Mirrors GLib’s GChecksum API. Standalone implementations with no external dependencies (works without AXL_TLS=1).
One-shot:
char *hex = axl_compute_checksum(AXL_CHECKSUM_SHA1, data, len); axl_printf("SHA-1: %s\n", hex); axl_free(hex);
Incremental (streaming):
AxlChecksum *cs = axl_checksum_new(AXL_CHECKSUM_SHA1); axl_checksum_update(cs, part1, len1); axl_checksum_update(cs, part2, len2); const char *hex = axl_checksum_get_string(cs); axl_checksum_free(cs);
Values:
-
enumerator AXL_CHECKSUM_MD5
MD5 (128-bit / 16-byte digest)
-
enumerator AXL_CHECKSUM_SHA1
SHA-1 (160-bit / 20-byte digest)
-
enumerator AXL_CHECKSUM_SHA256
SHA-256 (256-bit / 32-byte digest)
-
enumerator AXL_CHECKSUM_MD5
Functions
-
size_t axl_checksum_type_get_length(AxlChecksumType type)
Get the digest length in bytes for the given algorithm.
- Parameters:
type – checksum algorithm
- Returns:
digest length, or 0 for unknown type.
-
AxlChecksum *axl_checksum_new(AxlChecksumType type)
Create a new checksum context.
- Parameters:
type – checksum algorithm
- Returns:
new context, or NULL on failure.
-
void axl_checksum_update(AxlChecksum *cs, const void *data, size_t len)
Feed data into the checksum.
Can be called multiple times. Must not be called after axl_checksum_get_string() or axl_checksum_get_digest().
- Parameters:
cs – checksum context
data – input data
len – input length in bytes
-
const char *axl_checksum_get_string(AxlChecksum *cs)
Get the digest as a hex string.
Returns a pointer to an internal NUL-terminated lowercase hex string. The pointer is valid until axl_checksum_free(). After this call, axl_checksum_update() must not be called.
- Parameters:
cs – checksum context
- Returns:
hex string (e.g. “a9993e36…”), or NULL on error.
-
void axl_checksum_get_digest(AxlChecksum *cs, uint8_t *buf, size_t *len)
Get the raw digest bytes.
Writes the binary digest into
buf. On entry,*lenis the buffer size; on return, it is set to the digest length. After this call, axl_checksum_update() must not be called.- Parameters:
cs – checksum context
buf – output buffer
len – [in/out] buffer size / digest length
-
void axl_checksum_reset(AxlChecksum *cs)
Reset a checksum for reuse.
Clears the state so axl_checksum_update() can be called again.
- Parameters:
cs – checksum context
-
void axl_checksum_free(AxlChecksum *cs)
Free a checksum context. NULL-safe.
- Parameters:
cs – checksum context
-
char *axl_compute_checksum(AxlChecksumType type, const void *data, size_t len)
Compute a checksum and return it as a hex string.
Convenience wrapper for new + update + get_string + free.
- Parameters:
type – checksum algorithm
data – input data
len – input length in bytes
- Returns:
newly allocated hex string (caller frees with axl_free), or NULL on error.
-
int axl_compute_checksum_digest(AxlChecksumType type, const void *data, size_t len, uint8_t *out, size_t out_len)
Compute a checksum and write raw digest bytes.
- Parameters:
type – checksum algorithm
data – input data
len – input length in bytes
out – output buffer (must be large enough)
out_len – output buffer size
- Returns:
AXL_OK on success, AXL_ERR on error.