nova-nix-0.2.0.0: cbits/nn_attrset.h
/*
* nn_attrset.h — Sorted symbol-keyed attribute set for nova-nix.
*
* Replaces Haskell's Data.Map.Strict (48 bytes per Bin node, pointer-
* chasing balanced tree) with a contiguous sorted array of (symbol, slot)
* pairs. Binary search for O(log n) lookup; linear scan for iteration.
*
* Construction is two-phase:
* 1. nn_attrset_new(cap) + nn_attrset_insert() — append unsorted
* 2. nn_attrset_freeze() — sort by symbol ID, enable binary search
*
* After freeze, the set is immutable (values can be updated in-place
* but keys cannot change). This matches Nix semantics: attribute sets
* are constructed once and never gain/lose keys.
*
* Values are opaque void* — Haskell passes StablePtr Thunk. The C
* side never dereferences them.
*
* Lifecycle: caller owns the nn_attrset_t* and must call nn_attrset_free.
* Values (StablePtrs) are NOT freed by nn_attrset_free — Haskell owns them.
*/
#ifndef NN_ATTRSET_H
#define NN_ATTRSET_H
#include "nn_symbol.h"
#include <stddef.h>
#include <stdint.h>
/* --- Types --- */
/* Opaque attribute set handle. */
typedef struct nn_attrset nn_attrset_t;
/* --- Lifecycle --- */
/* Allocate a new empty attribute set with space for `capacity` entries.
* Returns NULL on allocation failure. */
nn_attrset_t *nn_attrset_new(uint32_t capacity);
/* Free the attribute set structure. Does NOT free the value pointers
* (Haskell owns those via StablePtr). */
void nn_attrset_free(nn_attrset_t *set);
/* --- Construction (before freeze) --- */
/* Append a key-value pair. Keys need not be sorted or unique at this
* stage — duplicates are resolved at freeze time (last insert wins,
* matching Nix // merge semantics).
* Grows the internal arrays if capacity is exceeded. */
void nn_attrset_insert(nn_attrset_t *set, nn_symbol_t key, void *value);
/* Sort entries by symbol ID and deduplicate (last writer wins).
* Must be called exactly once before any lookup/keys/values access.
* After freeze, no more inserts are allowed. */
void nn_attrset_freeze(nn_attrset_t *set);
/* --- Query (after freeze) --- */
/* Look up a key by symbol. Returns the value pointer, or NULL if
* the key is not present. O(log n) binary search. */
void *nn_attrset_lookup(const nn_attrset_t *set, nn_symbol_t key);
/* Return the index of a key, or -1 if not found.
* Useful for updating value slots in place. */
int32_t nn_attrset_index(const nn_attrset_t *set, nn_symbol_t key);
/* Update the value at a known index. Index must be valid (0 <= idx < count).
* Used for lazy materialization: slot starts NULL, gets filled on first force. */
void nn_attrset_set_value(nn_attrset_t *set, uint32_t idx, void *value);
/* Return the value at a known index. */
void *nn_attrset_get_value(const nn_attrset_t *set, uint32_t idx);
/* Return the key at a known index. */
nn_symbol_t nn_attrset_get_key(const nn_attrset_t *set, uint32_t idx);
/* --- Bulk access --- */
/* Number of entries (after freeze: unique keys). */
uint32_t nn_attrset_size(const nn_attrset_t *set);
/* Direct pointer to the sorted keys array (count elements).
* Valid until the set is freed. */
const nn_symbol_t *nn_attrset_keys_ptr(const nn_attrset_t *set);
/* --- Lifecycle: bulk cleanup --- */
/* Free all tracked attribute sets at once (arena-style cleanup).
* Every set created via nn_attrset_new is automatically tracked.
* Call this once at evaluation end, before destroying sub-arenas. */
void nn_attrset_free_all(void);
/* --- Set operations --- */
/* Create a new set that is the right-biased union of `a` and `b`
* (keys in `b` override `a`). Both inputs must be frozen.
* The result is returned frozen. Caller owns the result. */
nn_attrset_t *nn_attrset_union(const nn_attrset_t *a, const nn_attrset_t *b);
/* Create a new set with the given keys removed.
* Input must be frozen. Result is returned frozen. */
nn_attrset_t *nn_attrset_remove_keys(
const nn_attrset_t *set,
const nn_symbol_t *keys,
uint32_t key_count);
#endif /* NN_ATTRSET_H */