code wiki / _hdl_build / nx_seg_imp_build.nx

nx_seg_imp_build.nx source

↩ module page · 100 lines · 4646 B

1// nx_seg_imp_build.nx -- build the NXW1 .imp IMPACT sidecar for every live segment of a shard, IN 2// PLACE (2026-07-25, the WAND rung, seq606). The cheap format-upgrade path: reads each segment's 3// .idx (terms+post slices) + .pos (the NXQ1 tf source) and writes seg-<id>.imp via temp->rename. 4// ADDITIVE-ONLY BY CONSTRUCTION (rules 13/26): no existing file is ever mutated, a crash leaves only 5// a .tmp, and a re-run skips segments already carrying the sidecar (idempotent, rule 10). A segment 6// without .pos (pre-phrase format) is SKIPPED LOUD -- compaction is that segment's upgrade path. 7// usage: nx_seg_imp_build <domain> [--force] 8// license_tier: ORIGINAL 9import "nx_docportal_search_seg.nx" // dss_prefix + nx_seg_store (ss_* primitives) 10import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 11const SIB_MAGIC_65536: i64 = 65536 12 13const SIB_MAXSEGS: i64 = 8192 14 15func sib_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 16// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 17// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 18// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 19// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 20func sib_num(v: i64) -> i64 { nxi_out(v); return 0 } 21 22// one segment -> 0 built | 1 skipped (already has .imp) | 2 no .pos (pre-phrase) | 3 failed 23func sib_one(prefix: *u8, segname: *u8, force: i64) -> i64 { 24 let ipath: *u8 = sys_mmap(512) 25 var o: i64 = 0 26 o = ss_cat(ipath, o, prefix) 27 o = ss_cat(ipath, o, segname) 28 o = ss_cat(ipath, o, ".imp" as *u8) 29 ipath[o] = 0 as u8 30 if force == 0 { 31 let tfd: i64 = sys_openat_rd(ipath) 32 if tfd >= 0 { sys_close(tfd); return 1 } 33 } 34 let outs: *i64 = sys_mmap(64) as *i64 35 ss_load_aux2(prefix, segname, outs, 1) 36 if outs[2] == 0 { return 3 } 37 if outs[4] == 0 { return 3 } 38 let tsz: i64 = outs[3] 39 let psz: i64 = outs[5] 40 if tsz < 8 { return 3 } 41 if psz < 8 { return 3 } 42 let qpath: *u8 = sys_mmap(512) 43 o = 0 44 o = ss_cat(qpath, o, prefix) 45 o = ss_cat(qpath, o, segname) 46 o = ss_cat(qpath, o, ".pos" as *u8) 47 qpath[o] = 0 as u8 48 let szp: *i64 = sys_mmap(16) as *i64 49 szp[0] = 0 50 let qb: *u8 = ss_loadfile(qpath, szp, 1) 51 if (qb as i64) == 0 { return 2 } 52 if szp[0] < 8 { return 2 } 53 let wcap: i64 = tsz * 2 + psz * 10 + SIB_MAGIC_65536 54 let wblob: *u8 = sys_mmap(wcap) 55 let wlen: i64 = ss_build_imp(outs[2] as *u8, tsz, outs[4] as *u8, psz, qb, szp[0], wblob) 56 if wlen < 0 { return 3 } 57 let tpath: *u8 = sys_mmap(512) 58 o = 0 59 o = ss_cat(tpath, o, ipath) 60 o = ss_cat(tpath, o, ".tmp" as *u8) 61 tpath[o] = 0 as u8 62 if ss_writefile(tpath, wblob, wlen) != 0 { return 3 } 63 if sys_renameat(tpath, ipath) != 0 { return 3 } 64 return 0 65} 66 67func main(argc: i64, argv: *i64) -> i64 { 68 var domain: *u8 = "web" as *u8 69 if argc >= 2 { domain = argv[1] as *u8 } 70 var force: i64 = 0 71 if argc >= 3 { let f: *u8 = argv[2] as *u8; if f[0] == (45 as u8) { force = 1 } } 72 let prefix: *u8 = sys_mmap(512) 73 dss_prefix(domain, prefix) 74 let segs: *i64 = sys_mmap(8 * SIB_MAXSEGS) as *i64 75 let ns: i64 = ss_manifest_cap(prefix, segs, SIB_MAXSEGS) 76 sib_puts("=== nx_seg_imp_build " as *u8); sib_puts(prefix); sib_puts(" segments=" as *u8); sib_num(ns); sib_puts(" ===\n" as *u8) 77 if ns <= 0 { sib_puts("no live segments\nverdict=FAIL\n" as *u8); return 1 } 78 var built: i64 = 0 79 var skipped: i64 = 0 80 var nopos: i64 = 0 81 var failed: i64 = 0 82 var s: i64 = 0 83 while s < ns { 84 let rc: i64 = sib_one(prefix, segs[s] as *u8, force) 85 if rc == 0 { built = built + 1 } 86 if rc == 1 { skipped = skipped + 1 } 87 if rc == 2 { nopos = nopos + 1; sib_puts("NO-POS (pre-phrase, compact to upgrade): " as *u8); sib_puts(segs[s] as *u8); sib_puts("\n" as *u8) } 88 if rc == 3 { failed = failed + 1; sib_puts("FAILED: " as *u8); sib_puts(segs[s] as *u8); sib_puts("\n" as *u8) } 89 s = s + 1 90 } 91 sib_puts("built=" as *u8); sib_num(built) 92 sib_puts(" skipped=" as *u8); sib_num(skipped) 93 sib_puts(" nopos=" as *u8); sib_num(nopos) 94 sib_puts(" failed=" as *u8); sib_num(failed) 95 sib_puts("\n" as *u8) 96 if failed > 0 { sib_puts("verdict=FAIL\n" as *u8); return 2 } 97 if nopos > 0 { sib_puts("verdict=PARTIAL (pre-phrase segments remain; readers fall back shard-wide until compacted)\n" as *u8); return 0 } 98 sib_puts("verdict=PASS\n" as *u8) 99 return 0 100}