code wiki / _hdl_build / nx_eco_graph_build.nx
nx_eco_graph_build.nx source
↩ module page · 224 lines · 10886 B
1// nx_eco_graph_build.nx -- R0 brick-3: the LIVE whole-tree walk that POPULATES the ecosystem graph from real
2// organs, then answers the operator's query. Composes nx_dr_tree's getdents recursion + nx_import_scan (accurate
3// edges) + nx_eco_graph (queries). Usage: nx_eco_graph_build <root-dir> [query-basename]
4// no query -> summary (nodes, edges, files, top hub, orphan count)
5// query X.nx -> its ROOTS-TO-GOD (transitive imports/ancestors), CHILDREN (transitive importers), Ca/Ce.
6// license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_eco_graph.nx"
9import "nx_import_scan.nx"
10const EGB_MAGIC_3900: i64 = 3900
11const EGB_MAGIC_131072: i64 = 131072
12const EGB_MAGIC_1024: i64 = 1024
13const EGB_MAGIC_4096: i64 = 4096
14
15const EGB_MAXNODE: i64 = 24000
16const EGB_MAXEDGE: i64 = 300000
17const EGB_ARENA: i64 = 4194304
18const EGB_HASH: i64 = 65536
19const EGB_FILECAP: i64 = 262144
20
21func bw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
22func bn(v: i64) -> i64 { let b: *u8=sys_mmap(24); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} let t: *u8=sys_mmap(24); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var j: i64=0; while j<k{b[j]=t[k-1-j];j=j+1} sys_write(1,b,k); return 0 }
23func bslen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
24
25func egb_is_nx(nm: *u8, n: i64) -> i64 {
26 if n < 4 { return 0 }
27 if nm[n-3] != (46 as u8) { return 0 }
28 if nm[n-2] != (110 as u8) { return 0 }
29 if nm[n-1] != (120 as u8) { return 0 }
30 return 1
31}
32// append "/name" to path at base_n; return new length (NOT NUL-terminated here).
33func egb_join(path: *u8, base_n: i64, nm: *u8) -> i64 {
34 path[base_n] = 47 as u8
35 var o: i64 = base_n + 1
36 var i: i64 = 0
37 while nm[i] != (0 as u8) { path[o] = nm[i]; o = o + 1; i = i + 1 }
38 return o
39}
40func egb_read(path: *u8, buf: *u8, cap: i64) -> i64 {
41 let fd: i64 = sys_openat_rd(path)
42 if fd < 0 { return 0 }
43 var total: i64 = 0
44 var go: i64 = 1
45 while go == 1 {
46 let nr: i64 = sys_read(fd, ((buf as i64)+total) as *u8, cap - total)
47 if nr <= 0 { go = 0 } else { total = total + nr; if total >= cap { go = 0 } }
48 }
49 sys_close(fd)
50 return total
51}
52func egb_isdotdot(nm: *u8) -> i64 {
53 if nm[0] == (46 as u8) { if nm[1] == (0 as u8) { return 1 } if nm[1] == (46 as u8) { if nm[2] == (0 as u8) { return 1 } } }
54 return 0
55}
56// recursive walk: st[0]=files scanned, st[1]=edges added
57func egb_walk(g: *EcoGraph, path: *u8, path_n: i64, filebuf: *u8, aoff: *i64, alen: *i64, st: *i64) -> i64 {
58 if path_n > EGB_MAGIC_3900 { return 0 }
59 path[path_n] = 0 as u8
60 let fd: i64 = sys_openat_rd(path)
61 if fd < 0 { return 0 }
62 let dbuf: *u8 = sys_mmap(EGB_MAGIC_131072)
63 var go: i64 = 1
64 while go == 1 {
65 let nr: i64 = sys_getdents64(fd, dbuf, EGB_MAGIC_131072)
66 if nr <= 0 { go = 0 } else {
67 var off: i64 = 0
68 while off < nr {
69 let rec: *u8 = ((dbuf as i64) + off) as *u8
70 let ty: i64 = dirent_type(rec)
71 let nm: *u8 = dirent_name(rec)
72 if egb_isdotdot(nm) == 0 {
73 let cs: i64 = egb_join(path, path_n, nm)
74 if ty == 4 {
75 egb_walk(g, path, cs, filebuf, aoff, alen, st)
76 } else {
77 let nmn: i64 = bslen(nm)
78 if egb_is_nx(nm, nmn) == 1 {
79 let fnode: i64 = eg_intern(g, nm, nmn)
80 if fnode >= 0 {
81 path[cs] = 0 as u8
82 let flen: i64 = egb_read(path, filebuf, EGB_FILECAP)
83 if flen > 0 {
84 let ic: i64 = nis_scan(filebuf, flen, aoff, alen, EGB_MAGIC_1024)
85 var k: i64 = 0
86 while k < ic {
87 // basename of the import path (strip dir prefix)
88 let io: i64 = aoff[k]
89 let il: i64 = alen[k]
90 var bs: i64 = io
91 var j: i64 = io
92 while j < io + il { if filebuf[j] == (47 as u8) { bs = j + 1 } j = j + 1 }
93 let bl: i64 = (io + il) - bs
94 let inode: i64 = eg_intern(g, ((filebuf as i64)+bs) as *u8, bl)
95 if inode >= 0 { eg_add_edge(g, fnode, inode); st[1] = st[1] + 1 }
96 k = k + 1
97 }
98 st[0] = st[0] + 1
99 }
100 }
101 }
102 }
103 }
104 off = off + dirent_reclen(rec)
105 }
106 }
107 }
108 sys_close(fd)
109 sys_munmap(dbuf, EGB_MAGIC_131072)
110 return 0
111}
112
113func egb_printname(g: *EcoGraph, idx: i64) -> i64 {
114 let off: i64 = g.node_off[idx]
115 var i: i64 = 0
116 while g.arena[off+i] != (0 as u8) { i = i + 1 }
117 sys_write(1, ((g.arena as i64)+off) as *u8, i)
118 return 0
119}
120
121func bq() -> i64 { let c: *u8 = sys_mmap(1); c[0] = 34 as u8; sys_write(1, c, 1); return 0 }
122func egb_jname(g: *EcoGraph, idx: i64) -> i64 { bq(); egb_printname(g, idx); bq(); return 0 }
123// R1-b: emit the query result as JSON (the API/MCP response contract) from a LOADED store.
124func egb_json_query(g: *EcoGraph, q: *u8) -> i64 {
125 let qi: i64 = eg_find(g, q, bslen(q))
126 bw("{" as *u8); bq(); bw("node" as *u8); bq(); bw(":" as *u8); bq(); bw(q); bq()
127 if qi < 0 { bw("," as *u8); bq(); bw("found" as *u8); bq(); bw(":false}\n" as *u8); return 0 }
128 bw("," as *u8); bq(); bw("found" as *u8); bq(); bw(":true," as *u8); bq(); bw("ca" as *u8); bq(); bw(":" as *u8); bn(eg_ca(g, qi))
129 bw("," as *u8); bq(); bw("ce" as *u8); bq(); bw(":" as *u8); bn(eg_ce(g, qi))
130 bw("," as *u8); bq(); bw("orphan" as *u8); bq(); bw(":" as *u8)
131 if eg_ca(g, qi) == 0 { bw("true" as *u8) } else { bw("false" as *u8) }
132 let vis: *u8 = sys_mmap(EGB_MAXNODE)
133 let out: *i64 = sys_mmap(EGB_MAXNODE*8) as *i64
134 let on: *i64 = sys_mmap(8) as *i64
135 var z: i64 = 0
136 while z < g.node_count { vis[z] = 0 as u8; z = z + 1 }
137 on[0] = 0; vis[qi] = 1 as u8
138 eg_ancestors(g, qi, vis, out, on, EGB_MAXNODE)
139 bw("," as *u8); bq(); bw("roots_to_god" as *u8); bq(); bw(":[" as *u8)
140 var k: i64 = 0
141 while k < on[0] { if k > 0 { bw("," as *u8) } egb_jname(g, out[k]); k = k + 1 }
142 bw("]" as *u8)
143 z = 0
144 while z < g.node_count { vis[z] = 0 as u8; z = z + 1 }
145 on[0] = 0; vis[qi] = 1 as u8
146 eg_descendants(g, qi, vis, out, on, EGB_MAXNODE)
147 bw("," as *u8); bq(); bw("children_count" as *u8); bq(); bw(":" as *u8); bn(on[0])
148 bw("," as *u8); bq(); bw("children" as *u8); bq(); bw(":[" as *u8)
149 k = 0
150 while k < on[0] { if k < 200 { if k > 0 { bw("," as *u8) } egb_jname(g, out[k]) } k = k + 1 }
151 bw("]}\n" as *u8)
152 return 0
153}
154
155func main(argc: i64, argv: *i64) -> i64 {
156 if argc < 2 { bw("usage: nx_eco_graph_build <root> [query] [save-prefix] | --load <store-prefix> <query>\n" as *u8); return 2 }
157 let a1: *u8 = argv[1] as *u8
158 // ---- QUERY-ONLY from the SOVEREIGN seg_store (no walk; pure JSON = the API/MCP entry) ----
159 if a1[0] == (45 as u8) {
160 if argc < 4 { bw("usage: nx_eco_graph_build --load <store-prefix> <query>\n" as *u8); return 2 }
161 let gl: *EcoGraph = eg_load(argv[2] as *u8)
162 if (gl as i64) == 0 { bw("{" as *u8); bq(); bw("error" as *u8); bq(); bw(":" as *u8); bq(); bw("store_not_found" as *u8); bq(); bw("}\n" as *u8); return 3 }
163 egb_json_query(gl, argv[3] as *u8)
164 return 0
165 }
166 let root: *u8 = argv[1] as *u8
167 let g: *EcoGraph = eg_new(EGB_MAXNODE, EGB_MAXEDGE, EGB_ARENA, EGB_HASH)
168 let path: *u8 = sys_mmap(EGB_MAGIC_4096)
169 var rn: i64 = 0
170 while root[rn] != (0 as u8) { path[rn] = root[rn]; rn = rn + 1 }
171 let filebuf: *u8 = sys_mmap(EGB_FILECAP)
172 let aoff: *i64 = sys_mmap(EGB_MAGIC_1024*8) as *i64
173 let alen: *i64 = sys_mmap(EGB_MAGIC_1024*8) as *i64
174 let st: *i64 = sys_mmap(16) as *i64
175 st[0] = 0; st[1] = 0
176 bw("=== nx_eco_graph_build: living ecosystem graph over " as *u8); bw(root); bw(" ===\n" as *u8)
177 egb_walk(g, path, rn, filebuf, aoff, alen, st)
178 eg_finalize(g) // R0b: CSR adjacency + degree arrays -> O(1) coupling, O(reachable) traversals at 16k scale
179 bw(" nodes=" as *u8); bn(g.node_count); bw(" edges=" as *u8); bn(g.edge_count); bw(" files_scanned=" as *u8); bn(st[0]); bw("\n" as *u8)
180 if argc >= 4 { eg_save(g, argv[3] as *u8); bw(" SAVED to sovereign seg_store: " as *u8); bw(argv[3] as *u8); bw("\n" as *u8) }
181
182 // orphan count (Ca==0) + top hub (max Ca)
183 var orphans: i64 = 0
184 var hub: i64 = 0
185 var hubca: i64 = 0
186 var i: i64 = 0
187 while i < g.node_count {
188 let ca: i64 = eg_ca(g, i)
189 if ca == 0 { orphans = orphans + 1 }
190 if ca > hubca { hubca = ca; hub = i }
191 i = i + 1
192 }
193 bw(" top hub (most imported): " as *u8); egb_printname(g, hub); bw(" (Ca=" as *u8); bn(hubca); bw(") Ca==0 nodes=" as *u8); bn(orphans); bw(" (entry-points + orphans)\n" as *u8)
194
195 if argc >= 3 {
196 let q: *u8 = argv[2] as *u8
197 let qi: i64 = eg_intern(g, q, bslen(q))
198 bw("\n QUERY " as *u8); bw(q); bw(" Ca(importers)=" as *u8); bn(eg_ca(g, qi)); bw(" Ce(imports)=" as *u8); bn(eg_ce(g, qi)); bw("\n" as *u8)
199 let vis: *u8 = sys_mmap(EGB_MAXNODE)
200 let out: *i64 = sys_mmap(EGB_MAXNODE*8) as *i64
201 let on: *i64 = sys_mmap(8) as *i64
202 // roots-to-god
203 var z: i64 = 0
204 while z < g.node_count { vis[z] = 0 as u8; z = z + 1 }
205 on[0] = 0; vis[qi] = 1 as u8
206 eg_ancestors(g, qi, vis, out, on, EGB_MAXNODE)
207 bw(" ROOTS-TO-GOD (" as *u8); bn(on[0]); bw(" transitive ancestors):\n " as *u8)
208 var k: i64 = 0
209 while k < on[0] { egb_printname(g, out[k]); bw(" " as *u8); k = k + 1 }
210 bw("\n" as *u8)
211 // children-to-newest
212 z = 0
213 while z < g.node_count { vis[z] = 0 as u8; z = z + 1 }
214 on[0] = 0; vis[qi] = 1 as u8
215 eg_descendants(g, qi, vis, out, on, EGB_MAXNODE)
216 bw(" CHILDREN-TO-NEWEST (" as *u8); bn(on[0]); bw(" transitive importers):\n " as *u8)
217 k = 0
218 while k < on[0] { if k < 40 { egb_printname(g, out[k]); bw(" " as *u8) } k = k + 1 }
219 if on[0] > 40 { bw("... (+" as *u8); bn(on[0]-40); bw(" more)" as *u8) }
220 bw("\n" as *u8)
221 }
222 bw("=== eco-graph built (living: re-run to refresh; brick-3 live) ===\n" as *u8)
223 return 0
224}