nx_hexdump.nx source
↩ module page · 137 lines · 4418 B
1// hexdump.nx -- xxd-style byte buffer dump.
2//
3// Produces human-readable byte listings like:
4// 00000000: 4865 6c6c 6f2c 2057 6f72 6c64 210a Hello, World!.
5// 0000000d: 4e69 7368 690a Nishi.
6//
7// Used for: debugging crypto outputs, network packet dumps, IR
8// inspection, anything where hex + ASCII side-by-side is the
9// right debug lens.
10//
11// Format (xxd default):
12// <8-hex offset>: <8 pairs of 2-hex bytes, space between pairs> <ASCII>
13// Bytes 0x20..0x7E render as themselves; others as '.'.
14// 16 bytes per row.
15//
16// Invariants:
17// HD1 Output is deterministic: same input bytes -> same output.
18// HD2 ASCII column padded so rows align visually -- last row
19// with <16 bytes left gets padding spaces.
20// HD3 Offsets are 8 hex digits (fits up to 4 GiB; enough for
21// every realistic buffer).
22
23// nx_safety_envelope:
24// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
25// sil_target: SIL1
26// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
27// verdict: NOT_YET_EVALUATED
28//
29// nx_capability_manifest:
30// variant_class: hex_dump_formatter
31// variant_id: hex_dump_xxd_v1
32// requires_isa: [rv64imac, x86_64]
33// requires_syscalls: [write]
34// requires_ram_min_b: 256
35// tier_floor: NX_TIER_MCU
36// tier_ceiling: NX_TIER_HPC
37// cost_model:
38// flops_per_n: 4.0 // ~4 ops per byte (hex char + sep)
39// bytes_per_n: 4.0 // 2 hex chars + space + ascii char per input byte
40// syscalls_per_n: 0.0625 // 1 write per 16-byte row
41// adversary_class: THREAT_OPPORTUNISTIC
42
43import "nx_syscalls.nx"
44
45// Lowercase hex encode single byte to out[pos..pos+2].
46func hd_emit_hex_byte(out: *u8, pos: i64, b: i64) -> i64 {
47 let hi: i64 = (b >> 4) & 0xF
48 let lo: i64 = b & 0xF
49 var hc: i64 = 0x30 + hi
50 if hi > 9 { hc = 0x61 + hi - 10 }
51 var lc: i64 = 0x30 + lo
52 if lo > 9 { lc = 0x61 + lo - 10 }
53 out[pos] = hc
54 out[pos + 1] = lc
55 return 2
56}
57
58// 8-hex-digit offset.
59func hd_emit_offset(out: *u8, pos: i64, off: i64) -> i64 {
60 var i: i64 = 7
61 var v: i64 = off
62 while i >= 0 {
63 let nib: i64 = v & 0xF
64 var c: i64 = 0x30 + nib
65 if nib > 9 { c = 0x61 + nib - 10 }
66 out[pos + i] = c
67 v = (v >> 4) & 0x0FFFFFFFFFFFFFFF
68 i = i - 1
69 }
70 return 8
71}
72
73// Dump `n` bytes from `data` into `out`, xxd-style. Returns
74// number of bytes written.
75func hexdump(data: *u8, n: i64, out: *u8) -> i64 {
76 var op: i64 = 0
77 var off: i64 = 0
78 while off < n {
79 // Offset + ": ".
80 op = op + hd_emit_offset(out, op, off)
81 out[op] = 0x3A; op = op + 1
82 out[op] = 0x20; op = op + 1
83
84 // 16 bytes; pairs separated by spaces. Bytes 0..7, space,
85 // 8..15 (standard xxd layout uses one space per pair).
86 let row_end: i64 = off + 16
87 var i: i64 = 0
88 while i < 16 {
89 if off + i < n {
90 op = op + hd_emit_hex_byte(out, op, data[off + i])
91 } else {
92 // Padding blanks for alignment.
93 out[op] = 0x20; op = op + 1
94 out[op] = 0x20; op = op + 1
95 }
96 if (i & 1) == 1 {
97 out[op] = 0x20; op = op + 1
98 }
99 i = i + 1
100 }
101
102 // ASCII column: 2 extra spaces before.
103 out[op] = 0x20; op = op + 1
104 i = 0
105 while i < 16 {
106 if off + i < n {
107 let b: i64 = data[off + i]
108 if b < 0x20 { out[op] = 0x2E }
109 else {
110 if b > 0x7E { out[op] = 0x2E }
111 else { out[op] = b }
112 }
113 op = op + 1
114 } else {
115 out[op] = 0x20; op = op + 1
116 }
117 i = i + 1
118 }
119 out[op] = 0x0A; op = op + 1
120
121 off = off + 16
122 }
123 return op
124}
125
126// Compile-only smoke: dump "Hello, World!\n" (14 bytes).
127func main() -> i64 {
128 let data: *u8 = "Hello, World!\n"
129 let out: *u8 = sys_mmap(256)
130 let n: i64 = hexdump(data, 14, out)
131 // Should produce exactly one row: "00000000: ..." + newline.
132 if n == 0 { return 1 }
133 if out[0] != 0x30 { return 2 } // '0'
134 if out[8] != 0x3A { return 3 } // ':'
135 if out[10] != 0x34 { return 4 } // '4' of "48" ('H' = 0x48)
136 return 0
137}