nx_modelfacts.nx source
↩ module page · 259 lines · 11011 B
1// nx_modelfacts.nx -- CLI for 3D model fact extraction into the refcorpus measured-facts plane.
2//
3// nx_modelfacts probe <file>
4// nx_modelfacts sweep <listfile> <source> <provenance> <license> [put]
5//
6// The nx_mediafacts pattern applied to the model classes (GLB/STL/OBJ/PLY): bounded window read
7// + a DRAIN pass so the true file size is known (binary STL detection and truncation honesty
8// both need it), closed fact vocabulary, distribution rows through rc_put_row in-process so the
9// k-anonymity floor physically gates what reaches the journal.
10// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
11import "nx_syscalls.nx"
12import "nx_modelfacts_lib.nx"
13import "nx_mediafacts_lib.nx" // mf_median (sorts in place -- min/max only valid AFTER the call)
14import "nx_refcorpus.nx"
15const MDLC_MAGIC_65536: i64 = 65536
16
17const MDLC_WINDOW: i64 = 4194304
18const MDLC_MAX_FILES: i64 = 4096
19const MDLC_PATH_MAX: i64 = 384
20
21func mdlc_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
22func mdlc_w(s: *u8) -> i64 { sys_write(1, s, mdlc_slen(s)); return 0 }
23func mdlc_e(s: *u8) -> i64 { sys_write(2, s, mdlc_slen(s)); return 0 }
24func mdlc_num(v: i64) -> i64 {
25 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
26 var m: i64 = v
27 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
28 let t: *u8 = sys_mmap(32)
29 var k: i64 = 0
30 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
31 let o: *u8 = sys_mmap(32)
32 var i: i64 = 0
33 while i < k { o[i] = t[k - 1 - i]; i = i + 1 }
34 sys_write(1, o, k)
35 return 0
36}
37func mdlc_itoa(v: i64, out: *u8) -> i64 {
38 var m: i64 = v
39 var p: i64 = 0
40 if m < 0 { out[p] = 45 as u8; p = p + 1; m = 0 - m }
41 if m == 0 { out[p] = 48 as u8; out[p+1] = 0 as u8; return p + 1 }
42 let t: *u8 = sys_mmap(32)
43 var k: i64 = 0
44 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
45 var i: i64 = 0
46 while i < k { out[p] = t[k - 1 - i]; p = p + 1; i = i + 1 }
47 out[p] = 0 as u8
48 return p
49}
50func mdlc_fmtname(f: i64) -> *u8 {
51 if f == MDL_FMT_GLB { return "glb" as *u8 }
52 if f == MDL_FMT_STL { return "stl" as *u8 }
53 if f == MDL_FMT_OBJ { return "obj" as *u8 }
54 if f == MDL_FMT_PLY { return "ply" as *u8 }
55 return "unknown" as *u8
56}
57
58// window read + DRAIN: buf gets the first MDLC_WINDOW bytes, sizebox[0] the TRUE size (the drain
59// counts the tail without storing it). Returns bytes in buf, or -1 on open failure.
60func mdlc_read(path: *u8, buf: *u8, sizebox: *i64) -> i64 {
61 let fd: i64 = sys_openat_rd(path)
62 if fd < 0 { return 0 - 1 }
63 var got: i64 = 0
64 var stop: i64 = 0
65 while stop == 0 {
66 if got >= MDLC_WINDOW { stop = 1 } else {
67 let k: i64 = sys_read(fd, ((buf as i64) + got) as *u8, MDLC_WINDOW - got)
68 if k <= 0 { stop = 1 } else { got = got + k }
69 }
70 }
71 var total: i64 = got
72 if got >= MDLC_WINDOW {
73 let scratch: *u8 = sys_mmap(MDLC_MAGIC_65536)
74 var d: i64 = 1
75 while d > 0 { d = sys_read(fd, scratch, MDLC_MAGIC_65536); if d > 0 { total = total + d } }
76 }
77 sys_close(fd)
78 sizebox[0] = total
79 return got
80}
81
82func mdlc_print_probe(facts: *i64) -> i64 {
83 mdlc_w("MODELFACT format=" as *u8); mdlc_w(mdlc_fmtname(facts[0]))
84 if facts[1] >= 0 { mdlc_w(" tris=" as *u8); mdlc_num(facts[1]) }
85 if facts[2] >= 0 { mdlc_w(" verts=" as *u8); mdlc_num(facts[2]) }
86 if facts[3] >= 0 { mdlc_w(" faces=" as *u8); mdlc_num(facts[3]) }
87 if facts[4] >= 0 { mdlc_w(" uv_present=" as *u8); mdlc_num(facts[4]) }
88 if facts[5] >= 0 { mdlc_w(" glb_json_bytes=" as *u8); mdlc_num(facts[5]) }
89 if facts[6] >= 0 { mdlc_w(" glb_bin_bytes=" as *u8); mdlc_num(facts[6]) }
90 if facts[7] >= 0 { mdlc_w(" glb_accessors=" as *u8); mdlc_num(facts[7]) }
91 if facts[8] >= 0 { mdlc_w(" glb_skinned_prims=" as *u8); mdlc_num(facts[8]) }
92 if facts[9] >= 0 { mdlc_w(" vrm_present=" as *u8); mdlc_num(facts[9]) }
93 mdlc_w("\n" as *u8)
94 return 0
95}
96
97func mdlc_row(fmt: i64, metric: *u8, val: i64, unit: *u8, cnt: i64, src: *u8, prov: *u8, lic: *u8, do_put: i64, wrote: *i64) -> i64 {
98 let key: *u8 = sys_mmap(160)
99 var p: i64 = 0
100 let fn: *u8 = mdlc_fmtname(fmt)
101 var i: i64 = 0
102 while fn[i] != (0 as u8) { key[p] = fn[i]; p = p + 1; i = i + 1 }
103 key[p] = 95 as u8; p = p + 1
104 i = 0
105 while metric[i] != (0 as u8) { key[p] = metric[i]; p = p + 1; i = i + 1 }
106 key[p] = 0 as u8
107 mdlc_w("MODELFACT-DIST " as *u8); mdlc_w(key); mdlc_w("=" as *u8); mdlc_num(val)
108 mdlc_w(" unit=" as *u8); mdlc_w(unit); mdlc_w(" n=" as *u8); mdlc_num(cnt); mdlc_w("\n" as *u8)
109 if do_put == 0 { return 0 }
110 let vs: *u8 = sys_mmap(32)
111 mdlc_itoa(val, vs)
112 let r: i64 = rc_put_row(src, key, vs, unit, cnt, prov, lic)
113 if r == 0 { wrote[0] = wrote[0] + 1 }
114 return r
115}
116
117// min/median/max rows for one metric's sample array (mf_median sorts first -- T6 law)
118func mdlc_mmm(fmt: i64, base: *u8, arr: *i64, cnt: i64, unit: *u8, src: *u8, prov: *u8, lic: *u8, do_put: i64, wrote: *i64) -> i64 {
119 if cnt <= 0 { return 0 }
120 let med: i64 = mf_median(arr, cnt)
121 let kmin: *u8 = sys_mmap(96)
122 let kmed: *u8 = sys_mmap(96)
123 let kmax: *u8 = sys_mmap(96)
124 var p: i64 = 0
125 var i: i64 = 0
126 while base[i] != (0 as u8) { kmin[p] = base[i]; kmed[p] = base[i]; kmax[p] = base[i]; p = p + 1; i = i + 1 }
127 let smin: *u8 = "_min" as *u8
128 let smed: *u8 = "_median" as *u8
129 let smax: *u8 = "_max" as *u8
130 i = 0; var q: i64 = p
131 while smin[i] != (0 as u8) { kmin[q] = smin[i]; q = q + 1; i = i + 1 }
132 kmin[q] = 0 as u8
133 i = 0; q = p
134 while smed[i] != (0 as u8) { kmed[q] = smed[i]; q = q + 1; i = i + 1 }
135 kmed[q] = 0 as u8
136 i = 0; q = p
137 while smax[i] != (0 as u8) { kmax[q] = smax[i]; q = q + 1; i = i + 1 }
138 kmax[q] = 0 as u8
139 mdlc_row(fmt, kmin, arr[0], unit, cnt, src, prov, lic, do_put, wrote)
140 mdlc_row(fmt, kmed, med, unit, cnt, src, prov, lic, do_put, wrote)
141 mdlc_row(fmt, kmax, arr[cnt-1], unit, cnt, src, prov, lic, do_put, wrote)
142 return 0
143}
144
145func main(argc: i64, argv: *i64) -> i64 {
146 if argc < 3 {
147 mdlc_e("usage: nx_modelfacts probe <file>\n" as *u8)
148 mdlc_e(" nx_modelfacts sweep <listfile> <source> <provenance> <license> [put]\n" as *u8)
149 sys_exit(2)
150 return 2
151 }
152 let verb: *u8 = argv[1] as *u8
153 let buf: *u8 = sys_mmap(MDLC_WINDOW)
154 let sizebox: *i64 = sys_mmap(16) as *i64
155
156 if verb[0] == (112 as u8) { // probe
157 let n: i64 = mdlc_read(argv[2] as *u8, buf, sizebox)
158 if n < 0 { mdlc_e("ERROR: cannot open file\n" as *u8); sys_exit(1); return 1 }
159 let facts: *i64 = sys_mmap(MDL_N_SLOTS * 8) as *i64
160 mdl_probe(buf, n, sizebox[0], facts)
161 mdlc_print_probe(facts)
162 sys_exit(0)
163 return 0
164 }
165
166 if argc < 6 { mdlc_e("sweep needs <listfile> <source> <provenance> <license> [put]\n" as *u8); sys_exit(2); return 2 }
167 let src: *u8 = argv[3] as *u8
168 let prov: *u8 = argv[4] as *u8
169 let lic: *u8 = argv[5] as *u8
170 var do_put: i64 = 0
171 if argc >= 7 { let pv: *u8 = argv[6] as *u8; if pv[0] == (112 as u8) { do_put = 1 } }
172
173 let lp: *i64 = sys_mmap(16) as *i64
174 let lst: *u8 = sys_read_file(argv[2] as *u8, lp)
175 if (lst as i64) == 0 { mdlc_e("ERROR: cannot read listfile\n" as *u8); sys_exit(1); return 1 }
176 let ln: i64 = lp[0]
177
178 // per-format primary-metric samples: glb=accessors, stl=tris, obj/ply=verts
179 let samp: *i64 = sys_mmap(5 * MDLC_MAX_FILES * 8) as *i64
180 let cnt: *i64 = sys_mmap(5 * 8) as *i64
181 var ci: i64 = 0
182 while ci < 5 { cnt[ci] = 0; ci = ci + 1 }
183 var glb_skinned: i64 = 0
184 var glb_vrm: i64 = 0
185 var glb_uv: i64 = 0
186 var glb_total: i64 = 0
187
188 let path: *u8 = sys_mmap(MDLC_PATH_MAX + 8)
189 let facts: *i64 = sys_mmap(MDL_N_SLOTS * 8) as *i64
190 var pos: i64 = 0
191 var files: i64 = 0
192 var skipped: i64 = 0
193 while pos < ln {
194 var pl: i64 = 0
195 var eol: i64 = 0
196 while eol == 0 {
197 if pos >= ln { eol = 1 } else {
198 let c: i64 = lst[pos] as i64 & 0xff
199 pos = pos + 1
200 if c == 10 { eol = 1 } else {
201 if c != 13 { if pl < MDLC_PATH_MAX { path[pl] = c as u8; pl = pl + 1 } }
202 }
203 }
204 }
205 path[pl] = 0 as u8
206 if pl > 0 { if files < MDLC_MAX_FILES {
207 let n2: i64 = mdlc_read(path, buf, sizebox)
208 if n2 <= 0 { skipped = skipped + 1 } else {
209 mdl_probe(buf, n2, sizebox[0], facts)
210 let f: i64 = facts[0]
211 files = files + 1
212 if f != MDL_FMT_UNKNOWN {
213 if f == MDL_FMT_GLB { glb_total = glb_total + 1
214 if facts[8] > 0 { glb_skinned = glb_skinned + 1 }
215 if facts[9] > 0 { glb_vrm = glb_vrm + 1 }
216 if facts[4] > 0 { glb_uv = glb_uv + 1 } }
217 var metric: i64 = 0 - 1
218 if f == MDL_FMT_GLB { metric = facts[7] }
219 if f == MDL_FMT_STL { metric = facts[1] }
220 if f == MDL_FMT_OBJ { metric = facts[2] }
221 if f == MDL_FMT_PLY { metric = facts[2] }
222 if metric >= 0 { let k: i64 = cnt[f]
223 if k < MDLC_MAX_FILES { samp[f * MDLC_MAX_FILES + k] = metric; cnt[f] = k + 1 } }
224 }
225 }
226 } }
227 }
228
229 mdlc_w("MODELFACTS-SWEEP files=" as *u8); mdlc_num(files)
230 mdlc_w(" unreadable=" as *u8); mdlc_num(skipped); mdlc_w("\n" as *u8)
231
232 let wrote: *i64 = sys_mmap(16) as *i64
233 wrote[0] = 0
234 var fmt: i64 = 1
235 while fmt <= 4 {
236 let c2: i64 = cnt[fmt]
237 if c2 > 0 {
238 mdlc_row(fmt, "files_measured" as *u8, c2, "files" as *u8, c2, src, prov, lic, do_put, wrote)
239 var mname: *u8 = "verts" as *u8
240 var munit: *u8 = "verts" as *u8
241 if fmt == MDL_FMT_GLB { mname = "accessors" as *u8; munit = "accessors" as *u8 }
242 if fmt == MDL_FMT_STL { mname = "tris" as *u8; munit = "tris" as *u8 }
243 mdlc_mmm(fmt, mname, ((samp as i64) + fmt * MDLC_MAX_FILES * 8) as *i64, c2, munit, src, prov, lic, do_put, wrote)
244 }
245 fmt = fmt + 1
246 }
247 if glb_total > 0 {
248 mdlc_row(MDL_FMT_GLB, "skinned_files" as *u8, glb_skinned, "files" as *u8, glb_total, src, prov, lic, do_put, wrote)
249 mdlc_row(MDL_FMT_GLB, "vrm_files" as *u8, glb_vrm, "files" as *u8, glb_total, src, prov, lic, do_put, wrote)
250 mdlc_row(MDL_FMT_GLB, "uv_files" as *u8, glb_uv, "files" as *u8, glb_total, src, prov, lic, do_put, wrote)
251 }
252
253 if do_put == 1 {
254 mdlc_w("MODELFACTS-PUT rows_admitted=" as *u8); mdlc_num(wrote[0]); mdlc_w("\n" as *u8)
255 if wrote[0] == 0 { mdlc_e("MODELFACTS-REFUSED-ALL nothing met the admission walls (bucket under the k=8 floor?)\n" as *u8); sys_exit(3); return 3 }
256 }
257 sys_exit(0)
258 return 0
259}