code wiki / (root) / nx_varfacts_lib.nx

nx_varfacts_lib.nx source

↩ module page · 708 lines · 29249 B

1// nx_varfacts_lib.nx -- VaM .var PACKAGE FACT EXTRACTION CORE (conversion tier, VaM lane). 2// 3// A .var is a ZIP archive. THE WALL THIS LANE LIVES BEHIND (standing policy 1785794796 + 4// measure-dont-ship): VaM is a CAPABILITY TARGET -- asset BYTES (meshes/textures/morph deltas) 5// never enter our products or our published plane. What this core reads is the CENTRAL DIRECTORY 6// (file NAMES + sizes = the package's own manifest) and meta.json (the package's own declared 7// license) -- the package describing itself, never its content. Extracted MEASUREMENTS flow to 8// refcorpus behind the k>=8 admission wall like every other reference corpus. 9// 10// COMPOSES INCUMBENTS: EOCD/central-directory walk = the nx_zip_read shape (that organ is a 11// hardwired PyTorch-.bin test, not a library -- the walk is factored HERE as callables); ZIP 12// method-8 members inflate via nx_deflate_inflate, the SAME raw-DEFLATE core under nx_gzip_wrap 13// and nx_zlib_wrap (capacity-bounded; NOT the retired duplicate nx_inflate, debt 1785854636). 14// 15// ★TAIL-WINDOW CONTRACT: the EOCD + central directory live at the END of a ZIP, so a 519MB .var 16// measures from its last few MB. The caller hands the TAIL bytes + the true file size; if the 17// central directory starts before the window, the probe REFUSES (ok=0) -- a partial census is a 18// plausible undercount, which is worse than absence. 19// 20// Fact slots (flat i64, VF_N_SLOTS): 21// [0] ok [1] entries [2] morphs(.vmi) [3] scenes(Saves/scene/*.json) [4] textures(jpg/png/tif) 22// [5] clothing(/Clothing/) [6] hair(/Hair/) [7] plugins(.cs/.cslist/.dll) [8] appearance(.vap) 23// [9] meta_found [10] meta_lho [11] meta_method [12] meta_csize [13] meta_usize 24// 25// ★2026-09-11 EXTENSION (VaM census lane, ADDITIVE -- every legacy slot above keeps its meaning): 26// (1) THE WALK IS FACTORED INTO CALLABLES so vf_probe, the census CLI (nx_varcensus) and the gate share ONE walk: 27// vf_cd_locate reads the EOCD and names the outcome (OK / NO-EOCD / ZIP64 / WINDOW / SHORT) instead of one 28// ok=0 for five different reasons, and WINDOW hands the caller the exact window it must read (file_size - 29// cd_abs, derived from the archive itself, so no tail size has to be guessed); vf_cd_entry reads ONE record. 30// (2) vf_member_data: STORED bytes in place, DEFLATE through the incumbent nx_deflate_inflate sized from the member's 31// DECLARED usize and required to decode to exactly usize -- no capacity constant to tune. CRC is NOT verified here 32// (imprecision chosen: a corrupt meta.json fails its JSON parse and is counted as unparsed, it is never trusted). 33// (3) vf_json_*: a DEPTH-AWARE reader for meta.json. A key is matched only as a DIRECT member of the object it is asked 34// of, so a nested dependency's own licenseType can never answer for the package (vf_license below takes the FIRST 35// occurrence in the byte stream; the census prints both readings so their agreement is measured, not assumed). 36// license_tier: ORIGINAL No hw writes (Rule 26). 37import "nx_syscalls.nx" 38import "nx_deflate.nx" 39const VF_MAGIC_1024: i64 = 1024 40const VF_MAGIC_4194304: i64 = 4194304 41 42const VF_N_SLOTS: i64 = 14 43 44// ---- PKZIP layout (APPNOTE 4.3.6 / 4.3.12 / 4.3.16), named so every offset below reads as a field ---- 45const VF_SIG_EOCD: i64 = 0x06054b50 46const VF_SIG_CD: i64 = 0x02014b50 47const VF_SIG_LOCAL: i64 = 0x04034b50 48const VF_EOCD_LEN: i64 = 22 49const VF_CD_LEN: i64 = 46 50const VF_LOCAL_LEN: i64 = 30 51const VF_COMMENT_MAX: i64 = 65535 52const VF_U16_ALL: i64 = 0xffff 53const VF_U32_ALL: i64 = 0xffffffff 54const VF_EOCD_ENTRIES: i64 = 10 55const VF_EOCD_CDSIZE: i64 = 12 56const VF_EOCD_CDOFF: i64 = 16 57const VF_CD_FLAGS: i64 = 8 58const VF_CD_METHOD: i64 = 10 59const VF_CD_CSIZE: i64 = 20 60const VF_CD_USIZE: i64 = 24 61const VF_CD_FNL: i64 = 28 62const VF_CD_EXL: i64 = 30 63const VF_CD_CML: i64 = 32 64const VF_CD_LHO: i64 = 42 65const VF_LOCAL_FNL: i64 = 26 66const VF_LOCAL_EXL: i64 = 28 67const VF_METHOD_STORE: i64 = 0 68const VF_METHOD_DEFLATE: i64 = 8 69 70// vf_cd_locate box slots + states 71const VF_L_EOCD: i64 = 0 72const VF_L_ENTRIES: i64 = 1 73const VF_L_CDSIZE: i64 = 2 74const VF_L_CDABS: i64 = 3 75const VF_L_CD: i64 = 4 76const VF_L_STATE: i64 = 5 77const VF_L_N: i64 = 8 78const VF_LOC_OK: i64 = 0 79const VF_LOC_NOEOCD: i64 = 1 80const VF_LOC_ZIP64: i64 = 2 81const VF_LOC_WINDOW: i64 = 3 82const VF_LOC_SHORT: i64 = 4 83 84// vf_cd_entry record slots 85const VF_E_METHOD: i64 = 0 86const VF_E_CSIZE: i64 = 1 87const VF_E_USIZE: i64 = 2 88const VF_E_NAMELEN: i64 = 3 89const VF_E_LHO: i64 = 4 90const VF_E_NAMEOFF: i64 = 5 91const VF_E_NEXT: i64 = 6 92const VF_E_FLAGS: i64 = 7 93const VF_E_Z64: i64 = 8 94const VF_E_N: i64 = 10 95// ZIP64 extended information extra field (APPNOTE 4.5.3). MEASURED 2026-09-11 on real VaM packages: a writer can mark 96// EVERY central-directory size as 0xFFFFFFFF with the true values in extra 0x0001 even when the archive is small and its 97// EOCD is plain 32-bit -- so a reader that trusts the fixed fields reads 4294967295-byte members and every meta.json read fails. 98const VF_CD_DISK: i64 = 34 99const VF_X_ZIP64: i64 = 0x0001 100const VF_X_HDR: i64 = 4 101const VF_U64_LEN: i64 = 8 102const VF_Z64_NONE: i64 = 0 103const VF_Z64_RESOLVED: i64 = 1 104const VF_Z64_UNRESOLVED: i64 = 0 - 1 105 106// vf_member_data states 107const VF_MD_STORED: i64 = 0 108const VF_MD_INFLATED: i64 = 1 109const VF_MD_FAILED: i64 = 2 110const VF_MD_UNSUPPORTED: i64 = 3 111 112// JSON structural bytes 113const VF_J_QUOTE: i64 = 34 114const VF_J_BSL: i64 = 92 115const VF_J_COLON: i64 = 58 116const VF_J_COMMA: i64 = 44 117const VF_J_LBRACE: i64 = 123 118const VF_J_RBRACE: i64 = 125 119const VF_J_LBRACK: i64 = 91 120const VF_J_RBRACK: i64 = 93 121const VF_J_SPACE: i64 = 32 122const VF_J_TAB: i64 = 9 123const VF_J_LF: i64 = 10 124const VF_J_CR: i64 = 13 125// JSON value kinds (vf_json_kind) 126const VF_JK_BAD: i64 = 0 127const VF_JK_STRING: i64 = 1 128const VF_JK_OBJECT: i64 = 2 129const VF_JK_ARRAY: i64 = 3 130const VF_JK_SCALAR: i64 = 4 131// vf_json_member box slots 132const VF_JM_KS: i64 = 0 133const VF_JM_KE: i64 = 1 134const VF_JM_VS: i64 = 2 135const VF_JM_VE: i64 = 3 136const VF_JM_N: i64 = 4 137// UTF-8 encoder structure (RFC 3629) 138const VF_UTF8_CONT: i64 = 128 139const VF_UTF8_LEAD2: i64 = 192 140const VF_UTF8_LEAD3: i64 = 224 141const VF_UTF8_LEAD4: i64 = 240 142const VF_UTF8_SEXTET: i64 = 64 143const VF_UTF8_MAX1: i64 = 128 144const VF_UTF8_MAX2: i64 = 2048 145const VF_UTF8_MAX3: i64 = 65536 146const VF_SURR_HI: i64 = 0xd800 147const VF_SURR_LO: i64 = 0xdc00 148const VF_SURR_END: i64 = 0xe000 149const VF_SURR_BASE: i64 = 0x10000 150const VF_SURR_SPAN: i64 = 1024 151const VF_HEX_DIGITS: i64 = 4 152const VF_HEX_BASE: i64 = 16 153 154func vf_u16(b: *u8, o: i64) -> i64 { return (b[o] as i64 & 0xff) | ((b[o+1] as i64 & 0xff) << 8) } 155func vf_u32(b: *u8, o: i64) -> i64 { return (b[o] as i64 & 0xff) | ((b[o+1] as i64 & 0xff) << 8) | ((b[o+2] as i64 & 0xff) << 16) | ((b[o+3] as i64 & 0xff) << 24) } 156 157// name[0,nl) ends with suffix? 158func vf_ends(name: *u8, nl: i64, suf: *u8) -> i64 { 159 var m: i64 = 0 160 while suf[m] != (0 as u8) { m = m + 1 } 161 if nl < m { return 0 } 162 var i: i64 = 0 163 while i < m { if (name[nl-m+i] as i64) != (suf[i] as i64) { return 0 } i = i + 1 } 164 return 1 165} 166func vf_has(name: *u8, nl: i64, sub: *u8) -> i64 { 167 var m: i64 = 0 168 while sub[m] != (0 as u8) { m = m + 1 } 169 if nl < m { return 0 } 170 var i: i64 = 0 171 while i + m <= nl { 172 var k: i64 = 0 173 while k < m { if (name[i+k] as i64) != (sub[k] as i64) { k = m + 9 } else { k = k + 1 } } 174 if k == m { return 1 } 175 i = i + 1 176 } 177 return 0 178} 179 180// the first window a caller must read to be sure it holds the EOCD: the record plus the longest legal comment 181func vf_eocd_window(file_size: i64) -> i64 { 182 var w: i64 = VF_EOCD_LEN + VF_COMMENT_MAX 183 if w > file_size { w = file_size } 184 if w < 0 { w = 0 } 185 return w 186} 187 188// locate the central directory inside a tail window (tail[0] sits at absolute offset file_size - tailn). Fills box 189// (VF_L_*) and returns the NAMED state. WINDOW means the CD starts before the window: read file_size - box[VF_L_CDABS] 190// bytes from the end and locate again. ZIP64 is refused by name, never parsed as a 32-bit archive. 191func vf_cd_locate(tail: *u8, tailn: i64, file_size: i64, box: *i64) -> i64 { 192 var i: i64 = 0 193 while i < VF_L_N { box[i] = 0 - 1; i = i + 1 } 194 box[VF_L_STATE] = VF_LOC_NOEOCD 195 if tailn < VF_EOCD_LEN { return VF_LOC_NOEOCD } 196 var e: i64 = tailn - VF_EOCD_LEN 197 var eocd: i64 = 0 - 1 198 var run: i64 = 1 199 while run == 1 { 200 if e < 0 { run = 0 } else { 201 if vf_u32(tail, e) == VF_SIG_EOCD { eocd = e; run = 0 } else { e = e - 1 } 202 } 203 } 204 if eocd < 0 { return VF_LOC_NOEOCD } 205 box[VF_L_EOCD] = eocd 206 let entries: i64 = vf_u16(tail, eocd + VF_EOCD_ENTRIES) 207 let cd_size: i64 = vf_u32(tail, eocd + VF_EOCD_CDSIZE) 208 let cd_abs: i64 = vf_u32(tail, eocd + VF_EOCD_CDOFF) 209 box[VF_L_ENTRIES] = entries 210 box[VF_L_CDSIZE] = cd_size 211 box[VF_L_CDABS] = cd_abs 212 var z64: i64 = 0 213 if entries == VF_U16_ALL { z64 = 1 } 214 if cd_size == VF_U32_ALL { z64 = 1 } 215 if cd_abs == VF_U32_ALL { z64 = 1 } 216 if z64 == 1 { box[VF_L_STATE] = VF_LOC_ZIP64; return VF_LOC_ZIP64 } 217 let base: i64 = file_size - tailn 218 let cd: i64 = cd_abs - base 219 if cd < 0 { box[VF_L_STATE] = VF_LOC_WINDOW; return VF_LOC_WINDOW } 220 if cd + cd_size > tailn { box[VF_L_STATE] = VF_LOC_SHORT; return VF_LOC_SHORT } 221 box[VF_L_CD] = cd 222 box[VF_L_STATE] = VF_LOC_OK 223 return VF_LOC_OK 224} 225 226// ONE central-directory record at tail[off]. Fills rec (VF_E_*); returns 1, or 0 when the record or its name would 227// read outside the window or the signature is not a CD record (REFUSE, never a partial row). 228func vf_cd_entry(tail: *u8, tailn: i64, off: i64, rec: *i64) -> i64 { 229 if off < 0 { return 0 } 230 if off + VF_CD_LEN > tailn { return 0 } 231 if vf_u32(tail, off) != VF_SIG_CD { return 0 } 232 let fnl: i64 = vf_u16(tail, off + VF_CD_FNL) 233 let exl: i64 = vf_u16(tail, off + VF_CD_EXL) 234 let cml: i64 = vf_u16(tail, off + VF_CD_CML) 235 if off + VF_CD_LEN + fnl > tailn { return 0 } 236 rec[VF_E_METHOD] = vf_u16(tail, off + VF_CD_METHOD) 237 rec[VF_E_CSIZE] = vf_u32(tail, off + VF_CD_CSIZE) 238 rec[VF_E_USIZE] = vf_u32(tail, off + VF_CD_USIZE) 239 rec[VF_E_NAMELEN] = fnl 240 rec[VF_E_LHO] = vf_u32(tail, off + VF_CD_LHO) 241 rec[VF_E_NAMEOFF] = off + VF_CD_LEN 242 rec[VF_E_NEXT] = off + VF_CD_LEN + fnl + exl + cml 243 rec[VF_E_FLAGS] = vf_u16(tail, off + VF_CD_FLAGS) 244 rec[VF_E_Z64] = vf_zip64_extra(tail, tailn, off + VF_CD_LEN + fnl, exl, rec) 245 return 1 246} 247 248func vf_u64(b: *u8, o: i64) -> i64 { return vf_u32(b, o) | (vf_u32(b, o + 4) << 32) } 249 250// resolve the CD fields that read 0xFFFFFFFF from extra field 0x0001 (values present ONLY for the overflowed fields, in the 251// fixed order usize, csize, lho). The need flags are taken BEFORE any field is rewritten, so resolving one cannot change 252// whether the next is read. Returns VF_Z64_NONE (nothing overflowed), VF_Z64_RESOLVED, or VF_Z64_UNRESOLVED (a field 253// overflowed and no well-formed 0x0001 record carries it -- the caller must refuse that member's sizes by name). 254func vf_zip64_extra(tail: *u8, tailn: i64, xs: i64, xl: i64, rec: *i64) -> i64 { 255 var nu: i64 = 0 256 var nc: i64 = 0 257 var nlh: i64 = 0 258 if rec[VF_E_USIZE] == VF_U32_ALL { nu = 1 } 259 if rec[VF_E_CSIZE] == VF_U32_ALL { nc = 1 } 260 if rec[VF_E_LHO] == VF_U32_ALL { nlh = 1 } 261 if nu + nc + nlh == 0 { return VF_Z64_NONE } 262 let xe: i64 = xs + xl 263 if xe > tailn { return VF_Z64_UNRESOLVED } 264 var p: i64 = xs 265 while p + VF_X_HDR <= xe { 266 let id: i64 = vf_u16(tail, p) 267 let sz: i64 = vf_u16(tail, p + 2) 268 let ds: i64 = p + VF_X_HDR 269 let de: i64 = ds + sz 270 if de > xe { return VF_Z64_UNRESOLVED } 271 if id == VF_X_ZIP64 { 272 if (nu + nc + nlh) * VF_U64_LEN > sz { return VF_Z64_UNRESOLVED } 273 var q: i64 = ds 274 if nu == 1 { rec[VF_E_USIZE] = vf_u64(tail, q); q = q + VF_U64_LEN } 275 if nc == 1 { rec[VF_E_CSIZE] = vf_u64(tail, q); q = q + VF_U64_LEN } 276 if nlh == 1 { rec[VF_E_LHO] = vf_u64(tail, q); q = q + VF_U64_LEN } 277 return VF_Z64_RESOLVED 278 } 279 p = de 280 } 281 return VF_Z64_UNRESOLVED 282} 283 284// probe the TAIL of a .var: tail holds the last tailn bytes, file_size is the true size. (Legacy contract, now a 285// consumer of vf_cd_locate + vf_cd_entry: every slot and every refusal is what it was.) 286func vf_probe(tail: *u8, tailn: i64, file_size: i64, facts: *i64) -> i64 { 287 var i: i64 = 0 288 while i < VF_N_SLOTS { facts[i] = 0 - 1; i = i + 1 } 289 facts[0] = 0 290 let box: *i64 = sys_mmap(VF_L_N * 8) as *i64 291 if vf_cd_locate(tail, tailn, file_size, box) != VF_LOC_OK { return 0 } 292 let entries: i64 = box[VF_L_ENTRIES] 293 let rec: *i64 = sys_mmap(VF_E_N * 8) as *i64 294 var morphs: i64 = 0 295 var scenes: i64 = 0 296 var tex: i64 = 0 297 var cloth: i64 = 0 298 var hair: i64 = 0 299 var plug: i64 = 0 300 var vap: i64 = 0 301 var off: i64 = box[VF_L_CD] 302 var k: i64 = 0 303 while k < entries { 304 if vf_cd_entry(tail, tailn, off, rec) != 1 { facts[0] = 0; return 0 } 305 let nm: *u8 = ((tail as i64) + rec[VF_E_NAMEOFF]) as *u8 306 let fnl: i64 = rec[VF_E_NAMELEN] 307 if vf_ends(nm, fnl, ".vmi" as *u8) == 1 { morphs = morphs + 1 } 308 if vf_has(nm, fnl, "Saves/scene/" as *u8) == 1 { if vf_ends(nm, fnl, ".json" as *u8) == 1 { scenes = scenes + 1 } } 309 if vf_ends(nm, fnl, ".jpg" as *u8) == 1 { tex = tex + 1 } 310 if vf_ends(nm, fnl, ".png" as *u8) == 1 { tex = tex + 1 } 311 if vf_ends(nm, fnl, ".tif" as *u8) == 1 { tex = tex + 1 } 312 if vf_has(nm, fnl, "/Clothing/" as *u8) == 1 { cloth = cloth + 1 } 313 if vf_has(nm, fnl, "/Hair/" as *u8) == 1 { hair = hair + 1 } 314 if vf_ends(nm, fnl, ".cs" as *u8) == 1 { plug = plug + 1 } 315 if vf_ends(nm, fnl, ".cslist" as *u8) == 1 { plug = plug + 1 } 316 if vf_ends(nm, fnl, ".dll" as *u8) == 1 { plug = plug + 1 } 317 if vf_ends(nm, fnl, ".vap" as *u8) == 1 { vap = vap + 1 } 318 if fnl == 9 { if vf_has(nm, fnl, "meta.json" as *u8) == 1 { 319 facts[9] = 1 320 facts[10] = rec[VF_E_LHO] 321 facts[11] = rec[VF_E_METHOD] 322 facts[12] = rec[VF_E_CSIZE] 323 facts[13] = rec[VF_E_USIZE] 324 } } 325 off = rec[VF_E_NEXT] 326 k = k + 1 327 } 328 facts[0] = 1 329 facts[1] = entries 330 facts[2] = morphs 331 facts[3] = scenes 332 facts[4] = tex 333 facts[5] = cloth 334 facts[6] = hair 335 facts[7] = plug 336 facts[8] = vap 337 if facts[9] != 1 { facts[9] = 0 } 338 return 0 339} 340 341// the data of ONE member whose compressed bytes are body[0..csize). out[0] = data length, out[1] = VF_MD_* state. 342// Returns the data pointer (body itself when STORED) or 0. DEFLATE output is sized from the DECLARED usize and must 343// decode to EXACTLY usize bytes, so a lying header refuses instead of truncating. 344func vf_member_data(body: *u8, csize: i64, method: i64, usize: i64, out: *i64) -> *u8 { 345 out[0] = 0 346 out[1] = VF_MD_FAILED 347 if method == VF_METHOD_STORE { 348 out[0] = csize 349 out[1] = VF_MD_STORED 350 return body 351 } 352 if method != VF_METHOD_DEFLATE { out[1] = VF_MD_UNSUPPORTED; return 0 as *u8 } 353 if csize <= 0 { return 0 as *u8 } 354 if usize <= 0 { return 0 as *u8 } 355 let dr: *NxDeflateResult = nx_deflate_inflate(body, csize, usize) 356 if (dr as i64) == 0 { return 0 as *u8 } 357 if (dr.error_code as i64) != 0 { return 0 as *u8 } 358 if (dr.output_size as i64) != usize { return 0 as *u8 } 359 out[0] = usize 360 out[1] = VF_MD_INFLATED 361 return dr.output_data 362} 363 364// ---- depth-aware JSON reading for meta.json ---- 365func vf_json_ws(c: i64) -> i64 { 366 if c == VF_J_SPACE { return 1 } 367 if c == VF_J_TAB { return 1 } 368 if c == VF_J_LF { return 1 } 369 if c == VF_J_CR { return 1 } 370 return 0 371} 372func vf_json_skip(src: *u8, n: i64, p: i64) -> i64 { 373 var q: i64 = p 374 var run: i64 = 1 375 while run == 1 { 376 if q >= n { run = 0 } else { 377 if vf_json_ws(src[q] as i64 & 0xff) == 1 { q = q + 1 } else { run = 0 } 378 } 379 } 380 return q 381} 382func vf_json_open(c: i64) -> i64 { if c == VF_J_LBRACE { return 1 } if c == VF_J_LBRACK { return 1 } return 0 } 383func vf_json_close(c: i64) -> i64 { if c == VF_J_RBRACE { return 1 } if c == VF_J_RBRACK { return 1 } return 0 } 384 385// end (exclusive, one past the closing quote) of the string whose opening quote is at p; -1 when unterminated 386func vf_json_str_end(src: *u8, n: i64, p: i64) -> i64 { 387 var j: i64 = p + 1 388 while j < n { 389 let c: i64 = src[j] as i64 & 0xff 390 if c == VF_J_BSL { j = j + 2 } else { 391 if c == VF_J_QUOTE { return j + 1 } 392 j = j + 1 393 } 394 } 395 return 0 - 1 396} 397 398// kind of the value that starts at p 399func vf_json_kind(src: *u8, n: i64, p: i64) -> i64 { 400 if p < 0 { return VF_JK_BAD } 401 if p >= n { return VF_JK_BAD } 402 let c: i64 = src[p] as i64 & 0xff 403 if c == VF_J_QUOTE { return VF_JK_STRING } 404 if c == VF_J_LBRACE { return VF_JK_OBJECT } 405 if c == VF_J_LBRACK { return VF_JK_ARRAY } 406 if vf_json_close(c) == 1 { return VF_JK_BAD } 407 if c == VF_J_COMMA { return VF_JK_BAD } 408 if c == VF_J_COLON { return VF_JK_BAD } 409 return VF_JK_SCALAR 410} 411 412// end (exclusive) of the JSON value starting at p; -1 when malformed or unterminated. Brackets are matched with string 413// awareness, so a brace inside a description never closes an object. 414func vf_json_value_end(src: *u8, n: i64, p: i64) -> i64 { 415 let kind: i64 = vf_json_kind(src, n, p) 416 if kind == VF_JK_BAD { return 0 - 1 } 417 if kind == VF_JK_STRING { return vf_json_str_end(src, n, p) } 418 if kind == VF_JK_SCALAR { 419 var s: i64 = p 420 while s < n { 421 let c: i64 = src[s] as i64 & 0xff 422 if c == VF_J_COMMA { return s } 423 if vf_json_close(c) == 1 { return s } 424 if vf_json_ws(c) == 1 { return s } 425 s = s + 1 426 } 427 return n 428 } 429 var depth: i64 = 0 430 var j: i64 = p 431 while j < n { 432 let c2: i64 = src[j] as i64 & 0xff 433 if c2 == VF_J_QUOTE { 434 let se: i64 = vf_json_str_end(src, n, j) 435 if se < 0 { return 0 - 1 } 436 j = se 437 } else { 438 if vf_json_open(c2) == 1 { depth = depth + 1 } 439 if vf_json_close(c2) == 1 { 440 depth = depth - 1 441 if depth == 0 { return j + 1 } 442 } 443 j = j + 1 444 } 445 } 446 return 0 - 1 447} 448 449// ONE member of an object whose value span ends at `end` (exclusive; src[end-1] is its closing brace), starting at p 450// (whitespace already skipped). box: key content span [KS,KE) (inside the quotes), value span [VS,VE). Returns where the 451// next member starts (end-1 after the last member), or -1 when there is no member here or the object is malformed. 452func vf_json_member(src: *u8, end: i64, p: i64, box: *i64) -> i64 { 453 if p < 0 { return 0 - 1 } 454 if p >= end - 1 { return 0 - 1 } 455 if (src[p] as i64 & 0xff) != VF_J_QUOTE { return 0 - 1 } 456 let ke: i64 = vf_json_str_end(src, end, p) 457 if ke < 0 { return 0 - 1 } 458 let c: i64 = vf_json_skip(src, end, ke) 459 if c >= end { return 0 - 1 } 460 if (src[c] as i64 & 0xff) != VF_J_COLON { return 0 - 1 } 461 let vs: i64 = vf_json_skip(src, end, c + 1) 462 let ve: i64 = vf_json_value_end(src, end, vs) 463 if ve < 0 { return 0 - 1 } 464 box[VF_JM_KS] = p + 1 465 box[VF_JM_KE] = ke - 1 466 box[VF_JM_VS] = vs 467 box[VF_JM_VE] = ve 468 var q: i64 = vf_json_skip(src, end, ve) 469 if q < end { if (src[q] as i64 & 0xff) == VF_J_COMMA { q = vf_json_skip(src, end, q + 1) } } 470 if q <= p { return 0 - 1 } 471 return q 472} 473 474// src[s..e) equals the NUL-terminated lit, byte for byte 475func vf_span_is(src: *u8, s: i64, e: i64, lit: *u8) -> i64 { 476 var m: i64 = 0 477 while lit[m] != (0 as u8) { m = m + 1 } 478 if e - s != m { return 0 } 479 var i: i64 = 0 480 while i < m { if (src[s+i] as i64 & 0xff) != (lit[i] as i64 & 0xff) { return 0 } i = i + 1 } 481 return 1 482} 483 484// the value start of the DIRECT member `key` of the object whose opening brace is at obj; box gets the member spans. 485// -1 when obj is not an object, the key is not a direct member, or the object is malformed before the key. 486func vf_json_find(src: *u8, n: i64, obj: i64, key: *u8, box: *i64) -> i64 { 487 if vf_json_kind(src, n, obj) != VF_JK_OBJECT { return 0 - 1 } 488 let end: i64 = vf_json_value_end(src, n, obj) 489 if end < 0 { return 0 - 1 } 490 var p: i64 = vf_json_skip(src, end, obj + 1) 491 var run: i64 = 1 492 while run == 1 { 493 if p >= end - 1 { run = 0 } else { 494 let nx: i64 = vf_json_member(src, end, p, box) 495 if nx < 0 { run = 0 } else { 496 if vf_span_is(src, box[VF_JM_KS], box[VF_JM_KE], key) == 1 { return box[VF_JM_VS] } 497 p = nx 498 } 499 } 500 } 501 return 0 - 1 502} 503 504// number of DIRECT members of the object at obj, or -1 when it is not a well-formed object 505func vf_json_count_members(src: *u8, n: i64, obj: i64) -> i64 { 506 if vf_json_kind(src, n, obj) != VF_JK_OBJECT { return 0 - 1 } 507 let end: i64 = vf_json_value_end(src, n, obj) 508 if end < 0 { return 0 - 1 } 509 let box: *i64 = sys_mmap(VF_JM_N * 8) as *i64 510 var p: i64 = vf_json_skip(src, end, obj + 1) 511 var cnt: i64 = 0 512 var run: i64 = 1 513 while run == 1 { 514 if p >= end - 1 { run = 0 } else { 515 let nx: i64 = vf_json_member(src, end, p, box) 516 if nx < 0 { return 0 - 1 } 517 cnt = cnt + 1 518 p = nx 519 } 520 } 521 return cnt 522} 523 524// number of elements of the array at arr, or -1 when it is not a well-formed array 525func vf_json_count_elems(src: *u8, n: i64, arr: i64) -> i64 { 526 if vf_json_kind(src, n, arr) != VF_JK_ARRAY { return 0 - 1 } 527 let end: i64 = vf_json_value_end(src, n, arr) 528 if end < 0 { return 0 - 1 } 529 var p: i64 = vf_json_skip(src, end, arr + 1) 530 var cnt: i64 = 0 531 var run: i64 = 1 532 while run == 1 { 533 if p >= end - 1 { run = 0 } else { 534 let ve: i64 = vf_json_value_end(src, end, p) 535 if ve < 0 { return 0 - 1 } 536 cnt = cnt + 1 537 var q: i64 = vf_json_skip(src, end, ve) 538 if q < end { if (src[q] as i64 & 0xff) == VF_J_COMMA { q = vf_json_skip(src, end, q + 1) } } 539 if q <= p { return 0 - 1 } 540 p = q 541 } 542 } 543 return cnt 544} 545 546func vf_hexval(c: i64) -> i64 { 547 if c >= 48 { if c <= 57 { return c - 48 } } 548 if c >= 65 { if c <= 70 { return c - 55 } } 549 if c >= 97 { if c <= 102 { return c - 87 } } 550 return 0 - 1 551} 552func vf_hex4(src: *u8, at: i64) -> i64 { 553 var v: i64 = 0 554 var i: i64 = 0 555 while i < VF_HEX_DIGITS { 556 let h: i64 = vf_hexval(src[at+i] as i64 & 0xff) 557 if h < 0 { return 0 - 1 } 558 v = v * VF_HEX_BASE + h 559 i = i + 1 560 } 561 return v 562} 563func vf_utf8(out: *u8, o: i64, cp: i64) -> i64 { 564 if cp < VF_UTF8_MAX1 { out[o] = cp as u8; return o + 1 } 565 if cp < VF_UTF8_MAX2 { 566 out[o] = (VF_UTF8_LEAD2 + (cp / VF_UTF8_SEXTET)) as u8 567 out[o+1] = (VF_UTF8_CONT + (cp % VF_UTF8_SEXTET)) as u8 568 return o + 2 569 } 570 if cp < VF_UTF8_MAX3 { 571 out[o] = (VF_UTF8_LEAD3 + (cp / (VF_UTF8_SEXTET * VF_UTF8_SEXTET))) as u8 572 out[o+1] = (VF_UTF8_CONT + ((cp / VF_UTF8_SEXTET) % VF_UTF8_SEXTET)) as u8 573 out[o+2] = (VF_UTF8_CONT + (cp % VF_UTF8_SEXTET)) as u8 574 return o + 3 575 } 576 out[o] = (VF_UTF8_LEAD4 + (cp / (VF_UTF8_SEXTET * VF_UTF8_SEXTET * VF_UTF8_SEXTET))) as u8 577 out[o+1] = (VF_UTF8_CONT + ((cp / (VF_UTF8_SEXTET * VF_UTF8_SEXTET)) % VF_UTF8_SEXTET)) as u8 578 out[o+2] = (VF_UTF8_CONT + ((cp / VF_UTF8_SEXTET) % VF_UTF8_SEXTET)) as u8 579 out[o+3] = (VF_UTF8_CONT + (cp % VF_UTF8_SEXTET)) as u8 580 return o + 4 581} 582 583// decode the string CONTENT span src[s..e) (inside the quotes) into out. The caller sizes out at e - s + 1: a decoded 584// string is never longer than its escaped span (\uXXXX is 6 bytes in and at most 3 out; a surrogate pair 12 in, 4 out). 585// A decoded TAB, CR or LF -- and any raw control byte -- becomes a space, so a value can never tear a TSV row. 586// Returns the decoded length, or -1 on a malformed escape. 587func vf_json_decode(src: *u8, s: i64, e: i64, out: *u8) -> i64 { 588 var i: i64 = s 589 var o: i64 = 0 590 while i < e { 591 let c: i64 = src[i] as i64 & 0xff 592 if c != VF_J_BSL { 593 if c < VF_J_SPACE { out[o] = VF_J_SPACE as u8 } else { out[o] = c as u8 } 594 o = o + 1 595 i = i + 1 596 } else { 597 if i + 1 >= e { return 0 - 1 } 598 let x: i64 = src[i+1] as i64 & 0xff 599 var cp: i64 = 0 - 1 600 var adv: i64 = 2 601 if x == VF_J_QUOTE { cp = VF_J_QUOTE } 602 if x == VF_J_BSL { cp = VF_J_BSL } 603 if x == 47 { cp = 47 } 604 if x == 98 { cp = VF_J_SPACE } 605 if x == 102 { cp = VF_J_SPACE } 606 if x == 110 { cp = VF_J_SPACE } 607 if x == 114 { cp = VF_J_SPACE } 608 if x == 116 { cp = VF_J_SPACE } 609 if x == 117 { 610 if i + 2 + VF_HEX_DIGITS > e { return 0 - 1 } 611 cp = vf_hex4(src, i + 2) 612 if cp < 0 { return 0 - 1 } 613 adv = 2 + VF_HEX_DIGITS 614 if cp >= VF_SURR_HI { if cp < VF_SURR_LO { 615 if i + 4 + 2 * VF_HEX_DIGITS > e { return 0 - 1 } 616 if (src[i+6] as i64 & 0xff) != VF_J_BSL { return 0 - 1 } 617 if (src[i+7] as i64 & 0xff) != 117 { return 0 - 1 } 618 let lo: i64 = vf_hex4(src, i + 8) 619 if lo < VF_SURR_LO { return 0 - 1 } 620 if lo >= VF_SURR_END { return 0 - 1 } 621 cp = VF_SURR_BASE + (cp - VF_SURR_HI) * VF_SURR_SPAN + (lo - VF_SURR_LO) 622 adv = 4 + 2 * VF_HEX_DIGITS 623 } } 624 if cp >= VF_SURR_LO { if cp < VF_SURR_END { return 0 - 1 } } 625 if cp < VF_J_SPACE { cp = VF_J_SPACE } 626 } 627 if cp < 0 { return 0 - 1 } 628 o = vf_utf8(out, o, cp) 629 i = i + adv 630 } 631 } 632 out[o] = 0 as u8 633 return o 634} 635 636// decode the DIRECT string member `key` of the object at obj into a fresh buffer sized from its own span. 637// Returns the buffer (NUL-terminated) or 0 when absent, not a string, or malformed; len[0] = decoded length or -1. 638func vf_json_get_str(src: *u8, n: i64, obj: i64, key: *u8, len: *i64) -> *u8 { 639 len[0] = 0 - 1 640 let box: *i64 = sys_mmap(VF_JM_N * 8) as *i64 641 let vs: i64 = vf_json_find(src, n, obj, key, box) 642 if vs < 0 { return 0 as *u8 } 643 if vf_json_kind(src, n, vs) != VF_JK_STRING { return 0 as *u8 } 644 let ve: i64 = box[VF_JM_VE] 645 let out: *u8 = sys_mmap(ve - vs + 1) 646 let d: i64 = vf_json_decode(src, vs + 1, ve - 1, out) 647 if d < 0 { return 0 as *u8 } 648 len[0] = d 649 return out 650} 651 652// extract the declared licenseType from a meta.json BODY (already read from the archive; method 0 653// = as-is, method 8 = raw deflate via the incumbent core). Writes a NUL-terminated label (max 40 654// chars, whitelist alphabet) or "unparsed". The body is the package DESCRIBING itself -- the one 655// member this lane ever opens. 656func vf_license(body: *u8, csize: i64, method: i64, usize: i64, out: *u8) -> i64 { 657 var src: *u8 = body 658 var n: i64 = csize 659 if method == 8 { 660 var cap: i64 = usize + 64 661 if cap < VF_MAGIC_1024 { cap = VF_MAGIC_1024 } 662 if cap > VF_MAGIC_4194304 { out[0] = 0 as u8; return 0 - 1 } 663 let dr: *NxDeflateResult = nx_deflate_inflate(body, csize, cap) 664 if (dr as i64) == 0 { out[0] = 0 as u8; return 0 - 1 } 665 if dr.error_code != 0 { out[0] = 0 as u8; return 0 - 1 } 666 src = dr.output_data 667 n = dr.output_size 668 } else { if method != 0 { out[0] = 0 as u8; return 0 - 1 } } 669 // find "licenseType" then the next quoted string 670 let key: *u8 = "licenseType" as *u8 671 var at: i64 = 0 - 1 672 var i: i64 = 0 673 while i + 11 <= n { 674 var k: i64 = 0 675 while k < 11 { if (src[i+k] as i64) != (key[k] as i64) { k = 20 } else { k = k + 1 } } 676 if k == 11 { at = i + 11; i = n + 1 } else { i = i + 1 } 677 } 678 if at < 0 { out[0] = 0 as u8; return 0 - 1 } 679 // the value: first ':' after the key, then the first '"' after that opens it 680 var colon: i64 = 0 - 1 681 i = at 682 while i < n { if (src[i] as i64) == 58 { colon = i; i = n + 1 } else { i = i + 1 } } 683 if colon < 0 { out[0] = 0 as u8; return 0 - 1 } 684 var vq: i64 = 0 - 1 685 i = colon + 1 686 while i < n { if (src[i] as i64) == 34 { vq = i + 1; i = n + 1 } else { i = i + 1 } } 687 if vq < 0 { out[0] = 0 as u8; return 0 - 1 } 688 var p: i64 = 0 689 i = vq 690 while i < n { 691 let c2: i64 = src[i] as i64 692 if c2 == 34 { out[p] = 0 as u8; return p } 693 var ok: i64 = 0 694 if c2 >= 48 { if c2 <= 57 { ok = 1 } } 695 if c2 >= 65 { if c2 <= 90 { ok = 1 } } 696 if c2 >= 97 { if c2 <= 122 { ok = 1 } } 697 if c2 == 32 { ok = 1 } 698 if c2 == 45 { ok = 1 } 699 if c2 == 46 { ok = 1 } 700 if ok == 0 { out[0] = 0 as u8; return 0 - 1 } 701 if p >= 40 { out[0] = 0 as u8; return 0 - 1 } 702 out[p] = c2 as u8 703 p = p + 1 704 i = i + 1 705 } 706 out[0] = 0 as u8 707 return 0 - 1 708}