code wiki / (root) / nx_mhtarget_lib.nx

nx_mhtarget_lib.nx source

↩ module page · 224 lines · 8469 B

1// nx_mhtarget_lib.nx -- MAKEHUMAN TARGET FACT EXTRACTION CORE (the CC0 morph-target dialect; 2// /compare/koikatsu card-class lane, admitted 2026-08-30). A MakeHuman .target is a text file: 3// comment lines starting with a hash byte, blank lines, and DATA rows of exactly four 4// whitespace-separated tokens -- vertex index (uint) then dx dy dz as signed decimals which may 5// carry a LEADING DOT (measured on the real corpus 2026-08-30: 1675 rows, 4 fields each, max 3 6// fractional digits, 637 negatives, no exponents, max index 18001). 7// SCHEMA GROUND TRUTH is the fetched real corpus itself (makehumancommunity/makehuman, assets CC0 8// per LICENSE.ASSETS.md and each file's own header) -- the licence-clean control oracle beside the 9// per-author BodySlide/Nexus lane. Offsets decode to MICRO units (value x 1000000, integer, sign 10// carried, up to six fractional digits kept, extras declared truncated -- numbers, not verdicts). 11// Byte access composes kk_b from the sibling dialect lib; line walking is this dialect's own, and 12// every scan is a BOUNDED FLAG LOOP -- a loop-exit sentinel written into the cursor erases the 13// answer (standing law; the first draft of this very file committed it and was rewritten). 14// Slots: [0] is_target [1] data_rows [2] comment_rows [3] blank_rows [4] max_vert_index 15// [5] bad_rows [6..9] reserved -1 16// license_tier: ORIGINAL No hw writes (Rule 26). 17import "nx_syscalls.nx" 18import "nx_kkfacts_lib.nx" 19 20const MHT_N_SLOTS: i64 = 10 21const MHT_MICRO: i64 = 1000000 22const MHT_FRAC_KEEP: i64 = 6 // fractional digits kept; beyond this consumed but truncated (declared) 23const MHT_BAD: i64 = 0 - 1000000000000 // impossible offset (-1e6 model units); decode stops on it 24const MHT_CH_HASH: i64 = 35 25const MHT_CH_NL: i64 = 10 26const MHT_CH_CR: i64 = 13 27const MHT_CH_SP: i64 = 32 28const MHT_CH_TAB: i64 = 9 29const MHT_CH_MINUS: i64 = 45 30const MHT_CH_DOT: i64 = 46 31const MHT_CH_0: i64 = 48 32const MHT_CH_9: i64 = 57 33 34// row classification codes 35const MHT_ROW_COMMENT: i64 = 0 36const MHT_ROW_BLANK: i64 = 1 37const MHT_ROW_DATA: i64 = 2 38const MHT_ROW_BAD: i64 = 3 39 40func mht_is_ws(c: i64) -> i64 { if c == MHT_CH_SP { return 1 } if c == MHT_CH_TAB { return 1 } if c == MHT_CH_CR { return 1 } return 0 } 41func mht_is_digit(c: i64) -> i64 { if c < MHT_CH_0 { return 0 } if c > MHT_CH_9 { return 0 } return 1 } 42 43// bounded whitespace skip: first non-ws position at or after p, never past le 44func mht_skip_ws(buf: *u8, p: i64, le: i64) -> i64 { 45 var q: i64 = p 46 var moving: i64 = 1 47 while moving == 1 { 48 if q >= le { moving = 0 } else { 49 if mht_is_ws(kk_b(buf, q)) == 1 { q = q + 1 } else { moving = 0 } 50 } 51 } 52 return q 53} 54// bounded token end: first ws position at or after p, never past le 55func mht_tok_end(buf: *u8, p: i64, le: i64) -> i64 { 56 var q: i64 = p 57 var moving: i64 = 1 58 while moving == 1 { 59 if q >= le { moving = 0 } else { 60 if mht_is_ws(kk_b(buf, q)) == 0 { q = q + 1 } else { moving = 0 } 61 } 62 } 63 return q 64} 65// bounded line end: position of the newline at or after ls, or end when none remains 66func mht_line_end(buf: *u8, ls: i64, end: i64) -> i64 { 67 var q: i64 = ls 68 var moving: i64 = 1 69 while moving == 1 { 70 if q >= end { moving = 0 } else { 71 if kk_b(buf, q) == MHT_CH_NL { moving = 0 } else { q = q + 1 } 72 } 73 } 74 return q 75} 76 77// parse an unsigned integer token; -1 on any non-digit or empty 78func mht_uint(buf: *u8, off: i64, len: i64) -> i64 { 79 if len <= 0 { return 0 - 1 } 80 var v: i64 = 0 81 var i: i64 = 0 82 while i < len { 83 let c: i64 = kk_b(buf, off + i) 84 if mht_is_digit(c) == 0 { return 0 - 1 } 85 v = v * 10 + (c - MHT_CH_0) 86 i = i + 1 87 } 88 return v 89} 90 91// parse a signed decimal token (leading dot allowed) to MICRO units; MHT_BAD on malformation. 92func mht_num_micro(buf: *u8, off: i64, len: i64) -> i64 { 93 if len <= 0 { return MHT_BAD } 94 var i: i64 = 0 95 var neg: i64 = 0 96 var whole: i64 = 0 97 var frac: i64 = 0 98 var place: i64 = MHT_MICRO / 10 // first fractional digit is worth 100000 micro 99 var dot: i64 = 0 100 var digits: i64 = 0 101 while i < len { 102 let c: i64 = kk_b(buf, off + i) 103 if c == MHT_CH_MINUS { 104 if i != 0 { return MHT_BAD } 105 neg = 1 106 } else { if c == MHT_CH_DOT { 107 if dot == 1 { return MHT_BAD } 108 dot = 1 109 } else { if mht_is_digit(c) == 0 { return MHT_BAD } else { 110 digits = digits + 1 111 if dot == 0 { whole = whole * 10 + (c - MHT_CH_0) } 112 if dot == 1 { if place > 0 { frac = frac + (c - MHT_CH_0) * place; place = place / 10 } } 113 } } } 114 i = i + 1 115 } 116 if digits == 0 { return MHT_BAD } 117 var v: i64 = whole * MHT_MICRO + frac 118 if neg == 1 { return 0 - v } 119 return v 120} 121 122// classify + parse ONE line [ls,le). On DATA writes out4[0]=vert_index, out4[1..3]=micro offsets. 123func mht_row(buf: *u8, ls: i64, le: i64, out4: *i64) -> i64 { 124 var p: i64 = mht_skip_ws(buf, ls, le) 125 if p >= le { return MHT_ROW_BLANK } 126 if kk_b(buf, p) == MHT_CH_HASH { return MHT_ROW_COMMENT } 127 var t: i64 = 0 128 while t < 4 { 129 p = mht_skip_ws(buf, p, le) 130 if p >= le { return MHT_ROW_BAD } 131 let e: i64 = mht_tok_end(buf, p, le) 132 if t == 0 { 133 let vi: i64 = mht_uint(buf, p, e - p) 134 if vi < 0 { return MHT_ROW_BAD } 135 out4[0] = vi 136 } else { 137 let mv: i64 = mht_num_micro(buf, p, e - p) 138 if mv == MHT_BAD { return MHT_ROW_BAD } 139 out4[t] = mv 140 } 141 p = e 142 t = t + 1 143 } 144 p = mht_skip_ws(buf, p, le) 145 if p < le { return MHT_ROW_BAD } 146 return MHT_ROW_DATA 147} 148 149// probe core: buf holds the first n bytes; file_size = true size. Census only over a COMPLETE 150// file; a truncated read decides is_target from the complete-line PREFIX and abstains (-1) from 151// every count -- a truncated count is an undercount. 152func mht_probe(buf: *u8, n: i64, file_size: i64, facts: *i64) -> i64 { 153 var i: i64 = 0 154 while i < MHT_N_SLOTS { facts[i] = 0 - 1; i = i + 1 } 155 facts[0] = 0 156 if n <= 0 { return 0 } 157 var complete: i64 = 0 158 if file_size <= n { complete = 1 } 159 var scan_end: i64 = n 160 if complete == 0 { 161 // retreat to the last complete line, boundedly 162 var q: i64 = n - 1 163 var found: i64 = 0 - 1 164 var moving: i64 = 1 165 while moving == 1 { 166 if q < 0 { moving = 0 } else { 167 if kk_b(buf, q) == MHT_CH_NL { found = q; moving = 0 } else { q = q - 1 } 168 } 169 } 170 if found < 0 { return 0 } 171 scan_end = found + 1 172 } 173 let out4: *i64 = sys_mmap(32) as *i64 174 var rows: i64 = 0 175 var comments: i64 = 0 176 var blanks: i64 = 0 177 var bad: i64 = 0 178 var maxv: i64 = 0 - 1 179 var ls: i64 = 0 180 while ls < scan_end { 181 let eol: i64 = mht_line_end(buf, ls, scan_end) 182 let cls: i64 = mht_row(buf, ls, eol, out4) 183 if cls == MHT_ROW_DATA { rows = rows + 1; if out4[0] > maxv { maxv = out4[0] } } 184 if cls == MHT_ROW_COMMENT { comments = comments + 1 } 185 if cls == MHT_ROW_BLANK { blanks = blanks + 1 } 186 if cls == MHT_ROW_BAD { bad = bad + 1 } 187 ls = eol + 1 188 } 189 if rows >= 1 { if bad == 0 { facts[0] = 1 } } 190 if complete == 1 { 191 facts[1] = rows 192 facts[2] = comments 193 facts[3] = blanks 194 facts[4] = maxv 195 facts[5] = bad 196 } 197 return 0 198} 199 200// decode all data rows, honest-partial: meta[0] = candidate rows encountered (non-comment, 201// non-blank); a BAD row STOPS the parse so parsed < declared is visible, never papered over. 202func mht_decode(buf: *u8, n: i64, idxs: *i64, dxs: *i64, dys: *i64, dzs: *i64, outcap: i64, meta: *i64) -> i64 { 203 meta[0] = 0 204 let out4: *i64 = sys_mmap(32) as *i64 205 var parsed: i64 = 0 206 var ls: i64 = 0 207 while ls < n { 208 let eol: i64 = mht_line_end(buf, ls, n) 209 let cls: i64 = mht_row(buf, ls, eol, out4) 210 if cls == MHT_ROW_BAD { meta[0] = meta[0] + 1; return parsed } 211 if cls == MHT_ROW_DATA { 212 meta[0] = meta[0] + 1 213 if parsed < outcap { 214 idxs[parsed] = out4[0] 215 dxs[parsed] = out4[1] 216 dys[parsed] = out4[2] 217 dzs[parsed] = out4[3] 218 } 219 parsed = parsed + 1 220 } 221 ls = eol + 1 222 } 223 return parsed 224}