nova-nix-0.8.0.0: cbits/nn_ctxstr.c
/*
* nn_ctxstr.c - Context-bearing string allocation and tracking.
*
* Each nn_ctxstr_t is a single contiguous malloc (header + flexible
* array of nn_sce_t elements). Pointers are tracked in a global
* dynamic array for bulk cleanup via nn_ctxstr_free_all().
*/
#include "nn_ctxstr.h"
#include "nn_assert.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* --- Tracking array --- */
#define NN_CTXSTR_INITIAL_CAPACITY 256u
static nn_ctxstr_t **g_tracked = NULL;
static uint32_t g_tracked_count = 0;
static uint32_t g_tracked_capacity = 0;
static void
track(nn_ctxstr_t *s)
{
if (g_tracked_count >= g_tracked_capacity) {
uint32_t new_cap = g_tracked_capacity == 0
? NN_CTXSTR_INITIAL_CAPACITY
: g_tracked_capacity * 2;
nn_ctxstr_t **new_arr = (nn_ctxstr_t **)realloc(
g_tracked, (size_t)new_cap * sizeof(nn_ctxstr_t *));
if (!new_arr) {
fprintf(stderr, "nn_ctxstr_track: realloc failed\n");
abort();
}
g_tracked = new_arr;
g_tracked_capacity = new_cap;
}
g_tracked[g_tracked_count++] = s;
}
/* --- Lifecycle --- */
nn_ctxstr_t *
nn_ctxstr_new(uint32_t text, uint32_t ctx_count)
{
/* The count is input-reachable (it scales with the evaluated
* expression); computing the size in uint64_t and capping it at the
* UINT32_MAX byte limit used across the C layer keeps the malloc
* argument from wrapping size_t on any platform. */
uint64_t bytes64 = sizeof(nn_ctxstr_t)
+ (uint64_t)ctx_count * sizeof(nn_sce_t);
if (bytes64 > UINT32_MAX) return NULL;
nn_ctxstr_t *s = (nn_ctxstr_t *)malloc((size_t)bytes64);
if (!s) return NULL;
s->text = text;
s->ctx_count = ctx_count;
memset(s->ctx, 0, (size_t)ctx_count * sizeof(nn_sce_t));
track(s);
return s;
}
void
nn_ctxstr_free_all(void)
{
uint32_t i;
for (i = 0; i < g_tracked_count; i++) {
free(g_tracked[i]);
}
free(g_tracked);
g_tracked = NULL;
g_tracked_count = 0;
g_tracked_capacity = 0;
}
/* --- Element setters --- */
/* Element bounds stay live under NDEBUG: the fill index derives from
* input-sized data crossing the FFI, so a violated bound must drop the
* write, not run past the sized array. */
void
nn_ctxstr_set_plain(nn_ctxstr_t *s, uint32_t idx,
uint32_t sp_hash, uint32_t sp_name)
{
NN_ASSERT(s != NULL && idx < s->ctx_count, "nn_ctxstr_set_plain: idx out of bounds");
if (s == NULL || idx >= s->ctx_count) return;
s->ctx[idx].tag = NN_SCE_PLAIN;
s->ctx[idx].sp_hash = sp_hash;
s->ctx[idx].sp_name = sp_name;
s->ctx[idx].output = 0;
}
void
nn_ctxstr_set_drv_output(nn_ctxstr_t *s, uint32_t idx,
uint32_t sp_hash, uint32_t sp_name,
uint32_t output)
{
NN_ASSERT(s != NULL && idx < s->ctx_count, "nn_ctxstr_set_drv_output: idx out of bounds");
if (s == NULL || idx >= s->ctx_count) return;
s->ctx[idx].tag = NN_SCE_DRV_OUTPUT;
s->ctx[idx].sp_hash = sp_hash;
s->ctx[idx].sp_name = sp_name;
s->ctx[idx].output = output;
}
void
nn_ctxstr_set_all_outputs(nn_ctxstr_t *s, uint32_t idx,
uint32_t sp_hash, uint32_t sp_name)
{
NN_ASSERT(s != NULL && idx < s->ctx_count, "nn_ctxstr_set_all_outputs: idx out of bounds");
if (s == NULL || idx >= s->ctx_count) return;
s->ctx[idx].tag = NN_SCE_ALL_OUTPUTS;
s->ctx[idx].sp_hash = sp_hash;
s->ctx[idx].sp_name = sp_name;
s->ctx[idx].output = 0;
}
/* --- Accessors --- */
uint32_t
nn_ctxstr_text(const nn_ctxstr_t *s)
{
return s->text;
}
uint32_t
nn_ctxstr_ctx_count(const nn_ctxstr_t *s)
{
return s->ctx_count;
}
/* Element reads keep their bound live under NDEBUG (see setters);
* a violated bound returns 0 rather than reading out of bounds. */
uint8_t
nn_ctxstr_elem_tag(const nn_ctxstr_t *s, uint32_t idx)
{
NN_ASSERT(s != NULL && idx < s->ctx_count, "nn_ctxstr_elem_tag: idx out of bounds");
if (s == NULL || idx >= s->ctx_count) return 0;
return s->ctx[idx].tag;
}
uint32_t
nn_ctxstr_elem_hash(const nn_ctxstr_t *s, uint32_t idx)
{
NN_ASSERT(s != NULL && idx < s->ctx_count, "nn_ctxstr_elem_hash: idx out of bounds");
if (s == NULL || idx >= s->ctx_count) return 0;
return s->ctx[idx].sp_hash;
}
uint32_t
nn_ctxstr_elem_name(const nn_ctxstr_t *s, uint32_t idx)
{
NN_ASSERT(s != NULL && idx < s->ctx_count, "nn_ctxstr_elem_name: idx out of bounds");
if (s == NULL || idx >= s->ctx_count) return 0;
return s->ctx[idx].sp_name;
}
uint32_t
nn_ctxstr_elem_output(const nn_ctxstr_t *s, uint32_t idx)
{
NN_ASSERT(s != NULL && idx < s->ctx_count, "nn_ctxstr_elem_output: idx out of bounds");
if (s == NULL || idx >= s->ctx_count) return 0;
return s->ctx[idx].output;
}