code wiki / _hdl_build / nx_galx_thumb_prewarm.nx
nx_galx_thumb_prewarm.nx source
↩ module page · 92 lines · 5612 B
1// nx_galx_thumb_prewarm.nx -- pre-warm the gallery thumbnail cache so thumbs are INSTANT from the first
2// scroll instead of generated on demand. Reads a cid->path map (galx_cid_paths.tsv format: <cid>\t<path>),
3// and for each not-yet-cached cid runs nx_galx_thumb(src -> <thumbsdir>/<cid>.jpg). IDEMPOTENT + RESUMABLE
4// (skips cids whose thumb already exists), bounded fork-POOL for throughput. Runs NAS-side (where the images
5// + the gallery's thumbs/ live); the on-demand /thumb route remains the fallback for anything not pre-warmed.
6//
7// usage: nx_galx_thumb_prewarm <cid_paths.tsv> <thumbs_dir> <nx_galx_thumb.elf> [maxdim=384] [conc=4] [limit=0]
8// expect_exit: 0 . license_tier: ORIGINAL
9import "nx_syscalls.nx"
10const K_MAGIC_2048: i64 = 2048
11
12func pw_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
13func pw_putn(v: i64) -> i64 {
14 let bb: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) }
15 let t: *u8 = sys_mmap(28); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 }
16 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
17 var i: i64 = 0; while i < k { bb[i] = t[k-1-i]; i = i + 1 } sys_write(1, bb, k); return 0
18}
19func pw_atoi(s: *u8) -> i64 { var v: i64 = 0; var i: i64 = 0; while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c < 48 { return v } if c > 57 { return v } v = v * 10 + (c - 48); i = i + 1 } return v }
20func pw_exists(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 }
21
22func main(argc: i64, argv: *i64) -> i64 {
23 if argc < 4 { pw_puts("usage: nx_galx_thumb_prewarm <cid_paths.tsv> <thumbs_dir> <thumb.elf> [maxdim] [conc] [limit]\n" as *u8); return 2 }
24 let cidpaths: *u8 = argv[1] as *u8
25 let thumbsdir: *u8 = argv[2] as *u8
26 let thumbelf: *u8 = argv[3] as *u8
27 var maxs: *u8 = "384" as *u8
28 if argc > 4 { maxs = argv[4] as *u8 }
29 var conc: i64 = 4
30 if argc > 5 { let cc: i64 = pw_atoi(argv[5] as *u8); if cc > 0 { conc = cc } }
31 var limit: i64 = 0
32 if argc > 6 { limit = pw_atoi(argv[6] as *u8) }
33
34 // mkdir the thumbs dir (idempotent; EEXIST ignored)
35 __syscall(83, thumbsdir as i64, 493, 0, 0, 0, 0)
36
37 let szp: *i64 = sys_mmap(16) as *i64
38 let b: *u8 = sys_read_file(cidpaths, szp) // read once, then fork-loop (no per-iteration re-read)
39 if (b as i64) == 0 { pw_puts("prewarm: cannot read cid_paths\n" as *u8); return 1 }
40 let sz: i64 = szp[0]
41
42 let st: *i64 = sys_mmap(16) as *i64
43 let envp: *i64 = sys_mmap(8) as *i64; envp[0] = 0
44 var inflight: i64 = 0
45 var spawned: i64 = 0; var skipped: i64 = 0; var seen: i64 = 0
46 var i: i64 = 0; var ls: i64 = 0
47 while i <= sz {
48 var nl: i64 = 0; if i == sz { nl = 1 } else { if b[i] == (10 as u8) { nl = 1 } }
49 if nl == 1 {
50 if i - ls > 5 {
51 // split at the first TAB: cid = [ls, tab), src = [tab+1, i)
52 var tab: i64 = 0 - 1; var p: i64 = ls
53 while p < i { if b[p] == (9 as u8) { tab = p; p = i } else { p = p + 1 } }
54 if tab > ls { if tab + 1 < i {
55 seen = seen + 1
56 let cidlen: i64 = tab - ls
57 let srclen: i64 = i - (tab + 1)
58 // build cache path = thumbsdir + "/" + cid + ".jpg" (extension bytes 'j''p''g' below)
59 let cache: *u8 = sys_mmap(K_MAGIC_2048); var co: i64 = 0
60 var d: i64 = 0; while thumbsdir[d] != (0 as u8) { cache[co] = thumbsdir[d]; co = co + 1; d = d + 1 }
61 cache[co] = 47 as u8; co = co + 1
62 var q: i64 = 0; while q < cidlen { cache[co] = b[ls + q]; co = co + 1; q = q + 1 }
63 cache[co] = 46 as u8; co = co + 1; cache[co] = 106 as u8; co = co + 1; cache[co] = 112 as u8; co = co + 1; cache[co] = 103 as u8; co = co + 1
64 cache[co] = 0 as u8
65 if pw_exists(cache) == 1 {
66 skipped = skipped + 1
67 } else {
68 // NUL-terminated src copy for execve
69 let src: *u8 = sys_mmap(K_MAGIC_2048); var so: i64 = 0
70 var r: i64 = 0; while r < srclen { src[so] = b[tab + 1 + r]; so = so + 1; r = r + 1 } src[so] = 0 as u8
71 if inflight >= conc { var reaped: i64 = 0; while reaped <= 0 { reaped = sys_wait4(0 - 1, st, 0) } inflight = inflight - 1 }
72 let pid: i64 = sys_fork()
73 if pid == 0 {
74 let av: *i64 = sys_mmap(48) as *i64
75 av[0] = thumbelf as i64; av[1] = src as i64; av[2] = cache as i64; av[3] = maxs as i64; av[4] = 0
76 sys_execve(thumbelf, av, envp); sys_exit(127)
77 }
78 inflight = inflight + 1; spawned = spawned + 1
79 if spawned % 1000 == 0 { pw_puts(" prewarmed "); pw_putn(spawned); pw_puts(" (skipped "); pw_putn(skipped); pw_puts(")\n" as *u8) }
80 }
81 } }
82 }
83 ls = i + 1
84 if limit > 0 { if seen >= limit { i = sz } }
85 }
86 i = i + 1
87 }
88 // drain the pool
89 while inflight > 0 { let rr: i64 = sys_wait4(0 - 1, st, 0); if rr <= 0 { inflight = 0 } else { inflight = inflight - 1 } }
90 pw_puts("PREWARM DONE seen="); pw_putn(seen); pw_puts(" generated="); pw_putn(spawned); pw_puts(" skipped(cached)="); pw_putn(skipped); pw_puts("\n" as *u8)
91 sys_exit(0); return 0
92}