Registry¶
Header: libxs_reg.h
Thread-safe key-value store backed by a hash table with per-thread caching.
Configuration Macros¶
| Macro | Default | Description |
|---|---|---|
LIBXS_REGKEY_MAXSIZE |
64 | Maximum key size in bytes |
LIBXS_REGISTRY_NBUCKETS |
64 | Initial hash-table buckets (power of two); registries grow dynamically |
LIBXS_REGCACHE_NENTRIES |
16 | Thread-local cache entries per thread (power of two; 0 disables caching) |
Types¶
Lifetime¶
Create a new registry. Returns NULL in case of an error.
Destroy a registry and release all entries.
Return a pointer to the registry's internal lock. The returned lock can be passed as the lock argument to other registry functions (e.g., to hold the lock across multiple operations).
Iteration¶
void* libxs_registry_begin(libxs_registry_t* registry,
const void** key, size_t* cursor);
void* libxs_registry_next(libxs_registry_t* registry,
const void** key, size_t* cursor);
Enumerate entries. Initialize *cursor to 0 before the first begin call. Each call returns the value pointer and writes the key pointer to *key, or returns NULL when iteration is complete. The key pointer is suitably aligned to be cast to the caller's key type.
Access¶
void* libxs_registry_set(libxs_registry_t* registry,
const void* key, size_t key_size,
const void* value_init, size_t value_size,
libxs_lock_t* lock /* = NULL */);
Insert or update a key-value pair. The key must be binary-reproducible (zero-initialize padding). value_init may be NULL to defer initialization. Re-registering an existing key reallocates if the new value is larger. When lock is NULL the registry's internal lock is used. Returns a pointer to the stored value.
void* libxs_registry_get(const libxs_registry_t* registry,
const void* key, size_t key_size,
libxs_lock_t* lock /* = NULL */);
Look up a value by key. Returns NULL if not found.
unsigned int libxs_registry_hash(const libxs_registry_t* registry,
const void* key, size_t key_size);
void* libxs_registry_get_hashed(const libxs_registry_t* registry,
const void* key, size_t key_size, unsigned int hash,
libxs_lock_t* lock /* = NULL */);
void* libxs_registry_set_hashed(libxs_registry_t* registry,
const void* key, size_t key_size, unsigned int hash,
const void* value_init, size_t value_size,
libxs_lock_t* lock /* = NULL */);
Hash a key once and reuse the value across several operations. The seed
is per-registry (assigned in creation order, hence reproducible across
runs and independent of the allocator), so a hash is only valid for the
registry that produced it. The same value can also index caller-side
tables keyed by the same shape. libxs_registry_get/_set are
wrappers that hash internally.
libxs_registry_get_hashed additionally consults the thread-local
lookup cache before taking the lock, so a repeated query of the same
key costs no lock at all. That fast path is restricted to values too
large for the registry's inline storage, whose heap buffer does not move
when the table is rehashed, and it assumes the entry is neither removed
nor resized while another thread queries it -- true for a registry that
only grows. libxs_registry_get is unchanged and still takes the lock
whenever one is given.
int libxs_registry_get_copy(const libxs_registry_t* registry,
const void* key, size_t key_size,
void* value_out, size_t value_size,
libxs_lock_t* lock /* = NULL */);
Thread-safe query: copies up to value_size bytes of the stored value into value_out under the lock. Unlike libxs_registry_get, the caller never receives a raw pointer into the registry's internal storage, making this safe under concurrent modifications. Returns non-zero if the key was found, zero otherwise.
int libxs_registry_has(libxs_registry_t* registry,
const void* key, size_t key_size,
libxs_lock_t* lock /* = NULL */);
Check whether a key exists. Non-zero if found.
size_t libxs_registry_value_size(libxs_registry_t* registry,
const void* key, size_t key_size,
libxs_lock_t* lock /* = NULL */);
Query the stored value size in bytes. Returns 0 if not found.
void libxs_registry_remove(libxs_registry_t* registry,
const void* key, size_t key_size,
libxs_lock_t* lock /* = NULL */);
Remove a key-value pair and release its memory.
int libxs_registry_extract(libxs_registry_t* registry,
const void* key, size_t key_size,
void* value_out, size_t value_size,
libxs_lock_t* lock /* = NULL */);
Atomically retrieve and remove a key-value pair. Copies up to value_size bytes of the stored value into value_out (may be NULL to discard the value), then removes the entry and releases its memory. The lookup, copy, and removal are performed under a single lock hold, eliminating the race that exists when calling libxs_registry_get followed by libxs_registry_remove separately. Returns non-zero if the key was found, zero otherwise.
Serialization¶
Save the registry to a binary buffer. Pass buffer = NULL to query the required size (written to *size). On success, *size contains the number of bytes written. Returns EXIT_SUCCESS or EXIT_FAILURE.
libxs_registry_t* libxs_registry_load(const void* buffer, size_t size,
void (*fixup)(void* value, const void* key, size_t key_size,
size_t value_size, void* udata),
void* udata /* = NULL */);
Load a registry from a binary buffer (previously produced by libxs_registry_save). Keys are materialized immediately. When fixup is NULL, values remain in the buffer and are accessed on demand via libxs_registry_get; the buffer must remain valid for the lifetime of the returned registry. When fixup is non-NULL, each value is heap-allocated immediately and the callback is invoked once per entry, allowing the caller to restore pointers or handles by key identity. Returns a new registry or NULL on failure.
Status¶
Query registry capacity, number of entries, and total bytes stored.