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}