code wiki / (root) / nx_native_config.nx

nx_native_config.nx source

↩ module page · 128 lines · 6243 B

1// nx_native_config.nx -- ordered CONFIG/POLICY storage on the NATIVE substrate, replacing borrowed TSV. 2// 3// Operator directive (2026-06-20): "stop using tsv, migrate, get everything to nishi ecosystem only." 4// A config is an ORDERED list of (key,value) field-records. Instead of a flat .tsv parsed by ad-hoc 5// tab-splitting, it lives in ONE content-addressed seg_store (the SAME substrate the asset catalog 6// uses): each row is a canon-encoded record under key "<tag>:NNNN", plus "<tag>:count". Rows read 7// back IN ORDER (so first-match-wins policy semantics are preserved). No TSV, no SQL -- nishi 8// ecosystem only (#feedback-pure-nishi-infomgmt-no-sql). Authored BY AN ORGAN (the seeder), data 9// lives in the store (data-driven, #11/#17). No hardware writes (Rule 26). license_tier: ORIGINAL 10import "nx_syscalls.nx" 11import "nx_canon_cid.nx" 12import "nx_uxf_decode.nx" 13import "nx_seg_store.nx" 14const K_MAGIC_8192: i64 = 8192 15 16func ncfg_streq(a: *u8, b: *u8) -> i64 { 17 var i: i64 = 0 18 while 1 == 1 { if a[i] != b[i] { return 0 } if a[i] == (0 as u8) { return 1 } i = i + 1 } 19 return 1 20} 21 22// "<tag>:NNNN" (4-digit, zero-padded) into out; returns length. 23func ncfg_key(tag: *u8, idx: i64, out: *u8) -> i64 { 24 var o: i64 = 0 25 while tag[o] != (0 as u8) { out[o] = tag[o]; o = o + 1 } 26 out[o] = 58 as u8; o = o + 1 // ':' 27 out[o] = (48 + ((idx / 1000) % 10)) as u8; o = o + 1 28 out[o] = (48 + ((idx / 100) % 10)) as u8; o = o + 1 29 out[o] = (48 + ((idx / 10) % 10)) as u8; o = o + 1 30 out[o] = (48 + (idx % 10)) as u8; o = o + 1 31 out[o] = 0 as u8 32 return o 33} 34// 2026-07-29 seq1232: per-call scratch was FRESH page-granular mmaps per call (ncfg_row runs per ROW; 35// hub_gw measured ~1 MB leaked per request, no munmap exists in the runtime). Lazy statics: one page 36// each for the life of the process. Safe: no ncfg consumer forks mid-call, and every boxed value is 37// consumed before the next ncfg_* call reuses the box (pp/ll point INTO the mapped store, the box is 38// just the out-param). 39static ncfg_scr_key: *u8 40static ncfg_scr_pp: *i64 41static ncfg_scr_ll: *i64 42static ncfg_scr_t: *u8 43// "<tag>:count" into out. 44func ncfg_count_key(tag: *u8, out: *u8) -> i64 { 45 var o: i64 = 0 46 while tag[o] != (0 as u8) { out[o] = tag[o]; o = o + 1 } 47 let suf: *u8 = ":count\x00" as *u8 48 var i: i64 = 0 49 while suf[i] != (0 as u8) { out[o] = suf[i]; o = o + 1; i = i + 1 } 50 out[o] = 0 as u8 51 return o 52} 53func ncfg_itoa(v: i64, out: *u8) -> i64 { 54 if (ncfg_scr_t as i64) == 0 { ncfg_scr_t = sys_mmap(28) } 55 let t: *u8 = ncfg_scr_t 56 var m: i64 = v 57 var k: i64 = 0 58 if m == 0 { t[0] = 48 as u8; k = 1 } 59 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 60 var i: i64 = 0 61 while i < k { out[i] = t[k - 1 - i]; i = i + 1 } 62 out[k] = 0 as u8 63 return k 64} 65func ncfg_atoi(s: *u8, n: i64) -> i64 { 66 var v: i64 = 0 67 var i: i64 = 0 68 while i < n { let c: i64 = s[i]; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 } 69 return v 70} 71 72// ---- SEED (write an ordered config) ---- 73func ncfg_begin() -> *i64 { return ss_begin() } 74// add row `idx` of config `tag` = canon_encode(keys,vals,n). Returns ss_add rc (0 ok). 75func ncfg_add_row(w: *i64, tag: *u8, idx: i64, keys: *i64, vals: *i64, n: i64) -> i64 { 76 let buf: *u8 = sys_mmap(K_MAGIC_8192) 77 let blen: i64 = canon_encode(keys, vals, n, buf) 78 let key: *u8 = sys_mmap(64) 79 ncfg_key(tag, idx, key) 80 return ss_add(w, 1, key, buf, blen) 81} 82func ncfg_set_count(w: *i64, tag: *u8, count: i64) -> i64 { 83 let key: *u8 = sys_mmap(64); ncfg_count_key(tag, key) 84 let val: *u8 = sys_mmap(28); let vl: i64 = ncfg_itoa(count, val) 85 return ss_add(w, 1, key, val, vl) 86} 87func ncfg_commit(prefix: *u8, w: *i64) -> i64 { return ss_commit(prefix, w, sys_now_ms()) } 88 89// ---- READ ---- 90// 2026-07-25 (debt seq962): route EVERY native-config reader through the cached open. ncfg_open had 91// 23 call sites, several of them PER-REQUEST in long-lived daemons (nx_hub_gw calls it 3x per HTTP 92// request), and bare ss_open reads the whole store into fresh anonymous RAM with no ss_close in the 93// tree -- measured 1336 kB leaked per hub_gw request. One line here fixes the whole class. 94// Freshness is preserved: ss_open_cached invalidates on the manifest (st_size, st_mtime), so a store 95// edit is still visible with no restart. KNOWN BOUND: mtime has 1-second granularity, so a rewrite 96// that lands in the SAME second AND leaves the manifest byte-length identical can serve one stale 97// read; the manifest changes length on essentially every real commit, and this is the same bound the 98// already-proven dss_open_maybe_cached cache has run under in production. 99func ncfg_open(prefix: *u8) -> *i64 { return ss_open_cached(prefix) } 100func ncfg_count(h: *i64, tag: *u8) -> i64 { 101 if (h as i64) == 0 { return 0 } 102 if (ncfg_scr_key as i64) == 0 { ncfg_scr_key = sys_mmap(64) } 103 if (ncfg_scr_pp as i64) == 0 { ncfg_scr_pp = sys_mmap(16) as *i64 } 104 if (ncfg_scr_ll as i64) == 0 { ncfg_scr_ll = sys_mmap(16) as *i64 } 105 let key: *u8 = ncfg_scr_key; ncfg_count_key(tag, key) 106 let pp: *i64 = ncfg_scr_pp 107 let ll: *i64 = ncfg_scr_ll 108 if ss_hget(h, key, pp, ll) != 1 { return 0 } 109 return ncfg_atoi(pp[0] as *u8, ll[0]) 110} 111// decode row idx of config tag into out_keys/out_vals; returns field count, or -1 if absent. 112func ncfg_row(h: *i64, tag: *u8, idx: i64, out_keys: *i64, out_vals: *i64, maxf: i64) -> i64 { 113 if (h as i64) == 0 { return 0 - 1 } 114 if (ncfg_scr_key as i64) == 0 { ncfg_scr_key = sys_mmap(64) } 115 if (ncfg_scr_pp as i64) == 0 { ncfg_scr_pp = sys_mmap(16) as *i64 } 116 if (ncfg_scr_ll as i64) == 0 { ncfg_scr_ll = sys_mmap(16) as *i64 } 117 let key: *u8 = ncfg_scr_key; ncfg_key(tag, idx, key) 118 let pp: *i64 = ncfg_scr_pp 119 let ll: *i64 = ncfg_scr_ll 120 if ss_hget(h, key, pp, ll) != 1 { return 0 - 1 } 121 return canon_decode(pp[0] as *u8, ll[0], out_keys, out_vals, maxf) 122} 123// field value by name within a decoded row (null if absent). 124func ncfg_field(keys: *i64, vals: *i64, nf: i64, name: *u8) -> *u8 { 125 var i: i64 = 0 126 while i < nf { if ncfg_streq(keys[i] as *u8, name) == 1 { return vals[i] as *u8 } i = i + 1 } 127 return 0 as *u8 128}