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}