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}