code wiki / _hdl_build / nx_pattern_emit15.nx

nx_pattern_emit15.nx source

↩ module page · 577 lines · 30754 B

1// nx_pattern_emit15.nx -- PATTERN EMITTER: PNG_INGEST (shape 19 -- the COMPOSE-shape). 2// ==================================================================================== 3// DECLARED-SCAFFOLD (operator-authorized R3, X-AUT-NCF-001 / spec 2026-06-13-keystone- 4// newcontrolflow-route-decision.md, which names "the 5-stage ingest compose" VERBATIM 5// as the gap no existing data-driven shape emits). This emitter is HAND-WRITTEN -- it is 6// the GENERIC compose primitive. It is DEBITED vs autonomy: its register row is tagged 7// author=tutor (cl_register_dual), counted in A2 auth_all but NEVER in auth_emit. 8// Tagging it author=emitter to inflate A2 would be the cheat and is REFUSED. 9// BOOTSTRAP-PROVISIONAL: it stays a scaffold until >=2 distinct downstream organs ride it 10// hands-off (X-AUT-NCF-BOOT). The first rider -- nx_store_ingest -- is authored BY 11// nx_auto_builder hands-off from a DATA spec (author=emitter), NOT hand-written. 12// 13// WHY THIS IS A NEW SHAPE, NOT A VARIATION: every existing emitter authors ONE leaf 14// function over ONE buffer (e.g. pe8 STRUCT_WALK authors <name>_locate(b,n,out) -- a 15// straight-line field walk; NO loop over repeating records, NO calls into other organs, 16// NO store side-effects, NO idempotency control flow). The ingest organ must (a) LOOP over 17// ALL PNG chunks, (b) CALL _pe_pngchunk/_pe_pngtext per chunk, (c) CALL canon_encode+cid_of, 18// (d) CALL ss_get (idempotency) then ss_begin/ss_add/ss_commit. That cross-organ composition 19// + per-chunk loop is precisely the new control flow the transducer cannot synthesize. 20// 21// WHAT IT AUTHORS (GENERIC, data-driven from the spec param vector): 22// <name>_ingest(buf: *u8, flen: i64, prefix: *u8) -> i64 23// Walks every PNG chunk in buf[8..flen) (skips the 8-byte signature), and for each 24// chunk whose FourCC is in the spec's CHUNK-TYPE set, reads its keyword/value via the 25// green _pe_pngtext_locate. If the keyword is one of the spec's GENREC keys, the (key, 26// value) pair joins the canonical record. The assembled vector is canon_encode'd, the 27// CID computed (cid_of), the record key built ("img:"+CID). ss_get probes for the key: 28// FOUND -> return 0 (idempotent skip, rule 10, no dup); ABSENT -> ss_begin/ss_add(kind 29// 1=put, key, canonbytes)/ss_commit -> return 1 (new record stored). 30// Return: 1 = stored a NEW record; 0 = idempotent skip (already present); -1 = no GENREC 31// fields found / malformed PNG (REFUSED, never a clipped store). 32// 33// DATA-DRIVEN (the spec is the config, rule 11): the GENREC key STRINGS to harvest, the 34// chunk FourCC(s) to walk, the record-key prefix, and the segid are all carried in the 35// param vector -- the scaffold is reusable for any image-metadata ingest, not a PNG one-off. 36// 37// THE KAT (emitter-computed, no-fake-green): the test builds a REAL multi-tEXt PNG in 38// memory (one tEXt chunk per GENREC key, each with the EMITTER-COMPUTED crc32), ingests it 39// to a temp store prefix, then asserts: (1) CID round-trip -- ss_get returns the stored 40// canonical bytes and re-hashing them yields the SAME CID the emitter computed off-line; 41// (2) idempotency -- a 2nd ingest returns 0 and the segment count is unchanged; (3) GENREC 42// fields present -- the canonical bytes contain each key. A tamper arm flips one source 43// keyword byte so a DIFFERENT key is harvested -> the CID changes -> assertion (1) RED. 44// The expected CID is computed HOST-SIDE here (p15_canon/p15_cid mirror nx_canon_cid), so 45// the KAT bakes a real CID, never a hand-typed one. 46// 47// LAWS: struct-free, integer-only, flat ifs, no &&/||, <=6 args per func. license_tier: ORIGINAL 48// ==================================================================================== 49import "nx_syscalls.nx" 50import "nx_sha256.nx" 51const K_MAGIC_8192: i64 = 8192 52const K_MAGIC_1229472850: i64 = 1229472850 53const K_MAGIC_1229278788: i64 = 1229278788 54 55func p15_w(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd, s, n); return 0 } 56func p15_wn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m; sys_write(fd,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 } 57 58// ---- the param vector layout (the spec is the config) ------------------------------- 59// p[0] = nkeys (number of GENREC keyword strings to harvest) 60// p[1] = chunk FourCC packed BE (the chunk type to walk; tEXt = 1950701684) 61// p[2] = segid to commit under (a fixed test segid; production callers pass their own) 62// p[3..] = nkeys keyword strings, each encoded as <klen> then klen bytes-as-ints 63// (so the data spec carries the keys as bytes -- no hand-written key literal in 64// the emitter; the scaffold is generic over the key set). 65func p15_spec_ok(p: *i64, np: i64) -> i64 { 66 if np < 3 { return 0 } 67 let nk: i64 = p[0] 68 if nk < 1 { return 0 } 69 if nk > 16 { return 0 } 70 // each key: 1 (the length cell) + klen byte cells must fit 71 var idx: i64 = 3 72 var i: i64 = 0 73 while i < nk { 74 if idx >= np { return 0 } 75 let kl: i64 = p[idx] 76 if kl < 1 { return 0 } 77 if kl > 64 { return 0 } 78 idx = idx + 1 + kl 79 i = i + 1 80 } 81 if idx != np { return 0 } 82 return 1 83} 84 85// emit a NishiLang string literal byte-buffer for one key: writes `<var>[j] = <byte> as u8` 86// for each byte of the key whose cells start at p[base+1], length p[base]; null-terminates. 87func p15_emit_keybytes(fd: i64, p: *i64, base: i64, varname: *u8) -> i64 { 88 let kl: i64 = p[base] 89 var j: i64 = 0 90 while j < kl { 91 p15_w(fd, " "); p15_w(fd, varname); p15_w(fd, "["); p15_wn(fd, j) 92 p15_w(fd, "] = "); p15_wn(fd, p[base + 1 + j]); p15_w(fd, " as u8\n") 93 j = j + 1 94 } 95 p15_w(fd, " "); p15_w(fd, varname); p15_w(fd, "["); p15_wn(fd, kl) 96 p15_w(fd, "] = 0 as u8\n") 97 return kl 98} 99 100// ============ the AUTHORED MODULE: <name>_ingest -- the 5-stage compose ============ 101func pe15_emit_ingest(fd: i64, name: *u8, p: *i64, np: i64) -> i64 { 102 let nk: i64 = p[0] 103 let fourcc: i64 = p[1] 104 let segid: i64 = p[2] 105 p15_w(fd, "// AUTHORED BY THE NISHI BUILDER (pattern: PNG_INGEST compose) -- 5-stage ingest, no Claude logic\n") 106 p15_w(fd, "import \"_pe_pngchunk.nx\"\n") 107 p15_w(fd, "import \"_pe_pngtext.nx\"\n") 108 p15_w(fd, "import \"nx_canon_cid.nx\"\n") 109 p15_w(fd, "import \"nx_seg_store.nx\"\n") 110 p15_w(fd, "import \"nx_syscalls.nx\"\n") 111 // byte-compare helper: does buf[off..off+kl) equal key[0..kl) ? 112 p15_w(fd, "func "); p15_w(fd, name); p15_w(fd, "_keq(buf: *u8, off: i64, kl: i64, key: *u8) -> i64 {\n") 113 p15_w(fd, " var i: i64 = 0\n") 114 p15_w(fd, " while i < kl { if (buf[off + i] & 0xff) != (key[i] & 0xff) { return 0 } i = i + 1 }\n") 115 p15_w(fd, " if (key[kl] & 0xff) != 0 { return 0 }\n") 116 p15_w(fd, " return 1\n}\n") 117 // the ingest function 118 p15_w(fd, "func "); p15_w(fd, name); p15_w(fd, "_ingest(buf: *u8, flen: i64, prefix: *u8) -> i64 {\n") 119 // STAGE 0: WALK every chunk. cb/cn = the chunk stream after the 8-byte PNG signature. 120 p15_w(fd, " if flen < 8 { return 0 - 1 }\n") 121 p15_w(fd, " let cb: *u8 = (buf as i64 + 8) as *u8\n") 122 p15_w(fd, " let cn: i64 = flen - 8\n") 123 // GENREC accumulator: pointer arrays of keys/vals (null-terminated bytes), key count gn. 124 p15_w(fd, " let gkeys: *i64 = sys_mmap(8 * 64) as *i64\n") 125 p15_w(fd, " let gvals: *i64 = sys_mmap(8 * 64) as *i64\n") 126 p15_w(fd, " var gn: i64 = 0\n") 127 // the GENREC key table (DATA from the spec): each key as a null-terminated byte buffer. 128 var ki: i64 = 0 129 var base: i64 = 3 130 while ki < nk { 131 let kl: i64 = p[base] 132 p15_w(fd, " let key"); p15_wn(fd, ki); p15_w(fd, ": *u8 = sys_mmap("); p15_wn(fd, kl + 1); p15_w(fd, ")\n") 133 let kvar: *u8 = sys_mmap(32) 134 var ti: i64 = 0 135 ti = ti + p15_cat(kvar, 0, "key") 136 ti = ti + p15_catn(kvar, ti, ki) 137 kvar[ti] = 0 as u8 138 p15_emit_keybytes(fd, p, base, kvar) 139 base = base + 1 + kl 140 ki = ki + 1 141 } 142 // the per-chunk walk: off advances chunk-by-chunk like _pe_pngchunk_count. 143 p15_w(fd, " let out: *i64 = sys_mmap(64) as *i64\n") 144 p15_w(fd, " var off: i64 = 0\n") 145 p15_w(fd, " while off < cn {\n") 146 p15_w(fd, " if off + 8 > cn { return 0 - 1 }\n") 147 p15_w(fd, " let clen: i64 = _pe_pngchunk_rdbe(cb, off + 0, 4)\n") 148 p15_w(fd, " let cty: i64 = _pe_pngchunk_rdbe(cb, off + 4, 4)\n") 149 p15_w(fd, " if off + 12 + clen > cn { return 0 - 1 }\n") 150 p15_w(fd, " if cty == "); p15_wn(fd, fourcc); p15_w(fd, " {\n") 151 // STAGE 1: SPLIT the chunk body -> keyword(off,len)+value(off,len) via the green organ. 152 p15_w(fd, " let body: *u8 = (cb as i64 + off + 8) as *u8\n") 153 p15_w(fd, " if _pe_pngtext_locate(body, clen, out) == 0 {\n") 154 p15_w(fd, " let kwoff: i64 = out[2]\n") 155 p15_w(fd, " let kwlen: i64 = out[3]\n") 156 p15_w(fd, " let voff: i64 = out[0]\n") 157 p15_w(fd, " let vlen: i64 = out[1]\n") 158 // does this chunk's keyword match any GENREC key? (data-driven match) 159 ki = 0 160 while ki < nk { 161 let kl2: i64 = p[3 + p15_keybase_off(p, ki)] 162 p15_w(fd, " if "); p15_w(fd, name); p15_w(fd, "_keq(body, kwoff, kwlen, key"); p15_wn(fd, ki); p15_w(fd, ") == 1 {\n") 163 // copy keyword bytes into a fresh null-terminated buffer (the GENREC key) 164 p15_w(fd, " let kk: *u8 = sys_mmap(80)\n") 165 p15_w(fd, " var ci: i64 = 0\n") 166 p15_w(fd, " while ci < kwlen { kk[ci] = body[kwoff + ci]; ci = ci + 1 }\n") 167 p15_w(fd, " kk[kwlen] = 0 as u8\n") 168 // copy value bytes into a fresh null-terminated buffer (the GENREC value) 169 p15_w(fd, " let vv: *u8 = sys_mmap(512)\n") 170 p15_w(fd, " ci = 0\n") 171 p15_w(fd, " while ci < vlen { vv[ci] = body[voff + ci]; ci = ci + 1 }\n") 172 p15_w(fd, " vv[vlen] = 0 as u8\n") 173 p15_w(fd, " gkeys[gn] = kk as i64\n") 174 p15_w(fd, " gvals[gn] = vv as i64\n") 175 p15_w(fd, " gn = gn + 1\n") 176 p15_w(fd, " }\n") 177 ki = ki + 1 178 } 179 p15_w(fd, " }\n") 180 p15_w(fd, " }\n") 181 p15_w(fd, " off = off + 12 + clen\n") 182 p15_w(fd, " }\n") 183 p15_w(fd, " if gn < 1 { return 0 - 1 }\n") 184 // STAGE 2: CANONICALIZE -> CID. canon_encode sorts keys -> deterministic bytes -> sha256. 185 p15_w(fd, " let canon: *u8 = sys_mmap(8192)\n") 186 p15_w(fd, " let clen2: i64 = canon_encode(gkeys, gvals, gn, canon)\n") 187 p15_w(fd, " let cid: *u8 = sys_mmap(80)\n") 188 p15_w(fd, " cid_of(canon, clen2, cid)\n") 189 // STAGE 3: record key = "img:" + CID 190 p15_w(fd, " let rk: *u8 = sys_mmap(96)\n") 191 p15_w(fd, " rk[0] = 105 as u8\n") // i 192 p15_w(fd, " rk[1] = 109 as u8\n") // m 193 p15_w(fd, " rk[2] = 103 as u8\n") // g 194 p15_w(fd, " rk[3] = 58 as u8\n") // : 195 p15_w(fd, " var ri: i64 = 0\n") 196 p15_w(fd, " while ri < 69 { rk[4 + ri] = cid[ri]; ri = ri + 1 }\n") 197 p15_w(fd, " rk[73] = 0 as u8\n") 198 // STAGE 4: IDEMPOTENCY -- ss_get probes; FOUND (1) -> skip; else store (rule 10). 199 p15_w(fd, " let pp: *i64 = sys_mmap(16) as *i64\n") 200 p15_w(fd, " let ll: *i64 = sys_mmap(16) as *i64\n") 201 p15_w(fd, " if ss_get(prefix, rk, pp, ll) == 1 { return 0 }\n") 202 // STAGE 5: STORE (versioned, additive) -- ss_begin/ss_add(kind 1=put)/ss_commit (atomic). 203 // The segid ADVANCES per commit (base + current manifest count): each NEW record lands in 204 // a FRESH segment file so distinct images never overwrite each other on disk (the store's 205 // additive law). A baked constant segid would make the 2nd distinct image clobber the 1st. 206 p15_w(fd, " let segs: *i64 = sys_mmap(8 * 260) as *i64\n") 207 p15_w(fd, " let nseg: i64 = ss_manifest(prefix, segs)\n") 208 p15_w(fd, " var segid: i64 = "); p15_wn(fd, segid); p15_w(fd, "\n") 209 p15_w(fd, " if nseg >= 0 { segid = "); p15_wn(fd, segid); p15_w(fd, " + nseg }\n") 210 p15_w(fd, " let wr: *i64 = ss_begin()\n") 211 p15_w(fd, " ss_add(wr, 1, rk, canon, clen2)\n") 212 p15_w(fd, " let rc: i64 = ss_commit(prefix, wr, segid)\n") 213 p15_w(fd, " if rc != 0 { return 0 - 1 }\n") 214 p15_w(fd, " return 1\n}\n") 215 return 1 216} 217 218// ---- spec-side helpers reused by the emitter (host-side, NOT authored) -------------- 219func p15_cat(dst: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[o + i] = s[i]; i = i + 1 } return i } 220func p15_catn(dst: *u8, o: i64, v: i64) -> i64 { var m: i64 = v; let t: *u8 = sys_mmap(28); var k: i64 = 0; if m == 0 { t[0] = 48; k = 1 } while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } var i: i64 = 0; while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 } return k } 221 222// where does the i-th GENREC key's <klen> cell sit, RELATIVE to p[3]? (offset into p from base 3) 223func p15_keybase_off(p: *i64, target: i64) -> i64 { 224 var idx: i64 = 0 // relative to p[3] 225 var i: i64 = 0 226 while i < target { 227 let kl: i64 = p[3 + idx] 228 idx = idx + 1 + kl 229 i = i + 1 230 } 231 return idx 232} 233 234// ============ HOST-SIDE CANON+CID (mirrors nx_canon_cid; computes the KAT's expected CID) ============ 235// byte-lex compare of two byte ranges by length (shorter strict-prefix first), used to 236// sort the GENREC keys the same way canon_encode does -- so the host CID == the runtime CID. 237func p15_kcmp(a: *u8, al: i64, b: *u8, bl: i64) -> i64 { 238 var n: i64 = al 239 if bl < n { n = bl } 240 var i: i64 = 0 241 while i < n { 242 let ca: i64 = a[i] & 0xff 243 let cb: i64 = b[i] & 0xff 244 if ca != cb { return ca - cb } 245 i = i + 1 246 } 247 return al - bl 248} 249func p15_w32(p: *u8, off: i64, v: i64) -> i64 { 250 p[off] = ((v >> 24) & 0xff) as u8 251 p[off + 1] = ((v >> 16) & 0xff) as u8 252 p[off + 2] = ((v >> 8) & 0xff) as u8 253 p[off + 3] = (v & 0xff) as u8 254 return off + 4 255} 256// canon_encode mirror over (keyptr,keylen,valptr,vallen) arrays; returns canonical length. 257func p15_canon(kp: *i64, kl: *i64, vp: *i64, vl: *i64, n: i64, out: *u8) -> i64 { 258 let idx: *i64 = sys_mmap(8 * (n + 2)) as *i64 259 var i: i64 = 0 260 while i < n { idx[i] = i; i = i + 1 } 261 i = 1 262 while i < n { 263 let cur: i64 = idx[i] 264 var j: i64 = i - 1 265 var go: i64 = 1 266 while go == 1 { 267 if j < 0 { go = 0 } 268 if go == 1 { 269 if p15_kcmp(kp[idx[j]] as *u8, kl[idx[j]], kp[cur] as *u8, kl[cur]) > 0 { 270 idx[j + 1] = idx[j] 271 j = j - 1 272 } else { go = 0 } 273 } 274 } 275 idx[j + 1] = cur 276 i = i + 1 277 } 278 out[0] = 78 as u8; out[1] = 88 as u8; out[2] = 82 as u8; out[3] = 49 as u8 279 var o: i64 = 4 280 o = p15_w32(out, o, n) 281 i = 0 282 while i < n { 283 let kk: *u8 = kp[idx[i]] as *u8 284 let vv: *u8 = vp[idx[i]] as *u8 285 let kln: i64 = kl[idx[i]] 286 let vln: i64 = vl[idx[i]] 287 o = p15_w32(out, o, kln) 288 var t: i64 = 0 289 while t < kln { out[o] = kk[t]; o = o + 1; t = t + 1 } 290 o = p15_w32(out, o, vln) 291 t = 0 292 while t < vln { out[o] = vv[t]; o = o + 1; t = t + 1 } 293 i = i + 1 294 } 295 return o 296} 297// cid_of mirror: "nxc1-" + 64 hex of sha256(bytes), null-terminated. 298func p15_cid(bytes: *u8, n: i64, cid: *u8) -> i64 { 299 let dg: *u8 = sys_mmap(40) 300 sha256_digest(bytes, n, dg) 301 cid[0] = 110 as u8; cid[1] = 120 as u8; cid[2] = 99 as u8; cid[3] = 49 as u8; cid[4] = 45 as u8 302 var i: i64 = 0 303 while i < 32 { 304 let b: i64 = dg[i] & 0xff 305 let hi: i64 = (b >> 4) & 15 306 let lo: i64 = b & 15 307 var c1: i64 = 48 + hi 308 if hi > 9 { c1 = 87 + hi } 309 var c2: i64 = 48 + lo 310 if lo > 9 { c2 = 87 + lo } 311 cid[5 + i * 2] = c1 as u8 312 cid[6 + i * 2] = c2 as u8 313 i = i + 1 314 } 315 cid[69] = 0 as u8 316 return 69 317} 318 319// host-side crc32 (poly 0xEDB88320 reflected) -- bakes the KAT chunk trailers, never hand-typed. 320func p15_crc32(img: *u8, s: i64, e: i64) -> i64 { 321 var cc: i64 = 0xffffffff 322 var ci: i64 = s 323 while ci < e { 324 cc = cc ^ (img[ci] & 0xff) 325 var cb: i64 = 0 326 while cb < 8 { 327 let cm: i64 = 0 - (cc & 1) 328 cc = (cc >> 1) ^ (0xedb88320 & cm) 329 cb = cb + 1 330 } 331 ci = ci + 1 332 } 333 return (cc ^ 0xffffffff) & 0xffffffff 334} 335 336// ============ the KAT BUILDER: a REAL multi-tEXt PNG with EMITTER-COMPUTED chunks ============ 337// builds, in `img`, a valid PNG: 8-byte sig + IHDR(13) + one tEXt chunk per GENREC key 338// ("<key>\0<val>") with a correct crc32 + IEND. The values are FIXED host-side test strings 339// (one per key index) -- deterministic so the CID is reproducible. Returns the total length; 340// fills kp/kl/vp/vl/(n=nk) with the (key,value) the runtime SHOULD harvest, so the host CID 341// is computed over the SAME vector the authored ingest assembles. 342func p15_chunk_emit(img: *u8, off: i64, fourcc: i64, key: *u8, klen: i64, val: *u8, vlen: i64) -> i64 { 343 // length = klen + 1 (NUL) + vlen ; crc over [type..body) 344 let blen: i64 = klen + 1 + vlen 345 img[off] = ((blen >> 24) & 0xff) as u8 346 img[off + 1] = ((blen >> 16) & 0xff) as u8 347 img[off + 2] = ((blen >> 8) & 0xff) as u8 348 img[off + 3] = (blen & 0xff) as u8 349 img[off + 4] = ((fourcc >> 24) & 0xff) as u8 350 img[off + 5] = ((fourcc >> 16) & 0xff) as u8 351 img[off + 6] = ((fourcc >> 8) & 0xff) as u8 352 img[off + 7] = (fourcc & 0xff) as u8 353 var t: i64 = 0 354 while t < klen { img[off + 8 + t] = key[t]; t = t + 1 } 355 img[off + 8 + klen] = 0 as u8 356 t = 0 357 while t < vlen { img[off + 8 + klen + 1 + t] = val[t]; t = t + 1 } 358 // crc32 over type+body = [off+4 .. off+8+blen) 359 let cv: i64 = p15_crc32(img, off + 4, off + 8 + blen) 360 let cpos: i64 = off + 8 + blen 361 img[cpos] = ((cv >> 24) & 0xff) as u8 362 img[cpos + 1] = ((cv >> 16) & 0xff) as u8 363 img[cpos + 2] = ((cv >> 8) & 0xff) as u8 364 img[cpos + 3] = (cv & 0xff) as u8 365 return cpos + 4 366} 367 368// the fixed per-key test VALUE bytes (deterministic): "v" + key-index digit, length 2. 369func p15_testval(ki: i64, v: *u8) -> i64 { 370 v[0] = 118 as u8 // 'v' 371 v[1] = (48 + (ki % 10)) as u8 372 v[2] = 0 as u8 373 return 2 374} 375 376func pe15_emit_ingest_test(fd: i64, name: *u8, p: *i64, np: i64) -> i64 { 377 let nk: i64 = p[0] 378 let fourcc: i64 = p[1] 379 // -- assemble the (key,value) vector the runtime SHOULD harvest, host-side -- 380 let kp: *i64 = sys_mmap(8 * 64) as *i64 381 let kl: *i64 = sys_mmap(8 * 64) as *i64 382 let vp: *i64 = sys_mmap(8 * 64) as *i64 383 let vl: *i64 = sys_mmap(8 * 64) as *i64 384 // build the PNG image 385 let img: *u8 = sys_mmap(K_MAGIC_8192) 386 // 8-byte PNG signature 387 img[0] = 0x89 as u8; img[1] = 0x50 as u8; img[2] = 0x4e as u8; img[3] = 0x47 as u8 388 img[4] = 0x0d as u8; img[5] = 0x0a as u8; img[6] = 0x1a as u8; img[7] = 0x0a as u8 389 // IHDR (13-byte body, minimal 1x1) -- crc emitter-computed 390 var o: i64 = 8 391 let ihdr: *u8 = sys_mmap(16) 392 ihdr[0] = 0 as u8; ihdr[1] = 0 as u8; ihdr[2] = 0 as u8; ihdr[3] = 1 as u8 393 ihdr[4] = 0 as u8; ihdr[5] = 0 as u8; ihdr[6] = 0 as u8; ihdr[7] = 1 as u8 394 ihdr[8] = 8 as u8; ihdr[9] = 6 as u8; ihdr[10] = 0 as u8; ihdr[11] = 0 as u8; ihdr[12] = 0 as u8 395 let ihdr_fourcc: i64 = K_MAGIC_1229472850 // "IHDR" 396 o = p15_chunk_emit(img, o, ihdr_fourcc, "" as *u8, 0, ihdr, 13) 397 // one tEXt chunk per GENREC key 398 var ki: i64 = 0 399 var base: i64 = 3 400 while ki < nk { 401 let klen: i64 = p[base] 402 let kbuf: *u8 = sys_mmap(80) 403 var j: i64 = 0 404 while j < klen { kbuf[j] = (p[base + 1 + j] & 0xff) as u8; j = j + 1 } 405 kbuf[klen] = 0 as u8 406 let vbuf: *u8 = sys_mmap(8) 407 let vlen: i64 = p15_testval(ki, vbuf) 408 o = p15_chunk_emit(img, o, fourcc, kbuf, klen, vbuf, vlen) 409 kp[ki] = kbuf as i64; kl[ki] = klen 410 vp[ki] = vbuf as i64; vl[ki] = vlen 411 base = base + 1 + klen 412 ki = ki + 1 413 } 414 // IEND (0-length) 415 let iend_fourcc: i64 = K_MAGIC_1229278788 // "IEND" 416 o = p15_chunk_emit(img, o, iend_fourcc, "" as *u8, 0, "" as *u8, 0) 417 let total: i64 = o 418 // -- compute the EXPECTED CID host-side over the harvested vector -- 419 let canon: *u8 = sys_mmap(K_MAGIC_8192) 420 let clen: i64 = p15_canon(kp, kl, vp, vl, nk, canon) 421 let cid: *u8 = sys_mmap(80) 422 p15_cid(canon, clen, cid) 423 // ---- emit the self-contained KAT ---- 424 p15_w(fd, "// AUTHORED BY THE NISHI BUILDER (pattern: PNG_INGEST test) -- multi-tEXt PNG + CID/idempotency KATs computed at emit time\n") 425 p15_w(fd, "import \""); p15_w(fd, name); p15_w(fd, ".nx\"\n") 426 p15_w(fd, "import \"nx_canon_cid.nx\"\n") 427 p15_w(fd, "import \"nx_seg_store.nx\"\n") 428 p15_w(fd, "import \"nx_syscalls.nx\"\n") 429 p15_w(fd, "func gw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }\n") 430 p15_w(fd, "func main() -> i64 {\n") 431 p15_w(fd, " let b: *u8 = sys_mmap(8192)\n") 432 var i: i64 = 0 433 while i < total { 434 p15_w(fd, " b["); p15_wn(fd, i); p15_w(fd, "] = "); p15_wn(fd, img[i] & 0xff); p15_w(fd, " as u8\n") 435 i = i + 1 436 } 437 // the temp store prefix (fresh per run via a PID-stamped name would need getpid; a fixed 438 // unique prefix under /tmp suffices -- the KAT cleans by ingesting into an empty namespace 439 // and the idempotency check is RELATIVE to the same prefix within this run). 440 p15_w(fd, " let prefix: *u8 = \"/tmp/galxkat-\" as *u8\n") 441 // clean any stale manifest from a prior run so segment-count assertions are deterministic 442 p15_w(fd, " let mfn: *u8 = sys_mmap(64)\n") 443 p15_w(fd, " var mo: i64 = 0\n") 444 p15_w(fd, " while (prefix[mo] & 0xff) != 0 { mfn[mo] = prefix[mo]; mo = mo + 1 }\n") 445 p15_w(fd, " let suf: *u8 = \"manifest.txt\" as *u8\n") 446 p15_w(fd, " var so: i64 = 0\n") 447 p15_w(fd, " while (suf[so] & 0xff) != 0 { mfn[mo + so] = suf[so]; so = so + 1 }\n") 448 p15_w(fd, " mfn[mo + so] = 0 as u8\n") 449 // truncate the manifest to empty (O_TRUNC) so readers see an EMPTY namespace this run: 450 // the store only reads manifest-listed segments, so stale seg-*.docs become invisible. 451 p15_w(fd, " let cfd: i64 = sys_openat_wr(mfn, 0x1a4)\n") 452 p15_w(fd, " if cfd >= 0 { sys_close(cfd) }\n") 453 // STAGE-PROOF (1+3): first ingest -> stores -> returns 1 454 p15_w(fd, " let r1: i64 = "); p15_w(fd, name); p15_w(fd, "_ingest(b, "); p15_wn(fd, total); p15_w(fd, ", prefix)\n") 455 p15_w(fd, " if r1 != 1 { gw(\"KAT FAIL: first ingest did not store (r1=\" as *u8); sys_exit(1); return 1 }\n") 456 // CID round-trip: read back the stored canonical bytes, re-hash -> must equal the expected CID 457 p15_w(fd, " let rk: *u8 = sys_mmap(96)\n") 458 p15_w(fd, " rk[0]=105 as u8; rk[1]=109 as u8; rk[2]=103 as u8; rk[3]=58 as u8\n") 459 // bake the expected CID bytes into rk[4..] 460 i = 0 461 while i < 69 { 462 p15_w(fd, " rk["); p15_wn(fd, 4 + i); p15_w(fd, "] = "); p15_wn(fd, cid[i] & 0xff); p15_w(fd, " as u8\n") 463 i = i + 1 464 } 465 p15_w(fd, " rk[73] = 0 as u8\n") 466 p15_w(fd, " let pp: *i64 = sys_mmap(16) as *i64\n") 467 p15_w(fd, " let ll: *i64 = sys_mmap(16) as *i64\n") 468 p15_w(fd, " if ss_get(prefix, rk, pp, ll) != 1 { gw(\"KAT FAIL: stored record not found under expected CID key\" as *u8); sys_exit(1); return 1 }\n") 469 // re-hash the readback bytes -> a fresh CID; assert it equals the expected (byte-exact round-trip) 470 p15_w(fd, " let rb: *u8 = pp[0] as *u8\n") 471 p15_w(fd, " let rlen: i64 = ll[0]\n") 472 p15_w(fd, " let cid2: *u8 = sys_mmap(80)\n") 473 p15_w(fd, " cid_of(rb, rlen, cid2)\n") 474 p15_w(fd, " var ci: i64 = 0\n") 475 p15_w(fd, " while ci < 69 { if (cid2[ci] & 0xff) != (rk[4 + ci] & 0xff) { gw(\"KAT FAIL: CID round-trip mismatch\" as *u8); sys_exit(1); return 1 } ci = ci + 1 }\n") 476 // GENREC fields present: assert the canonical readback contains each key's bytes 477 ki = 0 478 base = 3 479 while ki < nk { 480 let klen: i64 = p[base] 481 p15_w(fd, " let gk"); p15_wn(fd, ki); p15_w(fd, ": *u8 = sys_mmap("); p15_wn(fd, klen + 1); p15_w(fd, ")\n") 482 let kvar: *u8 = sys_mmap(32) 483 var ti: i64 = 0 484 ti = p15_cat(kvar, 0, "gk") 485 ti = ti + p15_catn(kvar, ti, ki) 486 kvar[ti] = 0 as u8 487 p15_emit_keybytes(fd, p, base, kvar) 488 p15_w(fd, " if "); p15_w(fd, name); p15_w(fd, "_kat_contains(rb, rlen, gk"); p15_wn(fd, ki); p15_w(fd, ", "); p15_wn(fd, klen); p15_w(fd, ") != 1 { gw(\"KAT FAIL: GENREC key missing from canonical record\" as *u8); sys_exit(1); return 1 }\n") 489 base = base + 1 + klen 490 ki = ki + 1 491 } 492 // STAGE-PROOF (2): idempotency -- a 2nd ingest of the SAME bytes returns 0 (no dup) 493 p15_w(fd, " let r2: i64 = "); p15_w(fd, name); p15_w(fd, "_ingest(b, "); p15_wn(fd, total); p15_w(fd, ", prefix)\n") 494 p15_w(fd, " if r2 != 0 { gw(\"KAT FAIL: re-ingest produced a duplicate (r2 != 0)\" as *u8); sys_exit(1); return 1 }\n") 495 // NO-FAKE-GREEN TAMPER: flip one source keyword byte so a DIFFERENT key is harvested -> 496 // the CID changes -> ss_get on the original CID still found (idempotent), but the NEW ingest 497 // must STORE a new record (return 1), proving the CID is content-derived (not a constant). 498 // Find the first byte of the FIRST tEXt keyword in b and flip it. 499 // (the first tEXt chunk body keyword starts right after sig+IHDR+8 header bytes) 500 let first_text_kwpos: i64 = p15_first_kw_pos(img, total, fourcc) 501 p15_w(fd, " let tb: *u8 = sys_mmap(8192)\n") 502 p15_w(fd, " var tci: i64 = 0\n") 503 p15_w(fd, " while tci < "); p15_wn(fd, total); p15_w(fd, " { tb[tci] = b[tci]; tci = tci + 1 }\n") 504 // flip the keyword byte AND fix the chunk crc so the chunk still parses (a valid PNG with a 505 // changed keyword) -- this proves the CID tracks CONTENT, not that crc rejects it. 506 p15_w(fd, " tb["); p15_wn(fd, first_text_kwpos); p15_w(fd, "] = (((tb["); p15_wn(fd, first_text_kwpos); p15_w(fd, "] & 0xff) ^ 0x20) & 0xff) as u8\n") 507 // recompute that chunk's crc in-buffer so it remains a valid framed chunk 508 let tchunk = p15_chunk_of_pos(img, total, first_text_kwpos) 509 p15_w(fd, " "); p15_w(fd, name); p15_w(fd, "_kat_fixcrc(tb, "); p15_wn(fd, tchunk); p15_w(fd, ")\n") 510 p15_w(fd, " let r3: i64 = "); p15_w(fd, name); p15_w(fd, "_ingest(tb, "); p15_wn(fd, total); p15_w(fd, ", prefix)\n") 511 p15_w(fd, " if r3 != 1 { gw(\"KAT FAIL: tamper arm -- changed-content image did not yield a NEW CID record (r3 != 1)\" as *u8); sys_exit(1); return 1 }\n") 512 p15_w(fd, " gw(\"INGEST-KAT GREEN -- CID round-trip byte-exact + idempotent (0 dup) + GENREC fields + tamper(new-content->new-CID) fired\\n\" as *u8)\n") 513 p15_w(fd, " sys_exit(0)\n return 0\n}\n") 514 // helper: does buf[0..n) contain the kl bytes of key contiguously? 515 p15_w(fd, "func "); p15_w(fd, name); p15_w(fd, "_kat_contains(buf: *u8, n: i64, key: *u8, kl: i64) -> i64 {\n") 516 p15_w(fd, " var i: i64 = 0\n") 517 p15_w(fd, " while i + kl <= n {\n") 518 p15_w(fd, " var j: i64 = 0\n") 519 p15_w(fd, " var ok: i64 = 1\n") 520 p15_w(fd, " while j < kl { if (buf[i + j] & 0xff) != (key[j] & 0xff) { ok = 0; j = kl } else { j = j + 1 } }\n") 521 p15_w(fd, " if ok == 1 { return 1 }\n") 522 p15_w(fd, " i = i + 1\n") 523 p15_w(fd, " }\n") 524 p15_w(fd, " return 0\n}\n") 525 // helper: recompute the crc32 trailer of the chunk whose 4-byte length field starts at coff 526 p15_w(fd, "func "); p15_w(fd, name); p15_w(fd, "_kat_fixcrc(buf: *u8, coff: i64) -> i64 {\n") 527 p15_w(fd, " let blen: i64 = (((buf[coff] & 0xff) << 24) | ((buf[coff+1] & 0xff) << 16)) | (((buf[coff+2] & 0xff) << 8) | (buf[coff+3] & 0xff))\n") 528 p15_w(fd, " var cc: i64 = 0xffffffff\n") 529 p15_w(fd, " var ci: i64 = coff + 4\n") 530 p15_w(fd, " let ce: i64 = coff + 8 + blen\n") 531 p15_w(fd, " while ci < ce {\n") 532 p15_w(fd, " cc = cc ^ (buf[ci] & 0xff)\n") 533 p15_w(fd, " var cb: i64 = 0\n") 534 p15_w(fd, " while cb < 8 { let cm: i64 = 0 - (cc & 1); cc = (cc >> 1) ^ (0xedb88320 & cm); cb = cb + 1 }\n") 535 p15_w(fd, " ci = ci + 1\n") 536 p15_w(fd, " }\n") 537 p15_w(fd, " let cv: i64 = (cc ^ 0xffffffff) & 0xffffffff\n") 538 p15_w(fd, " buf[ce] = ((cv >> 24) & 0xff) as u8\n") 539 p15_w(fd, " buf[ce + 1] = ((cv >> 16) & 0xff) as u8\n") 540 p15_w(fd, " buf[ce + 2] = ((cv >> 8) & 0xff) as u8\n") 541 p15_w(fd, " buf[ce + 3] = (cv & 0xff) as u8\n") 542 p15_w(fd, " return 0\n}\n") 543 return 1 544} 545 546// host-side: the absolute byte offset of the FIRST tEXt keyword's first byte in the PNG. 547func p15_first_kw_pos(img: *u8, total: i64, fourcc: i64) -> i64 { 548 var off: i64 = 8 // skip signature 549 while off + 8 <= total { 550 let blen: i64 = (((img[off] & 0xff) << 24) | ((img[off+1] & 0xff) << 16)) | (((img[off+2] & 0xff) << 8) | (img[off+3] & 0xff)) 551 let ty: i64 = (((img[off+4] & 0xff) << 24) | ((img[off+5] & 0xff) << 16)) | (((img[off+6] & 0xff) << 8) | (img[off+7] & 0xff)) 552 if ty == fourcc { return off + 8 } // body (keyword) starts here 553 off = off + 12 + blen 554 } 555 return 0 - 1 556} 557// host-side: the chunk-length-field offset of the chunk that CONTAINS absolute position `pos`. 558func p15_chunk_of_pos(img: *u8, total: i64, pos: i64) -> i64 { 559 var off: i64 = 8 560 while off + 8 <= total { 561 let blen: i64 = (((img[off] & 0xff) << 24) | ((img[off+1] & 0xff) << 16)) | (((img[off+2] & 0xff) << 8) | (img[off+3] & 0xff)) 562 let cend: i64 = off + 12 + blen 563 if pos >= off { if pos < cend { return off } } 564 off = cend 565 } 566 return 0 - 1 567} 568 569// ---- the AUTHOR wrapper (mirrors pe8_author_struct_walk2) ---- 570func pe15_author_png_ingest(name: *u8, modpath: *u8, testpath: *u8, p: *i64, np: i64) -> i64 { 571 if p15_spec_ok(p, np) != 1 { return 0 } 572 let mf: i64 = sys_openat_wr(modpath, 0x1a4); if mf < 0 { return 0 } 573 pe15_emit_ingest(mf, name, p, np); sys_close(mf) 574 let tf: i64 = sys_openat_wr(testpath, 0x1a4); if tf < 0 { return 0 } 575 pe15_emit_ingest_test(tf, name, p, np); sys_close(tf) 576 return 1 577}