code wiki / _hdl_build / nx_b64_cli.nx

nx_b64_cli.nx source

↩ module page · 101 lines · 5173 B

1// nx_b64_cli.nx -- base64 a file to stdout (and optionally to a file), and DECODE a base64 file 2// back to bytes. WHY THIS EXISTS: runtime/nx_base64.nx carries a complete b64_encode/b64_decode 3// pair and NO main, so the capability could not be INVOKED. The encode half shipped 2026-08-06; 4// the DECODE half stayed uninvokable -- which made base64 a one-way transport: bytes could leave 5// the estate as text but text could not become bytes again, so binary INGEST (the rigged-donor 6// FBX lane, 2026-08-23) had no sovereign door. This adds the missing verb by composing the SAME 7// lib -- one codec, two verbs. 8// 9// BUFFER-CAP LAW (operator 2026-08-14: a ceiling that has to be guessed is a defect generator): 10// the old BC_MAX_IN 8 MiB / BC_MAX_OUT 12 MiB REFUSED any file over 8 MiB -- a 73.6 MB donor FBX 11// (98 MB as base64) could never pass. For FILE work there is no guess to make: sys_read_file 12// sizes its buffer from the file, and the OUTPUT bound is ARITHMETIC from the input length 13// (encode: 4*ceil(n/3) + newline; decode: 3*n/4). Both derived, both announced, no cap remains. 14// usage: nx_b64_cli <infile> [outfile] encode (unchanged contract) 15// nx_b64_cli dec <in.b64> <outfile> decode; announces bytes_in/bytes_out 16// exit 0 ok, 1 usage, 2 unreadable/empty, 3 write-failed 17// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 18import "nx_syscalls.nx" 19import "nx_base64.nx" 20 21const BC_ENC_UNIT_IN: i64 = 3 // encode consumes 3 bytes... 22const BC_ENC_UNIT_OUT: i64 = 4 // ...and emits 4 chars; decode is the inverse 23const BC_TAIL_SLACK: i64 = 8 // newline + NUL + rounding of the final group 24const BC_VERB_DEC: i64 = 100 // 'd' 25const BC_EXIT_OK: i64 = 0 26const BC_EXIT_USAGE: i64 = 1 27const BC_EXIT_UNREADABLE: i64 = 2 28const BC_EXIT_WRITEFAIL: i64 = 3 29 30func bc_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 31func bc_putn(v: i64) -> i64 { 32 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 } 33 var m: i64 = v 34 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } 35 let d: *u8 = sys_mmap(24) 36 var k: i64 = 0 37 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 38 let o: *u8 = sys_mmap(24) 39 var w: i64 = 0 40 while w < k { o[w] = d[k - 1 - w]; w = w + 1 } 41 sys_write(1, o, k) 42 return 0 43} 44func bc_eq(a: *u8, b: *u8) -> i64 { 45 var i: i64 = 0 46 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 47 if b[i] != (0 as u8) { return 0 } 48 return 1 49} 50 51func main(argc: i64, argv: *i64) -> i64 { 52 if argc < 2 { bc_puts("usage: nx_b64_cli <infile> [outfile] | dec <in.b64> <outfile>\n" as *u8); return BC_EXIT_USAGE } 53 54 if bc_eq(argv[1] as *u8, "dec" as *u8) == 1 { 55 if argc < 4 { bc_puts("usage: nx_b64_cli dec <in.b64> <outfile>\n" as *u8); return BC_EXIT_USAGE } 56 let lb: *i64 = sys_mmap(16) as *i64 57 let buf: *u8 = sys_read_file(argv[2] as *u8, lb) 58 if (buf as i64) == 0 { bc_puts("REJECT: cannot read input\n" as *u8); return BC_EXIT_UNREADABLE } 59 var n: i64 = lb[0] 60 if n <= 0 { bc_puts("REJECT: empty input\n" as *u8); return BC_EXIT_UNREADABLE } 61 // tolerate a trailing newline from the encode verb / text transports 62 while n > 0 { 63 let c: i64 = (buf[n - 1] & 0xff) as i64 64 if c == 10 { n = n - 1 } else { if c == 13 { n = n - 1 } else { break } } 65 } 66 // DERIVED output bound: decode emits 3 bytes per 4 chars, never more 67 let ocap: i64 = (n / BC_ENC_UNIT_OUT) * BC_ENC_UNIT_IN + BC_TAIL_SLACK 68 let out: *u8 = sys_mmap(ocap) 69 let dn: i64 = b64_decode(buf, n, out) 70 if dn <= 0 { bc_puts("REJECT: not decodable base64\n" as *u8); return BC_EXIT_UNREADABLE } 71 let fd: i64 = sys_openat_wr(argv[3] as *u8, MODE_0644) 72 if fd < 0 { bc_puts("REJECT: cannot open outfile\n" as *u8); return BC_EXIT_WRITEFAIL } 73 let wr: i64 = sys_write(fd, out, dn) 74 sys_close(fd) 75 bc_puts("B64-DEC bytes_in=" as *u8); bc_putn(n) 76 bc_puts(" bytes_out=" as *u8); bc_putn(dn) 77 bc_puts(" wrote=" as *u8); bc_putn(wr) 78 bc_puts("\n" as *u8) 79 if wr != dn { bc_puts("REJECT: short write\n" as *u8); return BC_EXIT_WRITEFAIL } 80 return BC_EXIT_OK 81 } 82 83 let path: *u8 = argv[1] as *u8 84 let lb2: *i64 = sys_mmap(16) as *i64 85 let buf2: *u8 = sys_read_file(path, lb2) 86 if (buf2 as i64) == 0 { bc_puts("REJECT: cannot read input\n" as *u8); return BC_EXIT_UNREADABLE } 87 let n2: i64 = lb2[0] 88 if n2 <= 0 { bc_puts("REJECT: empty input\n" as *u8); return BC_EXIT_UNREADABLE } 89 // DERIVED output bound: encode emits 4 chars per 3 bytes plus the trailing newline 90 let ecap: i64 = ((n2 + BC_ENC_UNIT_IN - 1) / BC_ENC_UNIT_IN) * BC_ENC_UNIT_OUT + BC_TAIL_SLACK 91 let out2: *u8 = sys_mmap(ecap) 92 let en: i64 = b64_encode(buf2, n2, out2) 93 sys_write(1, out2, en) 94 sys_write(1, "\n" as *u8, 1) 95 if argc >= 3 { 96 let opath: *u8 = argv[2] as *u8 97 let fd2: i64 = sys_openat_wr(opath, MODE_0644) 98 if fd2 >= 0 { sys_write(fd2, out2, en); sys_close(fd2) } 99 } 100 return BC_EXIT_OK 101}