code wiki / (root) / nx_lib_serve.nx

nx_lib_serve.nx source

↩ module page · 130 lines · 5541 B

1// nx_lib_serve.nx -- sovereign QUERY/SERVE layer for the nishi-library, on 2// nx_lib_store. This is the MIGRATION rung: the program's read path moves to 3// .nx, reading the sovereign catalog store and emitting the JSON an API 4// returns -- REPLACING serve.py's SQLite _read_work path. No shell, no python, 5// no 3rd-party. Built + run by the WOMB. 6// license_tier: ORIGINAL | genealogy_id: nishi_library_sovereign_serve_2026_06_30 7import "nx_syscalls.nx" 8import "nx_lib_store.nx" 9const K_MAGIC_1048576: i64 = 1048576 10 11// append NUL-terminated s to out at off; return new off. 12func lsv_cat(out: *u8, off: i64, s: *u8) -> i64 { 13 var i: i64 = 0 14 while s[i] != (0 as u8) { out[off + i] = s[i]; i = i + 1 } 15 return off + i 16} 17 18// locate the Nth (0-based) TAB-delimited field of rec[0..reclen): set 19// outp[0]=start ptr, outlen[0]=len; return 1 if found, else 0. 20func lsv_field(rec: *u8, reclen: i64, n: i64, outp: *i64, outlen: *i64) -> i64 { 21 var idx: i64 = 0 22 var start: i64 = 0 23 var i: i64 = 0 24 while i <= reclen { 25 var atend: i64 = 0 26 if i == reclen { atend = 1 } else { if rec[i] == 9 as u8 { atend = 1 } } 27 if atend == 1 { 28 if idx == n { 29 outp[0] = (rec as i64) + start 30 outlen[0] = i - start 31 return 1 32 } 33 idx = idx + 1 34 start = i + 1 35 } 36 i = i + 1 37 } 38 return 0 39} 40 41// append the Nth field of rec, JSON-escaped (" and \), to out at off. 42func lsv_cat_field(out: *u8, off: i64, rec: *u8, reclen: i64, n: i64) -> i64 { 43 let po: *i64 = sys_mmap(16) as *i64 44 let lo: *i64 = sys_mmap(16) as *i64 45 if lsv_field(rec, reclen, n, po, lo) == 0 { return off } 46 let fp: *u8 = po[0] as *u8 47 let fn: i64 = lo[0] 48 var i: i64 = 0 49 var o: i64 = off 50 while i < fn { 51 let c: i64 = fp[i] as i64 52 if c == 34 { out[o] = 92 as u8; o = o + 1; out[o] = 34 as u8; o = o + 1 } 53 else { if c == 92 { out[o] = 92 as u8; o = o + 1; out[o] = 92 as u8; o = o + 1 } 54 else { out[o] = fp[i]; o = o + 1 } } 55 i = i + 1 56 } 57 return o 58} 59 60// serve one work as JSON into out; returns byte length, or -1 if not found. 61// record layout: title<TAB>doi<TAB>published<TAB>license<TAB>abstract 62func nx_lib_serve_work(hk: *u8, out: *u8) -> i64 { 63 let po: *i64 = sys_mmap(16) as *i64 64 let lo: *i64 = sys_mmap(16) as *i64 65 if nx_lib_store_get_work(hk, po, lo) < 0 { return 0 - 1 } 66 let rec: *u8 = po[0] as *u8 67 let reclen: i64 = lo[0] 68 var o: i64 = 0 69 o = lsv_cat(out, o, "{\"work_hk\":\"" as *u8) 70 o = lsv_cat(out, o, hk) 71 o = lsv_cat(out, o, "\",\"title\":\"" as *u8) 72 o = lsv_cat_field(out, o, rec, reclen, 0) 73 o = lsv_cat(out, o, "\",\"doi\":\"" as *u8) 74 o = lsv_cat_field(out, o, rec, reclen, 1) 75 o = lsv_cat(out, o, "\",\"published\":\"" as *u8) 76 o = lsv_cat_field(out, o, rec, reclen, 2) 77 o = lsv_cat(out, o, "\",\"license\":\"" as *u8) 78 o = lsv_cat_field(out, o, rec, reclen, 3) 79 o = lsv_cat(out, o, "\",\"abstract\":\"" as *u8) 80 o = lsv_cat_field(out, o, rec, reclen, 4) 81 o = lsv_cat(out, o, "\",\"authors\":\"" as *u8) 82 o = lsv_cat_field(out, o, rec, reclen, 5) 83 o = lsv_cat(out, o, "\"}" as *u8) 84 return o 85} 86 87// serve the catalog work-list as a JSON array of work_hks into out; returns len, or -1 IF IT WOULD NOT FIT. 88// ⛔CAPACITY IS A PARAMETER, NOT AN ASSUMPTION (2026-08-16). This used to write into `out` with no cap at 89// all, so every caller had to guess -- and the guess is wrong BY CONSTRUCTION, because the JSON emitted 90// here is STRICTLY LARGER than the index it reads: each entry adds two quotes and a comma. lhd_response 91// sized its `body` at exactly the index buffer's 1 MiB, so a full catalog overran it. 92// ★★★★★★A WRITER WITH NO CAPACITY ARGUMENT MAKES EVERY CALLER GUESS, AND THEY ALL GUESS THE INPUT SIZE. 93// Refusal (-1), never silent truncation: a caller can handle a refusal and cannot see a truncation. 94func nx_lib_serve_list(out: *u8, outcap: i64) -> i64 { 95 if outcap < 2 { return 0 - 1 } 96 let idxbuf: *u8 = sys_mmap(K_MAGIC_1048576) 97 let n: i64 = nx_lib_store_index(idxbuf) 98 var o: i64 = 0 99 var over: i64 = 0 100 out[o] = 91 as u8; o = o + 1 // '[' 101 var i: i64 = 0 102 var start: i64 = 0 103 var first: i64 = 1 104 while i <= n { 105 var atend: i64 = 0 106 if i == n { atend = 1 } else { if idxbuf[i] == 10 as u8 { atend = 1 } } 107 if atend == 1 { 108 if i > start { if over == 0 { 109 let need: i64 = 4 + (i - start) 110 if o + need > outcap { over = 1 } else { 111 if first == 0 { out[o] = 44 as u8; o = o + 1 } // ',' 112 first = 0 113 out[o] = 34 as u8; o = o + 1 // '"' 114 var c: i64 = start 115 while c < i { out[o] = idxbuf[c]; o = o + 1; c = c + 1 } 116 out[o] = 34 as u8; o = o + 1 // '"' 117 } 118 } } 119 start = i + 1 120 } 121 i = i + 1 122 } 123 // ⚠THE INDEX BUFFER WAS NEVER RELEASED: 1 MiB per call inside a NON-FORKING accept loop, i.e. a 124 // megabyte and a VMA per request against nx_lib_httpd's declared 1,000,000-request budget -- the 125 // documented vm.max_map_count path to a hard failure. ★NEVER ALLOCATE IN A HOT LOOP WITHOUT RELEASING. 126 sys_munmap(idxbuf, K_MAGIC_1048576) 127 if over == 1 { return 0 - 1 } 128 out[o] = 93 as u8; o = o + 1 // ']' 129 return o 130}