code wiki / _hdl_build / nx_nbk_pack.nx
nx_nbk_pack.nx source
↩ module page · 104 lines · 5173 B
1// nx_nbk_pack.nx -- migrate a decoded book (reader/<slug>/ : book.json + chap*.txt + assets + xray.json) INTO the
2// sovereign Nishi container <slug>.nbk. EPUB (nx_epub_book) and MOBI (nx_mobi_book) both emit that same reader/<slug>/
3// layout, so this is the single convergence point: any lock-in format -> one owned, content-addressed file.
4// Usage: nx_nbk_pack <slug>. expect_exit: 0 license_tier: ORIGINAL
5import "nx_syscalls.nx"
6import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
7import "nx_nbk.nx"
8const K_MAGIC_2048: i64 = 2048
9const K_MAGIC_1024: i64 = 1024
10
11func pk_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
12func pk_p(s: *u8) -> i64 { sys_write(1, s, pk_slen(s)); return 0 }
13// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
14// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
15// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
16// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
17func pk_n(v: i64) -> i64 { nxi_out(v); return 0 }
18func ensure_dir(path: *u8) -> i64 { __syscall(258, 0-100, path, 0x1ed, 0, 0, 0); return 0 }
19func 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 }
20
21func jfrom(hay: *u8, hl: i64, start: i64, needle: *u8) -> i64 {
22 let nl: i64 = pk_slen(needle); if nl == 0 { return 0-1 }
23 var i: i64 = start; if i < 0 { i = 0 }
24 while i + nl <= hl { var k: i64=0; var hit: i64=1; while k<nl { if hay[i+k]!=needle[k]{hit=0;k=nl}else{k=k+1} } if hit==1 {return i} i=i+1 }
25 return 0-1
26}
27// copy a JSON string value (pos = opening quote) into out, unescaping \" \\.
28func jstr(hay: *u8, hl: i64, pos: i64, out: *u8, cap: i64) -> i64 {
29 var i: i64 = pos
30 if i < hl { if hay[i]==(0x22 as u8) { i=i+1 } }
31 var o: i64 = 0
32 while i < hl {
33 let c: i64 = hay[i] as i64
34 if c == 0x22 { i = hl }
35 else { if c == 0x5c { if i+1<hl { if o<cap-1 { out[o]=hay[i+1]; o=o+1 } i=i+2 } else { i=i+1 } } else { if o<cap-1 { out[o]=hay[i] as u8; o=o+1 } i=i+1 } }
36 }
37 out[o] = 0 as u8
38 return o
39}
40
41const ECAP: i64 = 1024
42
43// load file at dir/name into a fresh buffer; record name/data/len at entry slot `idx`. returns 1 if added.
44func add_file(dir: *u8, name: *u8, names: *i64, datas: *i64, lens: *i64, idx: i64) -> i64 {
45 let path: *u8 = sys_mmap(K_MAGIC_2048)
46 var o: i64 = cat(path, 0, dir); path[o]=0x2f as u8; o=o+1
47 var j: i64 = 0; while name[j]!=(0 as u8){ path[o]=name[j]; o=o+1; j=j+1 } path[o]=0 as u8
48 let lp: *i64 = sys_mmap(8) as *i64; lp[0]=0
49 let data: *u8 = sys_read_file(path, lp)
50 if (data as i64) == 0 { return 0 }
51 let nm: *u8 = sys_mmap(256); var k: i64=0; while name[k]!=(0 as u8){ nm[k]=name[k]; k=k+1 } nm[k]=0 as u8
52 names[idx] = nm as i64; datas[idx] = data as i64; lens[idx] = lp[0]
53 return 1
54}
55
56func main(argc: i64, argv: *i64) -> i64 {
57 var slug: *u8 = "nishi_fixture" as *u8
58 if argc >= 2 { slug = argv[1] as *u8 }
59
60 let dir: *u8 = sys_mmap(K_MAGIC_1024)
61 var dl: i64 = cat(dir, 0, "knowledge/staging/media/reader/" as *u8)
62 dl = cat(dir, dl, slug); dir[dl] = 0 as u8
63
64 // load book.json
65 let bjp: *u8 = sys_mmap(K_MAGIC_1024)
66 var bl: i64 = cat(bjp, 0, dir); bl = cat(bjp, bl, "/book.json" as *u8); bjp[bl]=0 as u8
67 let lp: *i64 = sys_mmap(8) as *i64; lp[0]=0
68 let bj: *u8 = sys_read_file(bjp, lp)
69 if (bj as i64)==0 { pk_p("NBK-PACK-FAIL no-book.json " as *u8); pk_p(bjp); pk_p("\n" as *u8); sys_exit(1); return 1 }
70 let bn: i64 = lp[0]
71
72 let names: *i64 = sys_mmap(ECAP*8) as *i64
73 let datas: *i64 = sys_mmap(ECAP*8) as *i64
74 let lens: *i64 = sys_mmap(ECAP*8) as *i64
75 var count: i64 = 0
76
77 // entry 0: book.json (the manifest)
78 names[0] = "book.json" as *u8 as i64; datas[0] = bj as i64; lens[0] = bn; count = 1
79
80 // every "file":"X" in book.json = chapters[].file + assets[].file (cover is in assets[])
81 let nbuf: *u8 = sys_mmap(512)
82 var cur: i64 = 0
83 var go: i64 = 1
84 while go == 1 {
85 let fat: i64 = jfrom(bj, bn, cur, "\"file\":\"" as *u8)
86 if fat < 0 { go = 0 } else {
87 jstr(bj, bn, fat + 7, nbuf, 512)
88 if count < ECAP { if add_file(dir, nbuf, names, datas, lens, count) == 1 { count = count + 1 } }
89 cur = fat + 7
90 }
91 }
92 // xray.json (optional)
93 if count < ECAP { if add_file(dir, "xray.json" as *u8, names, datas, lens, count) == 1 { count = count + 1 } }
94
95 ensure_dir("knowledge/staging/media/nbk" as *u8)
96 let outp: *u8 = sys_mmap(K_MAGIC_1024)
97 var ol: i64 = cat(outp, 0, "knowledge/staging/media/nbk/" as *u8); ol = cat(outp, ol, slug); ol = cat(outp, ol, ".nbk" as *u8); outp[ol]=0 as u8
98 let total: i64 = nbk_pack(outp, names, datas, lens, count)
99 if total <= 0 { pk_p("NBK-PACK-FAIL write\n" as *u8); sys_exit(1); return 1 }
100
101 pk_p("NBK-PACK slug=" as *u8); pk_p(slug); pk_p(" entries=" as *u8); pk_n(count); pk_p(" bytes=" as *u8); pk_n(total)
102 pk_p(" -> " as *u8); pk_p(outp); pk_p("\n" as *u8)
103 sys_exit(0); return 0
104}