code wiki / _hdl_build / nx_search_ship_lib.nx

nx_search_ship_lib.nx source

↩ module page · 273 lines · 11964 B

1// nx_search_ship_lib.nx -- THE PURE HALF OF THE SEARCH SHIP LOOP (2026-09-14): every parser the loop reads a 2// receipt or a probe with, in a lib so nx_search_ship_gate can drive them on planted fixtures without forking a 3// build, a deploy or the edge. The program (nx_search_ship) owns the forks and the exit codes; nothing here 4// touches a file, a socket or a process. Prefix sx_ (ss_ is the seg store's, os_ is nx_organ_ship's). 5// license_tier: ORIGINAL 6import "nx_syscalls.nx" 7 8const SX_PIPE: i64 = 124 9const SX_QUOTE: i64 = 34 10const SX_LF: i64 = 10 11const SX_SPACE: i64 = 32 12const SX_HASH: i64 = 35 13const SX_MINUS: i64 = 45 14const SX_D0: i64 = 48 15const SX_D9: i64 = 57 16const SX_PHASE_SLOTS: i64 = 8 // recv, prep, s1, l0dec, auth, div, total, cand 17 18func sx_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 19func sx_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var oo: i64 = o; while s[i] != (0 as u8) { d[oo] = s[i]; oo = oo + 1; i = i + 1 } d[oo] = 0 as u8; return oo } 20func sx_catn(d: *u8, o: i64, v: i64) -> i64 { 21 var oo: i64 = o 22 var x: i64 = v 23 if x < 0 { d[oo] = SX_MINUS as u8; oo = oo + 1; x = 0 - x } 24 if x == 0 { d[oo] = SX_D0 as u8; oo = oo + 1; d[oo] = 0 as u8; return oo } 25 var pw: i64 = 1 26 while x / pw >= 10 { pw = pw * 10 } 27 while pw > 0 { d[oo] = (SX_D0 + (x / pw) % 10) as u8; oo = oo + 1; pw = pw / 10 } 28 d[oo] = 0 as u8 29 return oo 30} 31func sx_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] == b[i] { if a[i] == (0 as u8) { return 1 } i = i + 1 } return 0 } 32func sx_starts(s: *u8, p: *u8) -> i64 { var i: i64 = 0; while p[i] != (0 as u8) { if s[i] != p[i] { return 0 } i = i + 1 } return 1 } 33// first occurrence of needle in hay[0..n), -1 absent 34func sx_find(hay: *u8, n: i64, needle: *u8) -> i64 { 35 let nl: i64 = sx_slen(needle) 36 if nl == 0 { return 0 - 1 } 37 var i: i64 = 0 38 while i + nl <= n { 39 var j: i64 = 0 40 var hit: i64 = 1 41 while j < nl { if hay[i + j] != needle[j] { hit = 0; j = nl } else { j = j + 1 } } 42 if hit == 1 { return i } 43 i = i + 1 44 } 45 return 0 - 1 46} 47// decimal (optional leading minus) immediately after the FIRST needle; -1 when absent or no digit follows. 48// -1 is therefore ambiguous with a literal -1 in the text: every field this loop reads is a count or a 49// duration, never negative, so the caller reads -1 as ABSENT. 50func sx_int_after(hay: *u8, n: i64, needle: *u8) -> i64 { 51 let p: i64 = sx_find(hay, n, needle) 52 if p < 0 { return 0 - 1 } 53 var i: i64 = p + sx_slen(needle) 54 var neg: i64 = 0 55 if i < n { if hay[i] == (SX_MINUS as u8) { neg = 1; i = i + 1 } } 56 var v: i64 = 0 57 var any: i64 = 0 58 var go: i64 = 1 59 while go == 1 { 60 if i >= n { go = 0 } else { 61 let c: i64 = hay[i] as i64 62 if c >= SX_D0 { if c <= SX_D9 { v = v * 10 + (c - SX_D0); any = 1; i = i + 1 } else { go = 0 } } else { go = 0 } 63 } 64 } 65 if any == 0 { return 0 - 1 } 66 if neg == 1 { return 0 - v } 67 return v 68} 69// the quoted string after key (key includes its colon, e.g. "\"sha256\":"), NUL-terminated into dst; returns its length 70func sx_json_str(hay: *u8, n: i64, key: *u8, dst: *u8, cap: i64) -> i64 { 71 dst[0] = 0 as u8 72 let p: i64 = sx_find(hay, n, key) 73 if p < 0 { return 0 } 74 var i: i64 = p + sx_slen(key) 75 var q: i64 = 0 - 1 76 var go: i64 = 1 77 while go == 1 { if i >= n { go = 0 } else { if hay[i] == (SX_QUOTE as u8) { q = i; go = 0 } else { i = i + 1 } } } 78 if q < 0 { return 0 } 79 var k: i64 = 0 80 var j: i64 = q + 1 81 go = 1 82 while go == 1 { if j >= n { go = 0 } else { if hay[j] == (SX_QUOTE as u8) { go = 0 } else { if k < cap - 1 { dst[k] = hay[j]; k = k + 1 } j = j + 1 } } } 83 dst[k] = 0 as u8 84 return k 85} 86// every line that STARTS with pfx, concatenated (each with its own LF) into dst[0..cap); returns the line count. 87// The referee A/B compares the two concatenations byte-for-byte: 17 queries, one line each, in file order. 88func sx_lines_with(hay: *u8, n: i64, pfx: *u8, dst: *u8, cap: i64, dlen: *i64) -> i64 { 89 var cnt: i64 = 0 90 var o: i64 = 0 91 var i: i64 = 0 92 while i < n { 93 var e: i64 = i 94 while e < n { if hay[e] == (SX_LF as u8) { e = n + 1 } else { e = e + 1 } } 95 var eol: i64 = e 96 if e == n + 1 { eol = 0 - 1; var f: i64 = i; while f < n { if hay[f] == (SX_LF as u8) { eol = f; f = n } else { f = f + 1 } } } 97 if eol < 0 { eol = n } 98 if sx_starts((hay as i64 + i) as *u8, pfx) == 1 { 99 var k: i64 = i 100 while k < eol { if o < cap - 2 { dst[o] = hay[k]; o = o + 1 } k = k + 1 } 101 dst[o] = SX_LF as u8; o = o + 1 102 cnt = cnt + 1 103 } 104 i = eol + 1 105 } 106 dst[o] = 0 as u8 107 dlen[0] = o 108 return cnt 109} 110// byte-for-byte equality of two buffers 111func sx_bytes_eq(a: *u8, an: i64, b: *u8, bn: i64) -> i64 { 112 if an != bn { return 0 } 113 var i: i64 = 0 114 while i < an { if a[i] != b[i] { return 0 } i = i + 1 } 115 return 1 116} 117// the FIRST line at which two line-buffers differ (1-based), 0 when identical 118func sx_first_diff_line(a: *u8, an: i64, b: *u8, bn: i64) -> i64 { 119 var i: i64 = 0 120 var line: i64 = 1 121 var n: i64 = an 122 if bn < n { n = bn } 123 while i < n { 124 if a[i] != b[i] { return line } 125 if a[i] == (SX_LF as u8) { line = line + 1 } 126 i = i + 1 127 } 128 if an != bn { return line } 129 return 0 130} 131// the phase timers out of one /api/search capture: out[0]=recv ms (the fetcher's first line), then from the 132// phase_us block prep, s1, l0dec, auth, div, total (microseconds) and cand. -1 = field absent. Returns 1 when the 133// phase_us block was found at all, else 0 (a 503, a warm-window refusal or a body without the field). 134func sx_phase(hay: *u8, n: i64, out: *i64) -> i64 { 135 var z: i64 = 0 136 while z < SX_PHASE_SLOTS { out[z] = 0 - 1; z = z + 1 } 137 out[0] = sx_int_after(hay, n, "recv=" as *u8) 138 let p: i64 = sx_find(hay, n, "\"phase_us\":{" as *u8) 139 if p < 0 { return 0 } 140 let h2: *u8 = (hay as i64 + p) as *u8 141 let n2: i64 = n - p 142 out[1] = sx_int_after(h2, n2, "\"prep\":" as *u8) 143 out[2] = sx_int_after(h2, n2, "\"s1\":" as *u8) 144 out[3] = sx_int_after(h2, n2, "\"l0dec\":" as *u8) 145 out[4] = sx_int_after(h2, n2, "\"auth\":" as *u8) 146 out[5] = sx_int_after(h2, n2, "\"div\":" as *u8) 147 out[6] = sx_int_after(h2, n2, "\"total\":" as *u8) 148 out[7] = sx_int_after(h2, n2, "\"cand\":" as *u8) 149 return 1 150} 151// the consumer bar (ms) of the journey named `id` in a uat_journeys.conf buffer (id|url|connect|latency_ms_max|must); 152// dflt when the row or its field is absent 153func sx_journey_bar(cf: *u8, n: i64, id: *u8, dflt: i64) -> i64 { 154 let key: *u8 = sys_mmap(128) 155 var ko: i64 = sx_cat(key, 0, id) 156 ko = sx_cat(key, ko, "|" as *u8) 157 let p: i64 = sx_find(cf, n, key) 158 sys_munmap(key, 128) 159 if p < 0 { return dflt } 160 var i: i64 = p 161 var pipes: i64 = 0 162 var go: i64 = 1 163 while go == 1 { 164 if i >= n { go = 0 } else { 165 if cf[i] == (SX_PIPE as u8) { pipes = pipes + 1; i = i + 1; if pipes == 3 { go = 0 } } else { i = i + 1 } 166 } 167 } 168 if pipes < 3 { return dflt } 169 // the digits start right after the third pipe (an empty needle would read as ABSENT -- caught by the gate's T5) 170 var v: i64 = 0 171 var any: i64 = 0 172 var g2: i64 = 1 173 while g2 == 1 { 174 if i >= n { g2 = 0 } else { 175 let c: i64 = cf[i] as i64 176 if c >= SX_D0 { if c <= SX_D9 { v = v * 10 + (c - SX_D0); any = 1; i = i + 1 } else { g2 = 0 } } else { g2 = 0 } 177 } 178 } 179 if any == 0 { return dflt } 180 return v 181} 182// space -> %20 (the only byte a conf query carries that a URL cannot); returns the new offset 183func sx_urlenc(d: *u8, o: i64, s: *u8) -> i64 { 184 var oo: i64 = o 185 var i: i64 = 0 186 while s[i] != (0 as u8) { 187 if s[i] == (SX_SPACE as u8) { d[oo] = 37 as u8; d[oo + 1] = 50 as u8; d[oo + 2] = SX_D0 as u8; oo = oo + 3 } else { d[oo] = s[i]; oo = oo + 1 } 188 i = i + 1 189 } 190 d[oo] = 0 as u8 191 return oo 192} 193// probe queries out of a conf buffer: one per line, '#' comments and blank lines skipped, each copied into 194// slots of `w` bytes at `dst` (NUL-terminated), at most `maxn`; returns the count 195func sx_probes(cf: *u8, n: i64, dst: *u8, w: i64, maxn: i64) -> i64 { 196 var cnt: i64 = 0 197 var i: i64 = 0 198 while i < n { 199 var e: i64 = i 200 var go: i64 = 1 201 while go == 1 { if e >= n { go = 0 } else { if cf[e] == (SX_LF as u8) { go = 0 } else { e = e + 1 } } } 202 if e > i { if cf[i] != (SX_HASH as u8) { if cnt < maxn { 203 let slot: *u8 = (dst as i64 + cnt * w) as *u8 204 var k: i64 = 0 205 var j: i64 = i 206 while j < e { if k < w - 1 { if cf[j] != (13 as u8) { slot[k] = cf[j]; k = k + 1 } } j = j + 1 } 207 slot[k] = 0 as u8 208 if k > 0 { cnt = cnt + 1 } 209 } } } 210 i = e + 1 211 } 212 return cnt 213} 214// HTML SERP WITNESS (2026-09-14). The JSON probes exercise only the /api/search branch of the daemon's router; the 215// HTML /search branch is a different code path (dad_is_search) and it BROKE through the buffered edge on 2026-09-14 216// while every JSON probe read GREEN. A page witness needs BOTH the status line and the marker the consumer journey 217// itself demands (uat_journeys.conf must_contain): a 200 without the marker is a shell without an answer, and a 218// marker under a refusal page is not an answer either. 1 only when both are present in the capture. 219func sx_html_ok(out: *u8, n: i64, marker: *u8) -> i64 { 220 if sx_find(out, n, "HTTP/1.1 200" as *u8) < 0 { return 0 } 221 if sx_find(out, n, marker) < 0 { return 0 } 222 return 1 223} 224// One column of one journey row (uat_journeys.conf: id|url|connect|latency_ms_max|must_contain), CR stripped and 225// NUL-terminated into out. Returns the bytes copied; 0 when no row carries that id or the column is empty. The id 226// must be followed by a pipe, so a shorter id never matches a longer id's prefix. 227const SX_JF_ID: i64 = 0 228const SX_JF_URL: i64 = 1 229const SX_JF_CONNECT: i64 = 2 230const SX_JF_BAR: i64 = 3 231const SX_JF_MUST: i64 = 4 232const SX_JF_CR: i64 = 13 233func sx_journey_field(cf: *u8, n: i64, id: *u8, col: i64, out: *u8, cap: i64) -> i64 { 234 let idn: i64 = sx_slen(id) 235 var i: i64 = 0 236 while i < n { 237 var e: i64 = i 238 var eol: i64 = 0 239 while eol == 0 { if e >= n { eol = 1 } else { if cf[e] == (SX_LF as u8) { eol = 1 } else { e = e + 1 } } } 240 var m: i64 = 1 241 if e - i < idn + 1 { m = 0 } else { 242 var k: i64 = 0 243 while k < idn { if cf[i + k] != id[k] { m = 0 } k = k + 1 } 244 if cf[i + idn] != (SX_PIPE as u8) { m = 0 } 245 } 246 if m == 1 { 247 var c: i64 = 0 248 var j: i64 = i 249 var o: i64 = 0 250 while j < e { 251 if cf[j] == (SX_PIPE as u8) { c = c + 1 } else { if c == col { if cf[j] != (SX_JF_CR as u8) { if o < cap - 1 { out[o] = cf[j]; o = o + 1 } } } } 252 j = j + 1 253 } 254 out[o] = 0 as u8 255 return o 256 } 257 i = e + 1 258 } 259 out[0] = 0 as u8 260 return 0 261} 262// AGENT DECISION (W2, 2026-09-14). The worker adopts on its own ONLY the class whose safety is mechanical: the 263// candidate differs from live, the ruler lost nothing, and the referee is byte-identical on every judged query -- 264// a pure speed change. A candidate identical to live is NOCHANGE; anything whose ranking moved, or that lost a 265// printable run, is HELD as a private candidate for the root (RACI: workers never adopt a ranking change). 266const SX_AGENT_NOCHANGE: i64 = 0 267const SX_AGENT_ADOPT: i64 = 1 268const SX_AGENT_HOLD: i64 = 2 269func sx_agent_decide(same_as_live: i64, referee_identical: i64, lost: i64) -> i64 { 270 if same_as_live == 1 { return SX_AGENT_NOCHANGE } 271 if referee_identical == 1 { if lost == 0 { return SX_AGENT_ADOPT } } 272 return SX_AGENT_HOLD 273}