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}