code wiki / _hdl_build / nx_store_ingest.nx

nx_store_ingest.nx source

↩ module page · 321 lines · 14132 B

1// AUTHORED BY THE NISHI BUILDER (pattern: PNG_INGEST compose) -- 5-stage ingest, no Claude logic 2import "_pe_pngchunk.nx" 3import "_pe_pngtext.nx" 4import "nx_canon_cid.nx" 5import "nx_seg_store.nx" 6import "nx_syscalls.nx" 7const K_MAGIC_4096: i64 = 4096 8const K_MAGIC_4095: i64 = 4095 9const K_MAGIC_1950701684: i64 = 1950701684 10const K_MAGIC_2048: i64 = 2048 11const K_MAGIC_2047: i64 = 2047 12const K_MAGIC_8192: i64 = 8192 13func nx_store_ingest_keq(buf: *u8, off: i64, kl: i64, key: *u8) -> i64 { 14 var i: i64 = 0 15 while i < kl { if (buf[off + i] & 0xff) != (key[i] & 0xff) { return 0 } i = i + 1 } 16 if (key[kl] & 0xff) != 0 { return 0 } 17 return 1 18} 19// ★ONE CANON, ONE PLACE (2026-08-04, debt 1785882708). This 210-line block -- the length guard, 20// the seven canonical key declarations and the tEXt chunk walk that harvests them -- was 21// DUPLICATED VERBATIM in nx_store_ingest_ingest and nx_store_ingest_compute_cid. Adding `size` and 22// then `args` to the canon meant editing BOTH by hand; the day someone edits one, the two disagree 23// about what a CID *is*, and the symptom is silent gallery data loss (a render overwriting another). 24// Extracted verbatim: both callers now harvest through here, so they CANNOT drift. 25// Returns the number of canonical fields found (gn), or -1 when the input is not a GENREC PNG. 26func nx_store_ingest_harvest(buf: *u8, flen: i64, gkeys: *i64, gvals: *i64) -> i64 { 27 if flen < 8 { return 0 - 1 } 28 let cb: *u8 = (buf as i64 + 8) as *u8 29 let cn: i64 = flen - 8 30 // gkeys/gvals are the CALLER'S arrays -- re-declaring them here would shadow the parameters and 31 // silently leave the caller with an empty canon (and therefore a wrong, colliding CID). 32 var gn: i64 = 0 33 let key0: *u8 = sys_mmap(5) 34 key0[0] = 115 as u8 35 key0[1] = 101 as u8 36 key0[2] = 101 as u8 37 key0[3] = 100 as u8 38 key0[4] = 0 as u8 39 let key1: *u8 = sys_mmap(6) 40 key1[0] = 109 as u8 41 key1[1] = 111 as u8 42 key1[2] = 100 as u8 43 key1[3] = 101 as u8 44 key1[4] = 108 as u8 45 key1[5] = 0 as u8 46 let key2: *u8 = sys_mmap(8) 47 key2[0] = 115 as u8 48 key2[1] = 97 as u8 49 key2[2] = 109 as u8 50 key2[3] = 112 as u8 51 key2[4] = 108 as u8 52 key2[5] = 101 as u8 53 key2[6] = 114 as u8 54 key2[7] = 0 as u8 55 let key3: *u8 = sys_mmap(6) 56 key3[0] = 115 as u8 57 key3[1] = 116 as u8 58 key3[2] = 101 as u8 59 key3[3] = 112 as u8 60 key3[4] = 115 as u8 61 key3[5] = 0 as u8 62 let key4: *u8 = sys_mmap(15) 63 key4[0] = 103 as u8 64 key4[1] = 101 as u8 65 key4[2] = 110 as u8 66 key4[3] = 101 as u8 67 key4[4] = 114 as u8 68 key4[5] = 97 as u8 69 key4[6] = 116 as u8 70 key4[7] = 111 as u8 71 key4[8] = 114 as u8 72 key4[9] = 95 as u8 73 key4[10] = 104 as u8 74 key4[11] = 111 as u8 75 key4[12] = 115 as u8 76 key4[13] = 116 as u8 77 key4[14] = 0 as u8 78 let key5: *u8 = sys_mmap(7) 79 key5[0] = 112 as u8 80 key5[1] = 114 as u8 81 key5[2] = 111 as u8 82 key5[3] = 109 as u8 83 key5[4] = 112 as u8 84 key5[5] = 116 as u8 85 key5[6] = 0 as u8 86 // key6 = "size" (WxH), added 2026-08-04 for the CID-COLLISION DATA-LOSS fix (debt 1785881333). 87 // The CID is a hash of THIS canonical key set, NOT of the pixels, so before this key two renders 88 // differing only in resolution produced an IDENTICAL cid and the second silently OVERWROTE the 89 // first blob. ★ADDITIVE AND PROVABLY NON-BREAKING FOR EVERY EXISTING CONSUMER (GALX included): 90 // a PNG carrying no `size` tEXt never matches this key, so gn is unchanged, the canonical form 91 // is unchanged, and its CID is byte-identical to before. Nothing already in a store moves. 92 // ⚠This canon builder is DUPLICATED in nx_store_ingest_ingest and nx_store_ingest_compute_cid; 93 // the key is added to BOTH so the two can never disagree about what a CID is. 94 let key6: *u8 = sys_mmap(5) 95 key6[0] = 115 as u8 96 key6[1] = 105 as u8 97 key6[2] = 122 as u8 98 key6[3] = 101 as u8 99 key6[4] = 0 as u8 100 // key7 = "args": the EXACT generator-facing argument string. `size` alone did not close the 101 // collision class -- cfg 1.0 vs 5.0 still produced two different images under ONE cid. Keying on 102 // what was actually sent to the engine closes it generically, for every present and future knob. 103 let key7: *u8 = sys_mmap(5) 104 key7[0] = 97 as u8 105 key7[1] = 114 as u8 106 key7[2] = 103 as u8 107 key7[3] = 115 as u8 108 key7[4] = 0 as u8 109 let out: *i64 = sys_mmap(64) as *i64 110 var off: i64 = 0 111 while off < cn { 112 if off + 8 > cn { return 0 - 1 } 113 let clen: i64 = _pe_pngchunk_rdbe(cb, off + 0, 4) 114 let cty: i64 = _pe_pngchunk_rdbe(cb, off + 4, 4) 115 if off + 12 + clen > cn { return 0 - 1 } 116 if cty == K_MAGIC_1950701684 { 117 let body: *u8 = (cb as i64 + off + 8) as *u8 118 if _pe_pngtext_locate(body, clen, out) == 0 { 119 let kwoff: i64 = out[2] 120 let kwlen: i64 = out[3] 121 let voff: i64 = out[0] 122 let vlen: i64 = out[1] 123 if nx_store_ingest_keq(body, kwoff, kwlen, key0) == 1 { 124 let kk: *u8 = sys_mmap(80) 125 var ci: i64 = 0 126 while ci < kwlen { kk[ci] = body[kwoff + ci]; ci = ci + 1 } 127 kk[kwlen] = 0 as u8 128 let vv: *u8 = sys_mmap(512) 129 ci = 0 130 while ci < vlen { vv[ci] = body[voff + ci]; ci = ci + 1 } 131 vv[vlen] = 0 as u8 132 gkeys[gn] = kk as i64 133 gvals[gn] = vv as i64 134 gn = gn + 1 135 } 136 if nx_store_ingest_keq(body, kwoff, kwlen, key1) == 1 { 137 let kk: *u8 = sys_mmap(80) 138 var ci: i64 = 0 139 while ci < kwlen { kk[ci] = body[kwoff + ci]; ci = ci + 1 } 140 kk[kwlen] = 0 as u8 141 let vv: *u8 = sys_mmap(512) 142 ci = 0 143 while ci < vlen { vv[ci] = body[voff + ci]; ci = ci + 1 } 144 vv[vlen] = 0 as u8 145 gkeys[gn] = kk as i64 146 gvals[gn] = vv as i64 147 gn = gn + 1 148 } 149 if nx_store_ingest_keq(body, kwoff, kwlen, key2) == 1 { 150 let kk: *u8 = sys_mmap(80) 151 var ci: i64 = 0 152 while ci < kwlen { kk[ci] = body[kwoff + ci]; ci = ci + 1 } 153 kk[kwlen] = 0 as u8 154 let vv: *u8 = sys_mmap(512) 155 ci = 0 156 while ci < vlen { vv[ci] = body[voff + ci]; ci = ci + 1 } 157 vv[vlen] = 0 as u8 158 gkeys[gn] = kk as i64 159 gvals[gn] = vv as i64 160 gn = gn + 1 161 } 162 if nx_store_ingest_keq(body, kwoff, kwlen, key3) == 1 { 163 let kk: *u8 = sys_mmap(80) 164 var ci: i64 = 0 165 while ci < kwlen { kk[ci] = body[kwoff + ci]; ci = ci + 1 } 166 kk[kwlen] = 0 as u8 167 let vv: *u8 = sys_mmap(512) 168 ci = 0 169 while ci < vlen { vv[ci] = body[voff + ci]; ci = ci + 1 } 170 vv[vlen] = 0 as u8 171 gkeys[gn] = kk as i64 172 gvals[gn] = vv as i64 173 gn = gn + 1 174 } 175 if nx_store_ingest_keq(body, kwoff, kwlen, key4) == 1 { 176 let kk: *u8 = sys_mmap(80) 177 var ci: i64 = 0 178 while ci < kwlen { kk[ci] = body[kwoff + ci]; ci = ci + 1 } 179 kk[kwlen] = 0 as u8 180 let vv: *u8 = sys_mmap(512) 181 ci = 0 182 while ci < vlen { vv[ci] = body[voff + ci]; ci = ci + 1 } 183 vv[vlen] = 0 as u8 184 gkeys[gn] = kk as i64 185 gvals[gn] = vv as i64 186 gn = gn + 1 187 } 188 if nx_store_ingest_keq(body, kwoff, kwlen, key7) == 1 { 189 let kk7: *u8 = sys_mmap(80) 190 var c7: i64 = 0 191 while c7 < kwlen { kk7[c7] = body[kwoff + c7]; c7 = c7 + 1 } 192 kk7[kwlen] = 0 as u8 193 let vv7: *u8 = sys_mmap(K_MAGIC_4096) 194 var v7: i64 = vlen 195 if v7 > K_MAGIC_4095 { v7 = K_MAGIC_4095 } 196 c7 = 0 197 while c7 < v7 { vv7[c7] = body[voff + c7]; c7 = c7 + 1 } 198 vv7[v7] = 0 as u8 199 gkeys[gn] = kk7 as i64 200 gvals[gn] = vv7 as i64 201 gn = gn + 1 202 } 203 if nx_store_ingest_keq(body, kwoff, kwlen, key6) == 1 { 204 let kk6: *u8 = sys_mmap(80) 205 var c6: i64 = 0 206 while c6 < kwlen { kk6[c6] = body[kwoff + c6]; c6 = c6 + 1 } 207 kk6[kwlen] = 0 as u8 208 let vv6: *u8 = sys_mmap(512) 209 var v6: i64 = vlen 210 if v6 > 511 { v6 = 511 } 211 c6 = 0 212 while c6 < v6 { vv6[c6] = body[voff + c6]; c6 = c6 + 1 } 213 vv6[v6] = 0 as u8 214 gkeys[gn] = kk6 as i64 215 gvals[gn] = vv6 as i64 216 gn = gn + 1 217 } 218 if nx_store_ingest_keq(body, kwoff, kwlen, key5) == 1 { 219 let kk: *u8 = sys_mmap(80) 220 var ci: i64 = 0 221 while ci < kwlen { kk[ci] = body[kwoff + ci]; ci = ci + 1 } 222 kk[kwlen] = 0 as u8 223 let vv: *u8 = sys_mmap(K_MAGIC_2048) 224 var vc: i64 = vlen 225 if vc > K_MAGIC_2047 { vc = K_MAGIC_2047 } 226 ci = 0 227 while ci < vc { vv[ci] = body[voff + ci]; ci = ci + 1 } 228 vv[vc] = 0 as u8 229 gkeys[gn] = kk as i64 230 gvals[gn] = vv as i64 231 gn = gn + 1 232 } 233 } 234 } 235 off = off + 12 + clen 236 } 237 return gn 238} 239func nx_store_ingest_ingest(buf: *u8, flen: i64, prefix: *u8) -> i64 { 240 let gkeys: *i64 = sys_mmap(8 * 64) as *i64 241 let gvals: *i64 = sys_mmap(8 * 64) as *i64 242 let gn: i64 = nx_store_ingest_harvest(buf, flen, gkeys, gvals) 243 if gn < 1 { return 0 - 1 } 244 let canon: *u8 = sys_mmap(K_MAGIC_8192) 245 let clen2: i64 = canon_encode(gkeys, gvals, gn, canon) 246 let cid: *u8 = sys_mmap(80) 247 cid_of(canon, clen2, cid) 248 let rk: *u8 = sys_mmap(96) 249 rk[0] = 105 as u8 250 rk[1] = 109 as u8 251 rk[2] = 103 as u8 252 rk[3] = 58 as u8 253 var ri: i64 = 0 254 while ri < 69 { rk[4 + ri] = cid[ri]; ri = ri + 1 } 255 rk[73] = 0 as u8 256 let pp: *i64 = sys_mmap(16) as *i64 257 let ll: *i64 = sys_mmap(16) as *i64 258 if ss_get(prefix, rk, pp, ll) == 1 { return 0 } 259 let segs: *i64 = sys_mmap(8 * 260) as *i64 260 let nseg: i64 = ss_manifest(prefix, segs) 261 var segid: i64 = 1 262 if nseg >= 0 { segid = 1 + nseg } 263 let wr: *i64 = ss_begin() 264 ss_add(wr, 1, rk, canon, clen2) 265 let rc: i64 = ss_commit(prefix, wr, segid) 266 if rc != 0 { return 0 - 1 } 267 return 1 268} 269// ADDITIVE sibling (GALX-PROD): same 5-stage ingest, but ALSO writes the 69-char CID to cidout 270// so a caller can build a CID->path sidecar without re-parsing. Returns 1=new, 0=already-present 271// (cidout still filled), -1=not a GENREC PNG. The original nx_store_ingest_ingest is UNTOUCHED. 272// GALX-PROD-FULL: dedup + segid use the cap-aware store siblings (8192) so re-ingest stays 273// idempotent past image #256 (the original ss_get/ss_manifest cap of 256 would have created 274// duplicate segments once the store grows beyond 256 segments). 275// SHARED compute (refactor): parse PNG -> harvest GENREC -> canon_encode -> CID -> record key. 276// NO store side-effect. Returns canon length (clen2>0) on success, -1 if <8 bytes or no GENREC found. 277// Writes the 69B CID (+NUL) to cidout, the canon bytes to canonout (caller mmaps >=8192), and the 278// record key "img:"+CID (73B +NUL) to rkout (>=96). nx_store_ingest_ingest_cid AND the bulk-ingest 279// driver BOTH call this, so their CIDs are byte-identical (no replication drift). 280func nx_store_ingest_compute_cid(buf: *u8, flen: i64, cidout: *u8, canonout: *u8, rkout: *u8) -> i64 { 281 let gkeys: *i64 = sys_mmap(8 * 64) as *i64 282 let gvals: *i64 = sys_mmap(8 * 64) as *i64 283 let gn: i64 = nx_store_ingest_harvest(buf, flen, gkeys, gvals) 284 if gn < 1 { return 0 - 1 } 285 let clen2: i64 = canon_encode(gkeys, gvals, gn, canonout) 286 let cid: *u8 = sys_mmap(80) 287 cid_of(canonout, clen2, cid) 288 var co: i64 = 0 289 while co < 69 { cidout[co] = cid[co]; co = co + 1 } 290 cidout[69] = 0 as u8 291 rkout[0] = 105 as u8 292 rkout[1] = 109 as u8 293 rkout[2] = 103 as u8 294 rkout[3] = 58 as u8 295 var ri: i64 = 0 296 while ri < 69 { rkout[4 + ri] = cid[ri]; ri = ri + 1 } 297 rkout[73] = 0 as u8 298 return clen2 299} 300 301// store wrapper -- compute then dedup (cap-aware) then commit ONE segment. Behavior is byte-identical 302// to the pre-refactor ingest_cid (GALX-001 gate verifies). The bulk path skips this and batches. 303func nx_store_ingest_ingest_cid(buf: *u8, flen: i64, prefix: *u8, cidout: *u8) -> i64 { 304 let canon: *u8 = sys_mmap(K_MAGIC_8192) 305 let rk: *u8 = sys_mmap(96) 306 let clen2: i64 = nx_store_ingest_compute_cid(buf, flen, cidout, canon, rk) 307 if clen2 < 0 { return 0 - 1 } 308 let pp: *i64 = sys_mmap(16) as *i64 309 let ll: *i64 = sys_mmap(16) as *i64 310 // GALX-PROD-FULL: dedup + segid must see the WHOLE store (cap-aware siblings, 8192). 311 if ss_get_cap(prefix, rk, pp, ll, K_MAGIC_8192) == 1 { return 0 } 312 let segs: *i64 = sys_mmap(8 * K_MAGIC_8192) as *i64 313 let nseg: i64 = ss_manifest_cap(prefix, segs, K_MAGIC_8192) 314 var segid: i64 = 1 315 if nseg >= 0 { segid = 1 + nseg } 316 let wr: *i64 = ss_begin() 317 ss_add(wr, 1, rk, canon, clen2) 318 let rc: i64 = ss_commit(prefix, wr, segid) 319 if rc != 0 { return 0 - 1 } 320 return 1 321}