code wiki / _hdl_build / nx_galx_durbin_lib.nx

nx_galx_durbin_lib.nx source

↩ module page · 134 lines · 7048 B

1// nx_galx_durbin_lib.nx -- O(1) duration lookup for the gallery .ts seekbar. Compacts galx_dur.raw 2// (text "size=.. dur_ms=<n>" per line, line N = recording N) into galx_dur.bin: a big-endian header 3// [vidpaths_size(8)][n(8)] followed by n*[dur_ms(8)]. Then /vid/<id>/dur is ONE 8-byte seek+read instead 4// of a 16 MB PCR re-scan. Mirrors gs_vidoff_build / galx_vid_off.bin exactly. The durindex is APPEND-ONLY 5// (line N's duration is written once and never changes), so a .bin built from the first n lines stays 6// correct for every id < n -- the file can keep growing under us without invalidating earlier entries. 7// vidpaths_size guards against galx_vid_paths.tsv changing (line N -> a different video); on mismatch or a 8// not-yet-indexed id we return -1 and the caller falls back to the live PCR scan. no main. license_tier: ORIGINAL 9import "nx_syscalls.nx" 10const K_MAGIC_86400000: i64 = 86400000 11 12func db_wb64(b: *u8, o: i64, v: i64) -> i64 { 13 b[o]=((v>>56)&0xff) as u8; b[o+1]=((v>>48)&0xff) as u8; b[o+2]=((v>>40)&0xff) as u8; b[o+3]=((v>>32)&0xff) as u8 14 b[o+4]=((v>>24)&0xff) as u8; b[o+5]=((v>>16)&0xff) as u8; b[o+6]=((v>>8)&0xff) as u8; b[o+7]=(v&0xff) as u8 15 return o+8 16} 17func db_rb64(b: *u8, o: i64) -> i64 { 18 return ((b[o] as i64)<<56)|((b[o+1] as i64)<<48)|((b[o+2] as i64)<<40)|((b[o+3] as i64)<<32)|((b[o+4] as i64)<<24)|((b[o+5] as i64)<<16)|((b[o+6] as i64)<<8)|(b[o+7] as i64) 19} 20// parse the unsigned int after `needle` within buf[ls..le); -1 if the needle is not present in that slice. 21func db_qint(buf: *u8, ls: i64, le: i64, needle: *u8, nlen: i64) -> i64 { 22 var i: i64 = ls 23 while i + nlen <= le { 24 var k: i64 = 0; var m: i64 = 1 25 while k < nlen { if buf[i+k] != needle[k] { m=0; k=nlen } else { k=k+1 } } 26 if m == 1 { 27 var v: i64 = 0; var j: i64 = i + nlen 28 while j < le { let c: i64 = buf[j] as i64; if c < 48 { j=le } else { if c > 57 { j=le } else { v=v*10+(c-48); j=j+1 } } } 29 return v 30 } 31 i = i + 1 32 } 33 return 0 - 1 34} 35// build bin_path from raw_path, stamping vps as the freshness key. returns n (#lines) or -1 on error. 36// idempotent: truncates bin_path. Safe to call repeatedly as galx_dur.raw grows (picks up new lines). 37func db_build(raw_path: *u8, bin_path: *u8, vps: i64) -> i64 { 38 // SNAPSHOT the size, then read EXACTLY that many bytes. galx_dur.raw is appended live by the durindex; 39 // sys_read_file's read-until-EOF + 4GB anon mmap raced those appends and SIGSEGV'd on the NAS. Bounding 40 // the read to the lseek snapshot (and mmap'ing only sz+16) makes the build safe to run at any time. 41 let fd: i64 = sys_openat_rd(raw_path) 42 if fd < 0 { return 0 - 1 } 43 let sz: i64 = sys_lseek(fd, 0, 2); sys_lseek(fd, 0, 0) 44 if sz <= 0 { sys_close(fd); return 0 - 1 } 45 let b: *u8 = sys_mmap(sz + 16) 46 var total: i64 = 0; var go: i64 = 1 47 while go == 1 { 48 if total >= sz { go = 0 } else { 49 let nr: i64 = sys_read(fd, ((b as i64) + total) as *u8, sz - total) 50 if nr <= 0 { go = 0 } else { total = total + nr } 51 } 52 } 53 sys_close(fd); b[total] = 0 as u8 54 var n: i64 = 0; var i: i64 = 0 55 while i < total { if b[i]==(10 as u8) { n=n+1 } i=i+1 } 56 let ob: *u8 = sys_mmap(16 + n*8 + 64) 57 db_wb64(ob, 0, vps); db_wb64(ob, 8, n) 58 var cnt: i64 = 0; var ls: i64 = 0; i = 0 59 while i < total { 60 if b[i]==(10 as u8) { 61 var dms: i64 = db_qint(b, ls, i, "dur_ms=" as *u8, 7) 62 if dms < 0 { dms = 0 } 63 if dms > K_MAGIC_86400000 { dms = 0 } // >24h = a container mis-parse; store 0 so it never sorts/badges as "longest" 64 db_wb64(ob, 16+cnt*8, dms); cnt = cnt + 1; ls = i + 1 65 } 66 i = i + 1 67 } 68 let wf: i64 = sys_openat_wr(bin_path, 0x1a4) 69 if wf < 0 { return 0 - 1 } 70 sys_write(wf, ob, 16 + n*8); sys_close(wf) 71 return n 72} 73func db_emit_u(out: *u8, o: i64, v: i64) -> i64 { 74 if v == 0 { out[o] = 48 as u8; return o + 1 } 75 let t: *u8 = sys_mmap(28); var m: i64 = v; var k: i64 = 0 76 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 77 var j: i64 = k; var oo: i64 = o 78 while j > 0 { j = j - 1; out[oo] = t[j]; oo = oo + 1 } 79 return oo 80} 81// Emit JSON {"<id>":<dur_ms>,...} for a comma-separated id list, looking each up in bin_path (the .bin is 82// opened ONCE; ids seek within it). Ids with dur<=0 / out-of-range are omitted so the UI only badges known 83// runtimes. cur_vps guards freshness (0 = skip). Returns bytes written to out. One round-trip per grid page 84// instead of one /vid/<id>/dur request per tile. 85func db_batch_durs(out: *u8, ids: *u8, bin_path: *u8, cur_vps: i64) -> i64 { 86 var o: i64 = 0; out[o] = 123 as u8; o = o + 1 87 let fd: i64 = sys_openat_rd(bin_path) 88 var n: i64 = 0; var ok: i64 = 0 89 if fd >= 0 { 90 let hdr: *u8 = sys_mmap(32); let hr: i64 = sys_read(fd, hdr, 16) 91 if hr == 16 { 92 let stored: i64 = db_rb64(hdr, 0); n = db_rb64(hdr, 8); ok = 1 93 if cur_vps > 0 { if stored != cur_vps { ok = 0 } } 94 } 95 } 96 var first: i64 = 1; var i: i64 = 0 97 while ids[i] != (0 as u8) { 98 let c: i64 = ids[i] as i64 99 if c < 48 { i = i + 1 } else { if c > 57 { i = i + 1 } else { 100 var v: i64 = 0; var go: i64 = 1 101 while go == 1 { let d: i64 = ids[i] as i64; if d < 48 { go = 0 } else { if d > 57 { go = 0 } else { v = v*10+(d-48); i = i + 1 } } } 102 if ok == 1 { if v >= 0 { if v < n { 103 sys_lseek(fd, 16 + v*8, 0) 104 let bb: *u8 = sys_mmap(16); let rr: i64 = sys_read(fd, bb, 8) 105 if rr == 8 { let dur: i64 = db_rb64(bb, 0) 106 if dur > 0 { 107 if first == 0 { out[o]=44 as u8; o=o+1 } first = 0 108 out[o]=34 as u8; o=o+1; o = db_emit_u(out, o, v); out[o]=34 as u8; o=o+1; out[o]=58 as u8; o=o+1 109 o = db_emit_u(out, o, dur) 110 } 111 } 112 } } } 113 } } 114 } 115 if fd >= 0 { sys_close(fd) } 116 out[o] = 125 as u8; o = o + 1 117 return o 118} 119// O(1) dur_ms lookup for recording id; -1 if missing/out-of-range/stale. cur_vps = current galx_vid_paths.tsv 120// size (pass 0 to skip the freshness guard). A returned 0 means "indexed but duration unknown" -- the caller 121// should treat <=0 as a miss and fall back to the live scan. 122func db_lookup(bin_path: *u8, id: i64, cur_vps: i64) -> i64 { 123 if id < 0 { return 0 - 1 } 124 let fd: i64 = sys_openat_rd(bin_path); if fd < 0 { return 0 - 1 } 125 let hdr: *u8 = sys_mmap(32); let hr: i64 = sys_read(fd, hdr, 16) 126 if hr != 16 { sys_close(fd); return 0 - 1 } 127 let stored: i64 = db_rb64(hdr, 0); let n: i64 = db_rb64(hdr, 8) 128 if cur_vps > 0 { if stored != cur_vps { sys_close(fd); return 0 - 1 } } 129 if id >= n { sys_close(fd); return 0 - 1 } 130 sys_lseek(fd, 16 + id*8, 0) 131 let bb: *u8 = sys_mmap(16); let rr: i64 = sys_read(fd, bb, 8); sys_close(fd) 132 if rr != 8 { return 0 - 1 } 133 return db_rb64(bb, 0) 134}