dict_t Overview

The dict_t family provides a generic, allocator-agnostic hash dictionary that maps arbitrary byte-span keys to fixed-size values. Keys and values are both stored as raw bytes, making the engine suitable for any value type: a scalar such as float or uint8_t, a struct copied by value, or a pointer to a heap object. Type-specific wrappers cast in and out of the void* interface without the engine needing to know anything about the concrete type.

Keys are represented as a dict_key_t — a (const void* data, size_t len) byte-span pair. This handles both null-terminated C-strings and arbitrary binary keys uniformly. The convenience macro DICT_KEY builds a dict_key_t from a C-string literal in a single expression. The dict always deep-copies key bytes into its own allocator-managed storage on insert, so the caller may free or reuse the key immediately after the call returns.

All memory is managed through an allocator_vtable_t supplied by the caller. init_dict() and insert_dict() each accept an allocator argument; all other operations use the allocator stored in the dict_t at initialisation time. This split allows nodes to be allocated from a different arena than the dict struct itself if needed. See C Allocator Overview for the full allocator API and available implementations.

Error handling follows the expected value pattern: operations that produce a new object return a dict_expect_t whose has_value field distinguishes success from failure, while in-place mutations return an error_code_t directly. Callers must always check the result before using the value.

#include "c_dict.h"

allocator_vtable_t a = heap_allocator();

/* Create a dict mapping C-string keys to float values. */
dict_expect_t r = init_dict(16, sizeof(float), FLOAT_TYPE, true, a);
if (!r.has_value) { /* handle r.u.error */ }
dict_t* d = r.u.value;

float pi = 3.14159f;
insert_dict(d, DICT_KEY("pi"), &pi, a);

float v;
if (get_dict_value(d, DICT_KEY("pi"), &v) == NO_ERROR)
    printf("pi = %f\n", v);

return_dict(d);