code wiki / _hdl_build / nx_galx_sortindex.nx
nx_galx_sortindex.nx source
↩ module page · 468 lines · 23664 B
1// nx_galx_sortindex.nx -- LIBRARY (no main -> build the _gate): build a SORT-INDEX over the gallery's
2// recordings so the daemon can SORT BY an integer key (runtime, file-size, view-count, rating) at
3// O(1)-per-page instead of re-sorting every request (operator: "slow to sort"). R2 of the s-class
4// media-UI epic. Composes with the R1 discovery offset (gs_eff_offset): the serve reads this index
5// forward (ascending) or backward (descending) and paginates with the SAME pct/rand/offset logic, so a
6// single ascending index serves both sort directions.
7//
8// FORMAT (big-endian u64 so the daemon reads it with the existing gs_rb64; SAME shape as the category
9// index galx_cat_<c>.bin so the serve reuses that reader):
10// [0..8) stamp = byte-size of the freshness file (vid_paths.tsv) -> stale-detect / rebuild-on-change
11// [8..16) count N
12// [16..) N x id = recording ids (0-based line numbers) sorted ASCENDING by key
13//
14// The KEY comes from a "key file": one record per line, key = the first run of decimal digits on that
15// line. So it ingests nx_galx_durindex's out_dur.raw "dur_ms=<n>" lines directly, and any future size /
16// view-count key file. Line index i == recording id i (matches the daemon's id->line mapping); a blank
17// or key-less line becomes key 0 (keeps id alignment) and simply sorts first.
18// license_tier: ORIGINAL
19import "nx_syscalls.nx"
20const K_MAGIC_1000003: i64 = 1000003
21const K_MAGIC_2000000: i64 = 2000000
22const K_MAGIC_65536: i64 = 65536
23const K_MAGIC_1500000: i64 = 1500000
24const K_MAGIC_2048: i64 = 2048
25const K_MAGIC_262144: i64 = 262144
26
27// first run of decimal digits on [from,to) of s -> integer (0 if none); skips leading non-digits, stops
28// at the first non-digit AFTER a digit was seen.
29func si_first_int(s: *u8, from: i64, to: i64) -> i64 {
30 var i: i64 = from; var seen: i64 = 0; var v: i64 = 0
31 while i < to {
32 let c: i64 = s[i] as i64
33 if c >= 48 {
34 if c <= 57 { v = v * 10 + (c - 48); seen = 1; i = i + 1 }
35 else { if seen == 1 { i = to } else { i = i + 1 } }
36 } else { if seen == 1 { i = to } else { i = i + 1 } }
37 }
38 return v
39}
40
41// write v as big-endian u64 into buf[off..off+8); returns off+8
42func si_wb64(buf: *u8, off: i64, v: i64) -> i64 {
43 var k: i64 = 0
44 while k < 8 { buf[off + k] = ((v >> ((7 - k) * 8)) & 255) as u8; k = k + 1 }
45 return off + 8
46}
47// read big-endian u64 from buf[off..off+8)
48func si_rb64(buf: *u8, off: i64) -> i64 {
49 var v: i64 = 0; var k: i64 = 0
50 while k < 8 { v = (v << 8) | (buf[off + k] as i64); k = k + 1 }
51 return v
52}
53
54// byte-size of path via SEEK_END (no full read); -1 if absent.
55func si_fsize(path: *u8) -> i64 {
56 let fd: i64 = sys_openat_rd(path)
57 if fd < 0 { return 0 - 1 }
58 let n: i64 = sys_lseek(fd, 0, 2)
59 sys_close(fd)
60 return n
61}
62
63// stable bottom-up merge sort: order ids[0..N) ascending by keys[]; tk/ti are N-length scratch arrays.
64func si_msort(keys: *i64, ids: *i64, tk: *i64, ti: *i64, N: i64) -> i64 {
65 var width: i64 = 1
66 while width < N {
67 var i: i64 = 0
68 while i < N {
69 var mid: i64 = i + width; if mid > N { mid = N }
70 var hi: i64 = i + width + width; if hi > N { hi = N }
71 var a: i64 = i; var b: i64 = mid; var o: i64 = i
72 while a < mid {
73 if b < hi {
74 if keys[a] <= keys[b] { tk[o] = keys[a]; ti[o] = ids[a]; a = a + 1; o = o + 1 }
75 else { tk[o] = keys[b]; ti[o] = ids[b]; b = b + 1; o = o + 1 }
76 } else { tk[o] = keys[a]; ti[o] = ids[a]; a = a + 1; o = o + 1 }
77 }
78 while b < hi { tk[o] = keys[b]; ti[o] = ids[b]; b = b + 1; o = o + 1 }
79 i = i + width + width
80 }
81 var j: i64 = 0
82 while j < N { keys[j] = tk[j]; ids[j] = ti[j]; j = j + 1 }
83 width = width + width
84 }
85 return 0
86}
87
88// Build the sort-index from key_file -> out_path, stamped by the size of fresh_file.
89// Returns N (>=0) on success, -1 on read/open failure.
90func si_build_index(key_file: *u8, out_path: *u8, fresh_file: *u8) -> i64 {
91 let szp: *i64 = sys_mmap(16) as *i64
92 let b: *u8 = sys_read_file(key_file, szp)
93 if (b as i64) == 0 { return 0 - 1 }
94 let sz: i64 = szp[0]
95 var N: i64 = 0; var i: i64 = 0
96 while i < sz { if b[i] == (10 as u8) { N = N + 1 } i = i + 1 }
97 if sz > 0 { if b[sz - 1] != (10 as u8) { N = N + 1 } }
98 var cap: i64 = N; if cap < 1 { cap = 1 }
99 let keys: *i64 = sys_mmap(8 * cap) as *i64
100 let ids: *i64 = sys_mmap(8 * cap) as *i64
101 let tk: *i64 = sys_mmap(8 * cap) as *i64
102 let ti: *i64 = sys_mmap(8 * cap) as *i64
103 var id: i64 = 0; i = 0; var ls: i64 = 0
104 while i <= sz {
105 var nl: i64 = 0; if i == sz { nl = 1 } else { if b[i] == (10 as u8) { nl = 1 } }
106 if nl == 1 {
107 if id < N { keys[id] = si_first_int(b, ls, i); ids[id] = id; id = id + 1 }
108 ls = i + 1
109 }
110 i = i + 1
111 }
112 let realN: i64 = id
113 si_msort(keys, ids, tk, ti, realN)
114 let stamp: i64 = si_fsize(fresh_file)
115 let outb: *u8 = sys_mmap(16 + 8 * realN + 16)
116 var o: i64 = 0
117 o = si_wb64(outb, o, stamp)
118 o = si_wb64(outb, o, realN)
119 var j: i64 = 0
120 while j < realN { o = si_wb64(outb, o, ids[j]); j = j + 1 }
121 let fd: i64 = sys_openat_wr(out_path, 0x1a4)
122 if fd < 0 { return 0 - 1 }
123 sys_write(fd, outb, o)
124 sys_close(fd)
125 return realN
126}
127
128// ============================================================================================
129// SORT-SERVE: the gallery's sort-by-key request handlers, here in the LIB so the daemon and the
130// sovereign gate share ONE implementation (no bash/curl test path -- a gate calls ss_emit_* and
131// asserts the response bytes). Self-contained: uses only si_* primitives + syscalls. Index files are
132// galx_sort_<key>_<c>.bin = [stamp=vid_paths.tsv size][count][ids asc-by-key], read forward (asc) or
133// backward (desc) and paginated through si_eff_offset (so pct=/rand= compose with any sort).
134// ============================================================================================
135func si_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i }
136func si_u(dst: *u8, off: i64, v: i64) -> i64 {
137 if v == 0 { dst[off] = 48 as u8; return off + 1 }
138 let t: *u8 = sys_mmap(28); var m: i64 = v; var k: i64 = 0
139 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
140 var o: i64 = off; var j: i64 = k; while j > 0 { j = j - 1; dst[o] = t[j]; o = o + 1 } return o
141}
142func si_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
143func si_okjson(rbuf: *u8, body: *u8, blen: i64) -> i64 {
144 var o: i64 = 0
145 o = si_cat(rbuf, o, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: close\r\nContent-Length: " as *u8)
146 o = si_u(rbuf, o, blen)
147 o = si_cat(rbuf, o, "\r\n\r\n" as *u8)
148 var i: i64 = 0; while i < blen { rbuf[o] = body[i]; o = o + 1; i = i + 1 }
149 return o
150}
151func si_qint(req: *u8, rn: i64, key: *u8, deflt: i64) -> i64 {
152 let kl: i64 = si_strlen(key); var i: i64 = 0; var at: i64 = 0 - 1; var stop: i64 = rn
153 while i < stop { if req[i] == (10 as u8) { stop = i } else {
154 if at < 0 { if i + kl <= rn { var j: i64 = 0; var ok: i64 = 1; while j < kl { if req[i+j] != key[j] { ok = 0 } j = j + 1 } if ok == 1 { at = i + kl } } }
155 i = i + 1 } }
156 if at < 0 { return deflt }
157 var v: i64 = 0; var any: i64 = 0; var q: i64 = at
158 while q < rn { if req[q] >= (48 as u8) { if req[q] <= (57 as u8) { v = v * 10 + ((req[q] as i64) - 48); any = 1; q = q + 1 } else { q = rn } } else { q = rn } }
159 if any == 0 { return deflt }
160 return v
161}
162func si_eff_offset(req: *u8, rn: i64, count: i64, o0: i64) -> i64 {
163 if count <= 0 { return 0 }
164 let pct: i64 = si_qint(req, rn, "pct=", 0 - 1)
165 if pct >= 0 { var pc: i64 = pct; if pc > 100 { pc = 100 } var off: i64 = (count * pc) / 100; if off >= count { off = count - 1 } return off }
166 let rnd: i64 = si_qint(req, rn, "rand=", 0)
167 if rnd == 1 { let ts: *i64 = sys_mmap(16) as *i64; sys_clock_gettime_mono(ts); var seed: i64 = ts[1]; if seed < 0 { seed = 0 - seed } return seed % count }
168 return o0
169}
170func ss_path_cat(b: *u8, ls: i64, le: i64, rb: *u8, rsz: i64) -> i64 {
171 if (rb as i64) == 0 { return 114 }
172 var i: i64 = 0; var lstart: i64 = 0
173 while i <= rsz {
174 var nl: i64 = 0; if i == rsz { nl = 1 } else { if rb[i] == (10 as u8) { nl = 1 } }
175 if nl == 1 {
176 var tab: i64 = 0 - 1; var p: i64 = lstart
177 while p < i { if rb[p] == (9 as u8) { if tab < 0 { tab = p } } p = p + 1 }
178 if tab > lstart { if tab + 1 < i {
179 let slen: i64 = tab - lstart
180 var q: i64 = ls; var found: i64 = 0
181 while q + slen <= le {
182 var j: i64 = 0; var ok: i64 = 1
183 while j < slen { if b[q+j] != rb[lstart+j] { ok = 0; j = slen } else { j = j + 1 } }
184 if ok == 1 { found = 1; q = le } else { q = q + 1 }
185 }
186 if found == 1 { return rb[tab+1] as i64 }
187 } }
188 lstart = i + 1
189 }
190 i = i + 1
191 }
192 return 114
193}
194func ss_sortidx_path(keyc: i64, wantc: i64, out: *u8) -> i64 {
195 var o: i64 = si_cat(out, 0, "knowledge/status/galx_sort_" as *u8)
196 out[o] = keyc as u8; o = o + 1
197 out[o] = 95 as u8; o = o + 1
198 out[o] = wantc as u8; o = o + 1
199 o = si_cat(out, o, ".bin" as *u8); out[o] = 0 as u8
200 return o
201}
202func ss_write_index(path: *u8, stamp: i64, arr: *i64, cnt: i64) -> i64 {
203 let ob: *u8 = sys_mmap(16 + cnt * 8 + 64)
204 si_wb64(ob, 0, stamp); si_wb64(ob, 8, cnt)
205 var k: i64 = 0; while k < cnt { si_wb64(ob, 16 + k * 8, arr[k]); k = k + 1 }
206 let fd: i64 = sys_openat_wr(path, 0x1a4); if fd < 0 { return 0 - 1 }
207 sys_write(fd, ob, 16 + cnt * 8); sys_close(fd)
208 return cnt
209}
210// ---- R3 soft-hide: reversible + additive + NEVER touches files (rule 13). galx_hidden.log appends
211// "<id> <1|0>" (1=hide, 0=unhide), last-entry-per-id wins. Build-time exclusion keeps the index DENSE, so
212// counts + pagination stay exact (no per-emit skip). The index freshness folds in hidden.log's size, so a
213// hide/unhide auto-invalidates + rebuilds the affected index.
214func ss_fsize0(path: *u8) -> i64 { let s: i64 = si_fsize(path); if s < 0 { return 0 } return s }
215func ss_live_stamp() -> i64 { return ss_fsize0("knowledge/status/galx_vid_paths.tsv" as *u8) + ss_fsize0("knowledge/status/galx_hidden.log" as *u8) * K_MAGIC_1000003 }
216// load galx_hidden.log into a bitmap (bit id => hidden); processes in order so the last entry per id wins.
217func ss_load_hidden(bm: *u8) -> i64 {
218 let szp: *i64 = sys_mmap(16) as *i64
219 let b: *u8 = sys_read_file("knowledge/status/galx_hidden.log" as *u8, szp)
220 if (b as i64) == 0 { return 0 }
221 let sz: i64 = szp[0]
222 var i: i64 = 0; var ls: i64 = 0
223 while i <= sz {
224 var nl: i64 = 0; if i == sz { nl = 1 } else { if b[i] == (10 as u8) { nl = 1 } }
225 if nl == 1 {
226 if i > ls {
227 var id: i64 = 0; var v: i64 = 0; var stage: i64 = 0; var p: i64 = ls
228 while p < i {
229 let c: i64 = b[p] as i64
230 if c >= 48 { if c <= 57 { if stage == 0 { id = id * 10 + (c - 48) } else { v = v * 10 + (c - 48) } } else { if stage == 0 { stage = 1 } } }
231 else { if stage == 0 { stage = 1 } }
232 p = p + 1
233 }
234 if id < K_MAGIC_2000000 { let byte: i64 = id >> 3; let bit: i64 = 1 << (id & 7)
235 if v == 1 { bm[byte] = (bm[byte] as i64 | bit) as u8 } else { bm[byte] = (bm[byte] as i64 & (255 - bit)) as u8 } }
236 }
237 ls = i + 1
238 }
239 i = i + 1
240 }
241 return 0
242}
243func ss_is_hidden(bm: *u8, id: i64) -> i64 { if id >= K_MAGIC_2000000 { return 0 } if (bm[id >> 3] as i64 & (1 << (id & 7))) != 0 { return 1 } return 0 }
244// emit one page of the index cb (count N) as {"kind":"rec","total":N,"items":[...]}; dir==1 -> backward
245// (largest/longest first), else forward (smallest/shortest first); page is [eo0, eo0+n).
246func ss_emit_page(rbuf: *u8, cb: *u8, count: i64, eo0: i64, dir: i64, n: i64) -> i64 {
247 let body: *u8 = sys_mmap(K_MAGIC_65536); var bo: i64 = 0
248 bo = si_cat(body, bo, "{\"kind\":\"rec\",\"total\":" as *u8); bo = si_u(body, bo, count); bo = si_cat(body, bo, ",\"items\":[" as *u8)
249 var k: i64 = 0; var first: i64 = 1
250 while k < n {
251 let disp: i64 = eo0 + k
252 if disp >= count { k = n } else {
253 var id: i64 = 0
254 if dir == 1 { id = si_rb64(cb, 16 + (count - 1 - disp) * 8) } else { id = si_rb64(cb, 16 + disp * 8) }
255 if first == 0 { body[bo] = 44 as u8; bo = bo + 1 } first = 0
256 body[bo] = 34 as u8; bo = bo + 1; bo = si_u(body, bo, id); body[bo] = 34 as u8; bo = bo + 1; k = k + 1
257 }
258 }
259 bo = si_cat(body, bo, "]}" as *u8)
260 return si_okjson(rbuf, body, bo)
261}
262// Build galx_sort_s_<c>.bin: ids of category wantc sorted ASC by file size. Returns count, -1 on fail.
263func ss_build_size(wantc: i64) -> i64 {
264 let rszp: *i64 = sys_mmap(16) as *i64
265 let rb: *u8 = sys_read_file("knowledge/status/galx_vid_rules.tsv" as *u8, rszp)
266 let rsz: i64 = rszp[0]
267 let szp: *i64 = sys_mmap(16) as *i64
268 let b: *u8 = sys_read_file("knowledge/status/galx_vid_paths.tsv" as *u8, szp)
269 let sz: i64 = szp[0]
270 if (b as i64) == 0 { return 0 - 1 }
271 let keys: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
272 let ids: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
273 let tk: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
274 let ti: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
275 let path: *u8 = sys_mmap(K_MAGIC_2048)
276 let hbm: *u8 = sys_mmap(K_MAGIC_262144); ss_load_hidden(hbm)
277 var M: i64 = 0; var i: i64 = 0; var ls: i64 = 0; var lineno: i64 = 0
278 while i <= sz {
279 var nl: i64 = 0; if i == sz { nl = 1 } else { if b[i] == (10 as u8) { nl = 1 } }
280 if nl == 1 {
281 if i - ls > 3 {
282 let cat: i64 = ss_path_cat(b, ls, i, rb, rsz)
283 if cat == wantc { if M < K_MAGIC_1500000 { if ss_is_hidden(hbm, lineno) == 0 {
284 var ce: i64 = i; if ce > ls { if b[ce-1] == (13 as u8) { ce = ce - 1 } }
285 var po: i64 = 0; var k: i64 = ls; while k < ce { path[po] = b[k]; po = po + 1; k = k + 1 } path[po] = 0 as u8
286 var fsz: i64 = si_fsize(path); if fsz < 0 { fsz = 0 }
287 keys[M] = fsz; ids[M] = lineno; M = M + 1
288 } } }
289 }
290 lineno = lineno + 1; ls = i + 1
291 }
292 i = i + 1
293 }
294 si_msort(keys, ids, tk, ti, M)
295 let spath: *u8 = sys_mmap(256); ss_sortidx_path(115, wantc, spath)
296 return ss_write_index(spath, ss_live_stamp(), ids, M)
297}
298func ss_emit_size(rbuf: *u8, req: *u8, rn: i64, wantc: i64, dir: i64, o0: i64, n: i64) -> i64 {
299 let live: i64 = ss_live_stamp()
300 if live < 0 { return 0 }
301 let spath: *u8 = sys_mmap(256); ss_sortidx_path(115, wantc, spath)
302 let szp: *i64 = sys_mmap(16) as *i64
303 var cb: *u8 = sys_read_file(spath, szp)
304 var fresh: i64 = 0
305 if (cb as i64) != 0 { if szp[0] >= 16 { if si_rb64(cb, 0) == live { fresh = 1 } } }
306 if fresh == 0 { if ss_build_size(wantc) < 0 { return 0 } cb = sys_read_file(spath, szp); if (cb as i64) == 0 { return 0 } }
307 let count: i64 = si_rb64(cb, 8)
308 let eo0: i64 = si_eff_offset(req, rn, count, o0)
309 return ss_emit_page(rbuf, cb, count, eo0, dir, n)
310}
311// Build galx_sort_t_<c>.bin from the duration batch galx_dur.raw (key=first int=dur_ms). -1 if batch absent.
312func ss_build_runtime(wantc: i64) -> i64 {
313 let dszp: *i64 = sys_mmap(16) as *i64
314 let db: *u8 = sys_read_file("knowledge/status/galx_dur.raw" as *u8, dszp)
315 if (db as i64) == 0 { return 0 - 1 }
316 let dsz: i64 = dszp[0]
317 if dsz < 1 { return 0 - 1 }
318 var durN: i64 = 0; var di: i64 = 0
319 while di < dsz { if db[di] == (10 as u8) { durN = durN + 1 } di = di + 1 }
320 if dsz > 0 { if db[dsz-1] != (10 as u8) { durN = durN + 1 } }
321 if durN < 1 { return 0 - 1 }
322 let durby: *i64 = sys_mmap(8 * durN) as *i64
323 var x: i64 = 0; var dls: i64 = 0; var did: i64 = 0
324 while x <= dsz {
325 var dnl: i64 = 0; if x == dsz { dnl = 1 } else { if db[x] == (10 as u8) { dnl = 1 } }
326 // nx_ts_dur prints "size=<n> first_pcr90=<n> last_pcr90=<n> dur_ms=<n>" -> the FIRST int is SIZE, so we
327 // must extract the dur_ms= field specifically (si_qint), NOT the first integer on the line.
328 if dnl == 1 { if did < durN { durby[did] = si_qint(((db as i64) + dls) as *u8, x - dls, "dur_ms=", 0); did = did + 1 } dls = x + 1 }
329 x = x + 1
330 }
331 let rszp: *i64 = sys_mmap(16) as *i64
332 let rb: *u8 = sys_read_file("knowledge/status/galx_vid_rules.tsv" as *u8, rszp)
333 let rsz: i64 = rszp[0]
334 let szp: *i64 = sys_mmap(16) as *i64
335 let b: *u8 = sys_read_file("knowledge/status/galx_vid_paths.tsv" as *u8, szp)
336 let sz: i64 = szp[0]
337 if (b as i64) == 0 { return 0 - 1 }
338 let keys: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
339 let ids: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
340 let tk: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
341 let ti: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
342 let hbm: *u8 = sys_mmap(K_MAGIC_262144); ss_load_hidden(hbm)
343 var M: i64 = 0; var i: i64 = 0; var ls: i64 = 0; var lineno: i64 = 0
344 while i <= sz {
345 var nl: i64 = 0; if i == sz { nl = 1 } else { if b[i] == (10 as u8) { nl = 1 } }
346 if nl == 1 {
347 if i - ls > 3 {
348 let cat: i64 = ss_path_cat(b, ls, i, rb, rsz)
349 if cat == wantc { if lineno < durN { if M < K_MAGIC_1500000 { if ss_is_hidden(hbm, lineno) == 0 { keys[M] = durby[lineno]; ids[M] = lineno; M = M + 1 } } } }
350 }
351 lineno = lineno + 1; ls = i + 1
352 }
353 i = i + 1
354 }
355 si_msort(keys, ids, tk, ti, M)
356 let spath: *u8 = sys_mmap(256); ss_sortidx_path(116, wantc, spath)
357 return ss_write_index(spath, ss_live_stamp(), ids, M)
358}
359func ss_emit_runtime(rbuf: *u8, req: *u8, rn: i64, wantc: i64, dir: i64, o0: i64, n: i64) -> i64 {
360 let live: i64 = ss_live_stamp()
361 if live < 0 { return 0 }
362 let spath: *u8 = sys_mmap(256); ss_sortidx_path(116, wantc, spath)
363 let szp: *i64 = sys_mmap(16) as *i64
364 var cb: *u8 = sys_read_file(spath, szp)
365 var fresh: i64 = 0
366 if (cb as i64) != 0 { if szp[0] >= 16 { if si_rb64(cb, 0) == live { fresh = 1 } } }
367 if fresh == 0 {
368 if ss_build_runtime(wantc) < 0 {
369 let bb: *u8 = sys_mmap(128); var bn: i64 = 0
370 bn = si_cat(bb, bn, "{\"kind\":\"rec\",\"total\":0,\"items\":[],\"building\":1}" as *u8)
371 return si_okjson(rbuf, bb, bn)
372 }
373 cb = sys_read_file(spath, szp); if (cb as i64) == 0 { return 0 }
374 }
375 let count: i64 = si_rb64(cb, 8)
376 let eo0: i64 = si_eff_offset(req, rn, count, o0)
377 return ss_emit_page(rbuf, cb, count, eo0, dir, n)
378}
379
380// ---- NAME sort (alphabetical by basename, case-insensitive) -- a real string merge-sort, not a lossy key ----
381func ss_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c }
382// compare two basenames [oa,oa+la) vs [ob,ob+lb) in buf, case-insensitive; <0 / 0 / >0.
383func ss_namecmp(buf: *u8, oa: i64, la: i64, ob: i64, lb: i64) -> i64 {
384 var i: i64 = 0; var lim: i64 = la; if lb < lim { lim = lb }
385 while i < lim {
386 let ca: i64 = ss_lc(buf[oa + i] as i64); let cb: i64 = ss_lc(buf[ob + i] as i64)
387 if ca < cb { return 0 - 1 }
388 if ca > cb { return 1 }
389 i = i + 1
390 }
391 if la < lb { return 0 - 1 }
392 if la > lb { return 1 }
393 return 0
394}
395// stable bottom-up merge sort of (bo,bl,ids) triples ASC by the basename each references; t* are scratch.
396func ss_msort_name(buf: *u8, bo: *i64, bl: *i64, ids: *i64, tbo: *i64, tbl: *i64, tid: *i64, N: i64) -> i64 {
397 var width: i64 = 1
398 while width < N {
399 var i: i64 = 0
400 while i < N {
401 var mid: i64 = i + width; if mid > N { mid = N }
402 var hi: i64 = i + width + width; if hi > N { hi = N }
403 var a: i64 = i; var b: i64 = mid; var o: i64 = i
404 while a < mid {
405 if b < hi {
406 if ss_namecmp(buf, bo[a], bl[a], bo[b], bl[b]) <= 0 { tbo[o] = bo[a]; tbl[o] = bl[a]; tid[o] = ids[a]; a = a + 1; o = o + 1 }
407 else { tbo[o] = bo[b]; tbl[o] = bl[b]; tid[o] = ids[b]; b = b + 1; o = o + 1 }
408 } else { tbo[o] = bo[a]; tbl[o] = bl[a]; tid[o] = ids[a]; a = a + 1; o = o + 1 }
409 }
410 while b < hi { tbo[o] = bo[b]; tbl[o] = bl[b]; tid[o] = ids[b]; b = b + 1; o = o + 1 }
411 i = i + width + width
412 }
413 var j: i64 = 0
414 while j < N { bo[j] = tbo[j]; bl[j] = tbl[j]; ids[j] = tid[j]; j = j + 1 }
415 width = width + width
416 }
417 return 0
418}
419// Build galx_sort_n_<c>.bin: ids of category wantc sorted ASC by basename (the part after the last '/').
420func ss_build_name(wantc: i64) -> i64 {
421 let rszp: *i64 = sys_mmap(16) as *i64
422 let rb: *u8 = sys_read_file("knowledge/status/galx_vid_rules.tsv" as *u8, rszp)
423 let rsz: i64 = rszp[0]
424 let szp: *i64 = sys_mmap(16) as *i64
425 let b: *u8 = sys_read_file("knowledge/status/galx_vid_paths.tsv" as *u8, szp)
426 let sz: i64 = szp[0]
427 if (b as i64) == 0 { return 0 - 1 }
428 let bo: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
429 let bl: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
430 let ids: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
431 let tbo: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
432 let tbl: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
433 let tid: *i64 = sys_mmap(8 * K_MAGIC_1500000) as *i64
434 let hbm: *u8 = sys_mmap(K_MAGIC_262144); ss_load_hidden(hbm)
435 var M: i64 = 0; var i: i64 = 0; var ls: i64 = 0; var lineno: i64 = 0
436 while i <= sz {
437 var nl: i64 = 0; if i == sz { nl = 1 } else { if b[i] == (10 as u8) { nl = 1 } }
438 if nl == 1 {
439 if i - ls > 3 {
440 let cat: i64 = ss_path_cat(b, ls, i, rb, rsz)
441 if cat == wantc { if M < K_MAGIC_1500000 { if ss_is_hidden(hbm, lineno) == 0 {
442 var ce: i64 = i; if ce > ls { if b[ce - 1] == (13 as u8) { ce = ce - 1 } }
443 var lastsl: i64 = ls - 1; var p: i64 = ls
444 while p < ce { if b[p] == (47 as u8) { lastsl = p } p = p + 1 }
445 bo[M] = lastsl + 1; bl[M] = ce - (lastsl + 1); ids[M] = lineno; M = M + 1
446 } } }
447 }
448 lineno = lineno + 1; ls = i + 1
449 }
450 i = i + 1
451 }
452 ss_msort_name(b, bo, bl, ids, tbo, tbl, tid, M)
453 let spath: *u8 = sys_mmap(256); ss_sortidx_path(110, wantc, spath) // 110 = 'n' (name)
454 return ss_write_index(spath, ss_live_stamp(), ids, M)
455}
456func ss_emit_name(rbuf: *u8, req: *u8, rn: i64, wantc: i64, dir: i64, o0: i64, n: i64) -> i64 {
457 let live: i64 = ss_live_stamp()
458 if live < 0 { return 0 }
459 let spath: *u8 = sys_mmap(256); ss_sortidx_path(110, wantc, spath)
460 let szp: *i64 = sys_mmap(16) as *i64
461 var cb: *u8 = sys_read_file(spath, szp)
462 var fresh: i64 = 0
463 if (cb as i64) != 0 { if szp[0] >= 16 { if si_rb64(cb, 0) == live { fresh = 1 } } }
464 if fresh == 0 { if ss_build_name(wantc) < 0 { return 0 } cb = sys_read_file(spath, szp); if (cb as i64) == 0 { return 0 } }
465 let count: i64 = si_rb64(cb, 8)
466 let eo0: i64 = si_eff_offset(req, rn, count, o0)
467 return ss_emit_page(rbuf, cb, count, eo0, dir, n)
468}