code wiki / _hdl_build / nx_csv.nx

nx_csv.nx source

↩ module page · 97 lines · 6104 B

1// nx_csv.nx -- sovereign CSV -> HTML table viewer. A Nishi browser document renderer: turns a .csv into a clean 2// 0-JS HTML table the browser shows inline (first row = header). Handles quoted cells ("a,b" and "" escapes). 3// Same `html <in> <out> [dlurl]` interface as nx_docx/nx_md so nx_doc_view routes to it by registry. license_tier: ORIGINAL 4import "nx_syscalls.nx" 5import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 6const K_MAGIC_4194320: i64 = 4194320 7const K_MAGIC_4194304: i64 = 4194304 8const K_MAGIC_16777216: i64 = 16777216 9 10func cv_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8) { n=n+1 } return n } 11func cv_puts(s: *u8) -> i64 { sys_write(1, s, cv_slen(s)); return 0 } 12// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 13// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 14// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 15// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 16func cv_num(v: i64) -> i64 { nxi_out(v); return 0 } 17func cv_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 } 18func cv_esc1(dst: *u8, o: i64, c: i64) -> i64 { 19 if c==38 { return cv_cat(dst,o,"&amp;" as *u8) } 20 if c==60 { return cv_cat(dst,o,"&lt;" as *u8) } 21 if c==62 { return cv_cat(dst,o,"&gt;" as *u8) } 22 dst[o]=c as u8; return o+1 23} 24func cv_readfile(path: *u8, buf: *u8, cap: i64) -> i64 { 25 let fd: i64=sys_openat_rd(path); if fd<0 { return 0-1 } 26 var tot: i64=0; while tot<cap { let r: i64=sys_read(fd,(buf as i64+tot) as *u8,cap-tot); if r<=0 { break } tot=tot+r } 27 sys_close(fd); return tot 28} 29 30// emit one CSV row [i,end) as <tr> with <td>/<th> cells; returns new offset. tag = "td" or "th". 31func cv_row(out: *u8, off: i64, src: *u8, i: i64, end: i64, isheader: i64) -> i64 { 32 var o: i64=off 33 o=cv_cat(out,o,"<tr>" as *u8) 34 var p: i64=i 35 while p<=end { 36 if isheader==1 { o=cv_cat(out,o,"<th>" as *u8) } else { o=cv_cat(out,o,"<td>" as *u8) } 37 var quoted: i64=0 38 if p<end { if src[p]==(34 as u8) { quoted=1; p=p+1 } } 39 var run: i64=1 40 while run==1 { 41 if p>=end { run=0 } else { 42 let c: i64=src[p] as i64 43 if quoted==1 { 44 if c==34 { 45 if p+1<end { if src[p+1]==(34 as u8) { o=cv_esc1(out,o,34); p=p+2 } else { quoted=0; p=p+1 } } else { quoted=0; p=p+1 } 46 } else { o=cv_esc1(out,o,c); p=p+1 } 47 } else { 48 if c==44 { run=0 } else { o=cv_esc1(out,o,c); p=p+1 } 49 } 50 } 51 } 52 if isheader==1 { o=cv_cat(out,o,"</th>" as *u8) } else { o=cv_cat(out,o,"</td>" as *u8) } 53 if p<end { if src[p]==(44 as u8) { p=p+1 } else { p=end+1 } } else { p=end+1 } 54 } 55 o=cv_cat(out,o,"</tr>\n" as *u8) 56 return o 57} 58 59func cv_to_html(src: *u8, slen: i64, out: *u8, dlurl: *u8) -> i64 { 60 var o: i64=0 61 o=cv_cat(out,o,"<!DOCTYPE html>\n<html lang=en><head><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Document Preview - Nishi Office</title><style>\nbody{font:16px/1.6 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;margin:0;background:#eef1f5;color:#1a1d21}\nheader{background:#0f172a;color:#fff;padding:16px 24px}header h1{margin:0;font-size:17px;font-weight:600}\n.doc{max-width:960px;margin:22px auto;background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:24px;box-shadow:0 1px 4px rgba(15,23,42,.08);overflow-x:auto}\ntable{border-collapse:collapse;width:100%;font-size:14px}\nth,td{border:1px solid #cbd5e1;padding:7px 12px;text-align:left}\nth{background:#0f172a;color:#fff;position:sticky;top:0}\ntr:nth-child(even) td{background:#f8fafc}\n.bar{max-width:960px;margin:0 auto 6px;text-align:right}.dl{display:inline-block;padding:9px 18px;background:#2563eb;color:#fff;border-radius:8px;text-decoration:none;font-size:14px;font-weight:500}\nfooter{max-width:960px;margin:14px auto 46px;color:#64748b;font-size:13px;text-align:center}\n</style></head>\n<body>\n<header><h1>&#128196; Document Preview &mdash; Nishi Office</h1></header>\n<div class=doc>\n<table>\n" as *u8) 62 var i: i64=0; var firstrow: i64=1 63 while i<slen { 64 var e: i64=i 65 while e<slen { if src[e]==(10 as u8) { break } e=e+1 } 66 var end: i64=e 67 if end>i { if src[end-1]==(13 as u8) { end=end-1 } } 68 if end>i { 69 if firstrow==1 { o=cv_row(out,o,src,i,end,1); firstrow=0 } else { o=cv_row(out,o,src,i,end,0) } 70 } 71 i=e+1 72 } 73 o=cv_cat(out,o,"</table>\n</div>\n" as *u8) 74 if (dlurl as i64)!=0 { o=cv_cat(out,o,"<div class=bar><a class=dl href=\"" as *u8); o=cv_cat(out,o,dlurl); o=cv_cat(out,o,"\">&#11015; Download .csv</a></div>\n" as *u8) } 75 o=cv_cat(out,o,"<footer>Rendered natively from CSV by <b>nx_csv</b> &middot; viewable inline in the Nishi browser</footer>\n</body></html>\n" as *u8) 76 return o 77} 78 79func main(argc: i64, argv: *i64) -> i64 { 80 if argc<4 { cv_puts("usage: nx_csv html <in.csv> <out.html> [download_url]\n" as *u8); sys_exit(2); return 2 } 81 let cmd: *u8=argv[1] as *u8 82 if cmd[0]!=(104 as u8) { cv_puts("nx_csv: unknown command (html)\n" as *u8); sys_exit(2); return 2 } 83 let inp: *u8=argv[2] as *u8 84 let outp: *u8=argv[3] as *u8 85 var dlu: *u8=0 as *u8 86 if argc>4 { dlu=argv[4] as *u8 } 87 let src: *u8=sys_mmap(K_MAGIC_4194320) 88 let slen: i64=cv_readfile(inp, src, K_MAGIC_4194304) 89 if slen<0 { cv_puts("CSV-HTML-FAIL cannot-read\n" as *u8); sys_exit(1); return 1 } 90 let out: *u8=sys_mmap(K_MAGIC_16777216) 91 let n: i64=cv_to_html(src, slen, out, dlu) 92 let fd: i64=sys_openat_wr(outp, 0x1a4) 93 if fd<0 { cv_puts("CSV-HTML-FAIL cannot-write\n" as *u8); sys_exit(1); return 1 } 94 sys_write(fd, out, n); sys_close(fd) 95 cv_puts("CSV-HTML-OK path=" as *u8); cv_puts(outp); cv_puts(" bytes=" as *u8); cv_num(n); cv_puts("\n" as *u8) 96 sys_exit(0); return 0 97}