code wiki / _hdl_build / nx_build_registry_lib.nx

nx_build_registry_lib.nx source

↩ module page · 164 lines · 7516 B

1// nx_build_registry_lib.nx -- the BUILD-REGISTRAR library (Librarian SSOT of backend ORGANS); the 2// build-time twin of the publisher's pub_submit -> ledger. SEPARATE from the WMS stream-registry (does 3// NOT clobber it, per coordinate-don't-clobber). Storage = the WMS-R0b framed-append floor (fa_appendz: 4// O_APPEND + flock(LOCK_EX) + write-until-complete = atomic, torn-safe, NO TSV). Records: 5// "BR organ=<name> owner=<role> state=<state> END\n" 6// IDEMPOTENT by LATEST-WINS-on-read: re-submit appends, but distinct-organ count is stable and the 7// newest record's state is the live one (ledger-style dedup without rewrites). Pure lib, no main. 8// Consumers: nx_build_registry (R1 gate), nx_build_bypass_gate (R2 audit). license_tier: ORIGINAL 9import "nx_framed_append.nx" 10import "nx_syscalls.nx" 11 12const BR_CAP: i64 = 256 // per-record byte cap for fa_appendz 13const BR_FBUF: i64 = 262144 // store read buffer 14const BR_SLOT: i64 = 128 // per-field byte slot 15const BR_MAX: i64 = 1024 // max distinct organs 16 17func br_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 18func br_streq(a: *u8, b: *u8) -> i64 { 19 var i: i64 = 0 20 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 21 if b[i] != (0 as u8) { return 0 } 22 return 1 23} 24func br_read(path: *u8, buf: *u8, cap: i64) -> i64 { 25 let fd: i64 = sys_openat_rd(path) 26 if fd < 0 { return 0 - 1 } 27 var total: i64 = 0 28 var nrd: i64 = sys_read(fd, buf, cap) 29 while nrd > 0 { 30 total = total + nrd 31 if total >= cap { nrd = 0 } else { nrd = sys_read(fd, ((buf as i64) + total) as *u8, cap - total) } 32 } 33 sys_close(fd) 34 return total 35} 36func br_find(buf: *u8, from: i64, to: i64, pat: *u8) -> i64 { 37 let pl: i64 = br_len(pat) 38 if pl == 0 { return 0 - 1 } 39 var i: i64 = from 40 while i + pl <= to { 41 var j: i64 = 0; var ok: i64 = 1 42 while j < pl { if buf[i + j] != pat[j] { ok = 0; j = pl } else { j = j + 1 } } 43 if ok == 1 { return i } 44 i = i + 1 45 } 46 return 0 - 1 47} 48// extract the space-delimited value of `key` within line [ls,le) into out (NUL-term). 1 if found. 49func br_extract(buf: *u8, ls: i64, le: i64, key: *u8, out: *u8) -> i64 { 50 let pos: i64 = br_find(buf, ls, le, key) 51 if pos < 0 { return 0 } 52 var v: i64 = pos + br_len(key) 53 var o: i64 = 0 54 while v < le { if buf[v] == (32 as u8) { v = le } else { if o < BR_SLOT - 1 { out[o] = buf[v]; o = o + 1 } v = v + 1 } } 55 out[o] = 0 as u8 56 return 1 57} 58// THE REGISTRAR INTAKE with GENEALOGY OWNERSHIP (operator 2026-06-21: organs owned god->...->organ): 59// `parent` = the organ's anchor node in knowledge/registry/genesis_lineage.tsv (so it traces to god, 60// verified by nx_build_lineage_gate). One atomic framed append. 61func br_submit_lin(store: *u8, organ: *u8, owner: *u8, parent: *u8, state: *u8) -> i64 { 62 let rec: *u8 = sys_mmap(BR_CAP + 16) 63 var o: i64 = 0 64 o = fa_cat(rec, o, "BR organ=" as *u8) 65 o = fa_cat(rec, o, organ) 66 o = fa_cat(rec, o, " owner=" as *u8) 67 o = fa_cat(rec, o, owner) 68 o = fa_cat(rec, o, " parent=" as *u8) 69 o = fa_cat(rec, o, parent) 70 o = fa_cat(rec, o, " state=" as *u8) 71 o = fa_cat(rec, o, state) 72 o = fa_cat(rec, o, " END" as *u8) 73 rec[o] = 0 as u8 74 return fa_appendz(store, rec, BR_CAP) 75} 76// backward-compatible 4-arg: default genealogy anchor = DESC-ORGANS (the genesis node every organ 77// descends from). Existing callers keep working; their records now carry the lineage anchor. 78func br_submit(store: *u8, organ: *u8, owner: *u8, state: *u8) -> i64 { 79 return br_submit_lin(store, organ, owner, "DESC-ORGANS" as *u8, state) 80} 81// latest genealogy anchor (parent= node) for `organ` into out. 1 if the organ is registered. 82// ★THE 256 KiB STORE BUFFER IS FREED ON EVERY PATH OUT. This is a LIBRARY: build machinery calls these 83// readers repeatedly, and BR_FBUF is real VMA (well above NXA_SMALL_MAX), so an unfreed buffer per call 84// is the vm.max_map_count exhaustion nx_syscalls documents. Found by nx_mmapbal. 85// ⚠The per-line `ob` slots are NOT touched: BR_SLOT is 128, i.e. bump-arena memory where sys_munmap 86// returns 0 without freeing -- and in br_distinct those same pointers are RETAINED in names[], so 87// "tidying" them would alias every name to one buffer and collapse the distinct count to 1. 88// ★★★★★★A LEAK FIX THAT DOES NOT READ THE POINTER'S LIFETIME TURNS A LEAK INTO A CORRUPTION. 89func br_get_parent(store: *u8, organ: *u8, out: *u8) -> i64 { 90 let buf: *u8 = sys_mmap(BR_FBUF) 91 let n: i64 = br_read(store, buf, BR_FBUF) 92 out[0] = 0 as u8 93 if n <= 0 { sys_munmap(buf, BR_FBUF); return 0 } 94 var found: i64 = 0 95 var i: i64 = 0 96 while i < n { 97 let ls: i64 = i 98 var le: i64 = ls 99 var g: i64 = 1 100 while g == 1 { if le >= n { g = 0 } else { if buf[le] == (10 as u8) { g = 0 } else { le = le + 1 } } } 101 let ob: *u8 = sys_mmap(BR_SLOT) 102 if br_extract(buf, ls, le, "organ=" as *u8, ob) == 1 { 103 if br_streq(ob, organ) == 1 { br_extract(buf, ls, le, "parent=" as *u8, out); found = 1 } 104 } 105 i = le + 1 106 } 107 sys_munmap(buf, BR_FBUF) 108 return found 109} 110// distinct organs in the store (latest-wins => re-submit does NOT inflate this). 111func br_distinct(store: *u8) -> i64 { 112 let buf: *u8 = sys_mmap(BR_FBUF) 113 let n: i64 = br_read(store, buf, BR_FBUF) 114 if n <= 0 { sys_munmap(buf, BR_FBUF); return 0 } 115 let names: *i64 = sys_mmap(8 * BR_MAX) as *i64 116 var cnt: i64 = 0 117 var i: i64 = 0 118 while i < n { 119 let ls: i64 = i 120 var le: i64 = ls 121 var g: i64 = 1 122 while g == 1 { if le >= n { g = 0 } else { if buf[le] == (10 as u8) { g = 0 } else { le = le + 1 } } } 123 let ob: *u8 = sys_mmap(BR_SLOT) 124 if br_extract(buf, ls, le, "organ=" as *u8, ob) == 1 { 125 var found: i64 = 0 126 var k: i64 = 0 127 while k < cnt { if br_streq(names[k] as *u8, ob) == 1 { found = 1; k = cnt } else { k = k + 1 } } 128 if found == 0 { if cnt < BR_MAX { names[cnt] = ob as i64; cnt = cnt + 1 } } 129 } 130 i = le + 1 131 } 132 // names[] holds pointers into the per-line arena slots and is dead once cnt is computed; the slots 133 // themselves are arena-backed (BR_SLOT=128) so they are not freed here -- see the note on 134 // br_get_parent for why touching them would be a correctness bug, not a tidy-up. 135 sys_munmap(names as *u8, 8 * BR_MAX) 136 sys_munmap(buf, BR_FBUF) 137 return cnt 138} 139// latest state for `organ` into out (NUL-term). returns 1 if the organ is registered, else 0. 140func br_get_state(store: *u8, organ: *u8, out: *u8) -> i64 { 141 let buf: *u8 = sys_mmap(BR_FBUF) 142 let n: i64 = br_read(store, buf, BR_FBUF) 143 out[0] = 0 as u8 144 if n <= 0 { sys_munmap(buf, BR_FBUF); return 0 } 145 var found: i64 = 0 146 var i: i64 = 0 147 while i < n { 148 let ls: i64 = i 149 var le: i64 = ls 150 var g: i64 = 1 151 while g == 1 { if le >= n { g = 0 } else { if buf[le] == (10 as u8) { g = 0 } else { le = le + 1 } } } 152 let ob: *u8 = sys_mmap(BR_SLOT) 153 if br_extract(buf, ls, le, "organ=" as *u8, ob) == 1 { 154 if br_streq(ob, organ) == 1 { br_extract(buf, ls, le, "state=" as *u8, out); found = 1 } 155 } 156 i = le + 1 157 } 158 sys_munmap(buf, BR_FBUF) 159 return found 160} 161func br_registered(store: *u8, organ: *u8) -> i64 { 162 let tmp: *u8 = sys_mmap(BR_SLOT) 163 return br_get_state(store, organ, tmp) 164}