From 05e096d8b74e7fbc8d91e290c72b8714df786150 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Thu, 16 Oct 2025 13:56:26 -0700 Subject: [PATCH] further port of rd cfg --- src/config/config.c | 351 ++++++++++++++++++++++++++++++++++++++++++-- src/config/config.h | 30 +++- 2 files changed, 368 insertions(+), 13 deletions(-) diff --git a/src/config/config.c b/src/config/config.c index 9bd7260b..b4f01b98 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -440,6 +440,9 @@ cfg_state_alloc(void) Arena *arena = arena_alloc(); CFG_State *state = push_array(arena, CFG_State, 1); state->arena = arena; + state->ctx.id_slots_count = 4096; + state->ctx.id_slots = push_array(arena, CFG_NodePtrSlot, state->ctx.id_slots_count); + state->ctx.root = cfg_node_alloc(state); return state; } @@ -458,90 +461,414 @@ cfg_state_ctx(CFG_State *state) return ctx; } +//- rjf: string allocations + +internal U64 +cfg_string_bucket_num_from_size(U64 size) +{ + U64 bucket_num = 0; + if(size > 0) + { + for EachElement(idx, cfg_string_bucket_chunk_sizes) + { + if(size <= cfg_string_bucket_chunk_sizes[idx]) + { + bucket_num = idx+1; + break; + } + } + } + return bucket_num; +} + +internal String8 +cfg_string_alloc(CFG_State *state, String8 string) +{ + //- rjf: allocate node + CFG_StringChunkNode *node = 0; + { + U64 bucket_num = cfg_string_bucket_num_from_size(string.size); + if(bucket_num == ArrayCount(cfg_string_bucket_chunk_sizes)) + { + CFG_StringChunkNode *best_node = 0; + CFG_StringChunkNode *best_node_prev = 0; + U64 best_node_size = max_U64; + { + for(CFG_StringChunkNode *n = state->free_string_chunks[bucket_num-1], *prev = 0; n != 0; (prev = n, n = n->next)) + { + if(n->size >= string.size && n->size < best_node_size) + { + best_node = n; + best_node_prev = prev; + best_node_size = n->size; + } + } + } + if(best_node != 0) + { + node = best_node; + if(best_node_prev) + { + best_node_prev->next = best_node->next; + } + else + { + state->free_string_chunks[bucket_num-1] = best_node->next; + } + } + else + { + U64 chunk_size = u64_up_to_pow2(string.size); + node = (CFG_StringChunkNode *)push_array(state->arena, U8, chunk_size); + } + } + else if(bucket_num != 0) + { + node = state->free_string_chunks[bucket_num-1]; + if(node != 0) + { + SLLStackPop(state->free_string_chunks[bucket_num-1]); + } + else + { + node = (CFG_StringChunkNode *)push_array(state->arena, U8, cfg_string_bucket_chunk_sizes[bucket_num-1]); + } + } + } + + //- rjf: fill node + String8 result = {0}; + if(node != 0) + { + result.str = (U8 *)node; + result.size = string.size; + MemoryCopy(result.str, string.str, result.size); + } + return result; +} + +internal void +cfg_string_release(CFG_State *state, String8 string) +{ + U64 bucket_num = cfg_string_bucket_num_from_size(string.size); + if(1 <= bucket_num && bucket_num <= ArrayCount(cfg_string_bucket_chunk_sizes)) + { + U64 bucket_idx = bucket_num-1; + CFG_StringChunkNode *node = (CFG_StringChunkNode *)string.str; + SLLStackPush(state->free_string_chunks[bucket_idx], node); + node->size = u64_up_to_pow2(string.size); + } +} + //- rjf: tree building internal CFG_Node * cfg_node_alloc(CFG_State *state) { + state->ctx.change_gen += 1; + // rjf: allocate + CFG_Node *result = state->free; + { + if(result) + { + SLLStackPop(state->free); + } + else + { + result = push_array_no_zero(state->arena, CFG_Node, 1); + } + } + + // rjf: generate ID & fill + state->id_gen += 1; + MemoryZeroStruct(result); + result->first = result->last = result->next = result->prev = result->parent = &cfg_nil_node; + result->id = state->id_gen; + + // rjf: store to ID -> cfg map + { + CFG_NodePtrNode *cfg_id_node = state->free_id_node; + if(cfg_id_node != 0) + { + SLLStackPop(state->free_id_node); + } + else + { + cfg_id_node = push_array(state->arena, CFG_NodePtrNode, 1); + } + U64 hash = u64_hash_from_str8(str8_struct(&result->id)); + U64 slot_idx = hash%state->ctx.id_slots_count; + DLLPushBack(state->ctx.id_slots[slot_idx].first, state->ctx.id_slots[slot_idx].last, cfg_id_node); + cfg_id_node->v = result; + } + + return result; } internal void cfg_node_release(CFG_State *state, CFG_Node *node) { + state->ctx.change_gen += 1; + Temp scratch = scratch_begin(0, 0); + + // rjf: unhook from context + cfg_node_unhook(state, node->parent, node); + + // rjf: gather root & all descendants + CFG_NodePtrList nodes = {0}; + for(CFG_Node *c = node; c != &cfg_nil_node; c = cfg_node_rec__depth_first(node, c).next) + { + cfg_node_ptr_list_push(scratch.arena, &nodes, c); + } + + // rjf: release all nodes + for(CFG_NodePtrNode *n = nodes.first; n != 0; n = n->next) + { + CFG_Node *c = n->v; + cfg_string_release(state, c->string); + SLLStackPush(state->free, c); + c->first = c->last = c->prev = c->parent = 0; + c->id = 0; + c->string = str8_zero(); + U64 hash = u64_hash_from_str8(str8_struct(&c->id)); + U64 slot_idx = hash%state->ctx.id_slots_count; + for(CFG_NodePtrNode *n = state->ctx.id_slots[slot_idx].first; n != 0; n = n->next) + { + if(n->v == c) + { + DLLRemove(state->ctx.id_slots[slot_idx].first, state->ctx.id_slots[slot_idx].last, n); + SLLStackPush(state->free_id_node, n); + break; + } + } + } + + scratch_end(scratch); } internal void cfg_node_release_all_children(CFG_State *state, CFG_Node *node) { - + for(CFG_Node *child = node->first, *next = &cfg_nil_node; child != &cfg_nil_node; child = next) + { + next = child->next; + cfg_node_release(state, child); + } } internal CFG_Node * cfg_node_new(CFG_State *state, CFG_Node *parent, String8 string) { - + CFG_Node *node = cfg_node_alloc(state); + cfg_node_insert_child(state, parent, parent->last, node); + cfg_node_equip_string(state, node, string); + return node; } internal CFG_Node * cfg_node_newf(CFG_State *state, CFG_Node *parent, char *fmt, ...) { - + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + CFG_Node *result = cfg_node_new(state, parent, string); + va_end(args); + scratch_end(scratch); + return result; } internal CFG_Node * cfg_node_new_replace(CFG_State *state, CFG_Node *parent, String8 string) { - + Temp scratch = scratch_begin(0, 0); + string = push_str8_copy(scratch.arena, string); + for(CFG_Node *child = parent->first->next, *next = &cfg_nil_node; child != &cfg_nil_node; child = next) + { + next = child->next; + cfg_node_release(state, child); + } + if(parent->first == &cfg_nil_node) + { + cfg_node_new(state, parent, str8_zero()); + } + CFG_Node *child = parent->first; + cfg_node_equip_string(state, child, string); + scratch_end(scratch); + return child; } internal CFG_Node * cfg_node_new_replacef(CFG_State *state, CFG_Node *parent, char *fmt, ...) { - + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + CFG_Node *result = cfg_node_new_replace(state, parent, string); + va_end(args); + scratch_end(scratch); + return result; } internal CFG_Node * cfg_node_deep_copy(CFG_State *state, CFG_Node *src_root) { - + CFG_NodeRec rec = {0}; + CFG_Node *dst_root = &cfg_nil_node; + CFG_Node *dst_parent = &cfg_nil_node; + for(CFG_Node *src = src_root; src != &cfg_nil_node; src = rec.next) + { + CFG_Node *dst = cfg_node_new(state, dst_parent, src->string); + if(dst_root == &cfg_nil_node) + { + dst_root = dst; + } + rec = cfg_node_rec__depth_first(src_root, src); + if(rec.push_count > 0) + { + dst_parent = dst; + } + else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1) + { + dst_parent = dst_parent->parent; + } + } + return dst_root; } internal void cfg_node_equip_string(CFG_State *state, CFG_Node *node, String8 string) { - + cfg_string_release(state, node->string); + node->string = cfg_string_alloc(state, string); + state->ctx.change_gen += 1; } internal void cfg_node_equip_stringf(CFG_State *state, CFG_Node *node, char *fmt, ...) { - + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + cfg_node_equip_string(state, node, string); + va_end(args); + scratch_end(scratch); } internal void cfg_node_insert_child(CFG_State *state, CFG_Node *parent, CFG_Node *prev_child, CFG_Node *new_child) { - + if(parent != &cfg_nil_node) + { + if(new_child->parent != &cfg_nil_node) + { + cfg_node_unhook(state, new_child->parent, new_child); + } + DLLInsert_NPZ(&cfg_nil_node, parent->first, parent->last, prev_child, new_child, next, prev); + new_child->parent = parent; + } } internal void cfg_node_unhook(CFG_State *state, CFG_Node *parent, CFG_Node *child) { - + if(child != &cfg_nil_node && parent == child->parent && parent != &cfg_nil_node) + { + DLLRemove_NPZ(&cfg_nil_node, parent->first, parent->last, child, next, prev); + child->parent = &cfg_nil_node; + } } internal CFG_Node * cfg_node_child_from_string_or_alloc(CFG_State *state, CFG_Node *parent, String8 string) { - + CFG_Node *node = cfg_node_child_from_string(parent, string); + if(node == &cfg_nil_node) + { + node = cfg_node_new(state, parent, string); + } + return node; } //- rjf: deserialization internal CFG_NodePtrList -cfg_node_ptr_list_from_string(Arena *arena, String8 root_path, String8 string) +cfg_node_ptr_list_from_string(Arena *arena, CFG_State *state, CFG_SchemaTable *schema_table, String8 root_path, String8 string) { + CFG_NodePtrList result = {0}; + Temp scratch = scratch_begin(&arena, 1); + //- rjf: parse the string as metadesk + MD_Node *root = md_tree_from_string(scratch.arena, string); + + //- rjf: iterate the top-level metadesk trees, generate new cfg trees for each + for MD_EachNode(tln, root->first) + { + CFG_Node *dst_root_n = &cfg_nil_node; + CFG_Node *dst_active_parent_n = &cfg_nil_node; + MD_NodeRec rec = {0}; + for(MD_Node *src_n = tln; !md_node_is_nil(src_n); src_n = rec.next) + { + // rjf: lookup schema for this string + MD_Node *schema = &md_nil_node; + { + MD_NodePtrList schemas = cfg_schemas_from_name(scratch.arena, schema_table, dst_active_parent_n->parent->string); + for(MD_NodePtrNode *n = schemas.first; n != 0 && schema == &md_nil_node; n = n->next) + { + schema = md_child_from_string(n->v, dst_active_parent_n->string, 0); + } + } + + // rjf: extract & transform metadesk node's string (it is raw textual data, so we need to + // go escaped -> raw, and derelativize paths) + String8 dst_n_string = {0}; + { + String8 src_n_string = src_n->string; + String8 src_n_string__raw = raw_from_escaped_str8(scratch.arena, src_n_string); + if(!md_node_has_tag(schema->first, str8_lit("no_relativize"), 0)) + { + if(str8_match(schema->first->string, str8_lit("path"), 0)) + { + src_n_string__raw = path_absolute_dst_from_relative_dst_src(scratch.arena, src_n_string__raw, root_path); + } + else if(str8_match(schema->first->string, str8_lit("path_pt"), 0)) + { + String8TxtPtPair parts = str8_txt_pt_pair_from_string(src_n_string__raw); + src_n_string__raw = push_str8f(scratch.arena, "%S:%I64d:%I64d", path_absolute_dst_from_relative_dst_src(scratch.arena, parts.string, root_path), parts.pt.line, parts.pt.column); + } + } + dst_n_string = src_n_string__raw; + } + + // rjf: allocate, fill, & insert new cfg for this metadesk node + CFG_Node *dst_n = cfg_node_alloc(state); + cfg_node_equip_string(state, dst_n, dst_n_string); + if(dst_active_parent_n != &cfg_nil_node) + { + cfg_node_insert_child(state, dst_active_parent_n, dst_active_parent_n->last, dst_n); + } + + // rjf: recurse + rec = md_node_rec_depth_first_pre(src_n, tln); + if(dst_active_parent_n == &cfg_nil_node) + { + dst_root_n = dst_n; + } + if(rec.push_count > 0) + { + dst_active_parent_n = dst_n; + } + else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1) + { + dst_active_parent_n = dst_active_parent_n->parent; + } + } + cfg_node_list_push(arena, &result, dst_root_n); + } + scratch_end(scratch); + return result; } diff --git a/src/config/config.h b/src/config/config.h index 806f3633..430065d9 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -77,6 +77,28 @@ struct CFG_NodeRec S32 pop_count; }; +//////////////////////////////// +//~ rjf: String Allocator + +read_only global U64 cfg_string_bucket_chunk_sizes[] = +{ + 16, + 64, + 256, + 1024, + 4096, + 16384, + 65536, + 0xffffffffffffffffull, +}; + +typedef struct CFG_StringChunkNode CFG_StringChunkNode; +struct CFG_StringChunkNode +{ + CFG_StringChunkNode *next; + U64 size; +}; + //////////////////////////////// //~ rjf: Config State Bundles @@ -97,6 +119,7 @@ struct CFG_State Arena *arena; CFG_Node *free; CFG_NodePtrNode *free_id_node; + CFG_StringChunkNode *free_string_chunks[ArrayCount(cfg_string_bucket_chunk_sizes)]; U64 id_gen; CFG_Ctx ctx; }; @@ -182,6 +205,11 @@ internal void cfg_state_release(CFG_State *state); //- rjf: state -> ctx internal CFG_Ctx *cfg_state_ctx(CFG_State *state); +//- rjf: string allocations +internal U64 cfg_string_bucket_num_from_size(U64 size); +internal String8 cfg_string_alloc(CFG_State *state, String8 string); +internal void cfg_string_release(CFG_State *state, String8 string); + //- rjf: tree building internal CFG_Node *cfg_node_alloc(CFG_State *state); internal void cfg_node_release(CFG_State *state, CFG_Node *node); @@ -198,6 +226,6 @@ internal void cfg_node_unhook(CFG_State *state, CFG_Node *parent, CFG_Node *chil internal CFG_Node *cfg_node_child_from_string_or_alloc(CFG_State *state, CFG_Node *parent, String8 string); //- rjf: deserialization -internal CFG_NodePtrList cfg_node_ptr_list_from_string(Arena *arena, String8 root_path, String8 string); +internal CFG_NodePtrList cfg_node_ptr_list_from_string(Arena *arena, CFG_State *state, CFG_SchemaTable *schema_table, String8 root_path, String8 string); #endif // CONFIG_H