code wiki / (root) / nx_hex.nx

nx_hex.nx source

↩ module page · 185 lines · 6079 B

1// nx_hex.nx -- hex + binary + byte dump helpers. 2// 3// Every enterprise toolchain has a `hexdump` equivalent: raw byte 4// regions rendered in a stable human-readable format for 5// debugging memory state. This module provides three formats: 6// 7// nx_hex_i64 i64 as 16-char hex (no prefix, no pad-space) 8// nx_hex_bytes byte array as `hh hh hh ...` 9// nx_hex_dump full hexdump-C-style: offset + 16 hex + ASCII 10// 11// All write to stderr (fd 2) by default to avoid collision with 12// program stdout. Deterministic output -- no timestamps, no 13// tool-version headers. F6-compatible. 14// 15// Enterprise grade means: 16// * fixed-width columns (sortable, greppable) 17// * ASCII fallback for unprintable bytes (prints '.') 18// * explicit region length (no overruns on short buffers) 19// * offset prefix shows 8-digit hex (up to 4 GB regions) 20// * no dependency on the filesystem -- pure fd write 21// 22// Paired with nx_log (levels + tags) and nx_metrics (counters), 23// nx_hex closes the "what do I see when I'm lost" gap. Every 24// diagnostic should use ONE of: tagged log, numeric metric, or 25// hex dump. Nothing else. 26 27// nx_safety_envelope: 28// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 29// sil_target: SIL1 30// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 31// verdict: NOT_YET_EVALUATED 32 33import "syscalls.nx" 34 35// Convert a nibble (0-15) to an ASCII hex digit. Lowercase 36// matches Prometheus + standard hex-dump conventions. 37func nx_hex_nibble(n: i64) -> i64 { 38 if n < 10 { return 0x30 + n } // '0'..'9' 39 return 0x61 + (n - 10) // 'a'..'f' 40} 41 42// Emit `v` to `fd` as 16 characters of lowercase hex, no prefix. 43// Typical use: dumping a pointer address or a 64-bit word. 44func nx_hex_i64(fd: i64, v: i64) -> i64 { 45 let buf: *u8 = sys_mmap(32) 46 var i: i64 = 0 47 while i < 16 { 48 let nib: i64 = (v >> ((15 - i) * 4)) & 0xF 49 buf[i] = nx_hex_nibble(nib) 50 i = i + 1 51 } 52 sys_write(fd, buf, 16) 53 return 0 54} 55 56// Emit `v` as N lowercase hex chars (N in 1..16). Used for fixed- 57// width offset displays in hexdump. 58func nx_hex_i64_n(fd: i64, v: i64, n: i64) -> i64 { 59 if n < 1 { return 0 } 60 if n > 16 { return 0 } 61 let buf: *u8 = sys_mmap(32) 62 var i: i64 = 0 63 while i < n { 64 let shift: i64 = (n - 1 - i) * 4 65 let nib: i64 = (v >> shift) & 0xF 66 buf[i] = nx_hex_nibble(nib) 67 i = i + 1 68 } 69 sys_write(fd, buf, n) 70 return 0 71} 72 73// Emit a single byte as two hex chars. 74func nx_hex_u8(fd: i64, b: i64) -> i64 { 75 let buf: *u8 = sys_mmap(8) 76 buf[0] = nx_hex_nibble((b >> 4) & 0xF) 77 buf[1] = nx_hex_nibble(b & 0xF) 78 sys_write(fd, buf, 2) 79 return 0 80} 81 82// Emit `n` bytes of buf as space-separated hex pairs. 83// nx_hex_bytes(2, p, 4) -> "de ad be ef" 84func nx_hex_bytes(fd: i64, buf: *u8, n: i64) -> i64 { 85 var i: i64 = 0 86 while i < n { 87 if i > 0 { sys_write(fd, " " as *u8, 1) } 88 nx_hex_u8(fd, buf[i] & 0xFF) 89 i = i + 1 90 } 91 return 0 92} 93 94// Full `hexdump -C` style dump. 16 bytes per row: 95// <8-hex-offset> <16 hex pairs> |<16 printable-or-dot>| 96// For short buffers the last row is padded with spaces where bytes 97// are missing so column alignment stays fixed-width. 98func nx_hex_dump(fd: i64, buf: *u8, n: i64) -> i64 { 99 var row: i64 = 0 100 while row * 16 < n { 101 // Offset: 8 hex digits, 2 spaces 102 nx_hex_i64_n(fd, row * 16, 8) 103 sys_write(fd, " " as *u8, 2) 104 105 // 16 hex pairs, space-separated; extra space in the middle 106 var col: i64 = 0 107 while col < 16 { 108 let pos: i64 = row * 16 + col 109 if col == 8 { sys_write(fd, " " as *u8, 1) } 110 if pos < n { 111 nx_hex_u8(fd, buf[pos] & 0xFF) 112 if col < 15 { sys_write(fd, " " as *u8, 1) } 113 } 114 if pos >= n { 115 // Padding to keep column width fixed. 116 sys_write(fd, " " as *u8, 2) 117 if col < 15 { sys_write(fd, " " as *u8, 1) } 118 } 119 col = col + 1 120 } 121 122 // ASCII section: 2 spaces, bar, 16 chars, bar, newline 123 sys_write(fd, " |" as *u8, 3) 124 col = 0 125 while col < 16 { 126 let pos: i64 = row * 16 + col 127 if pos < n { 128 let b: i64 = buf[pos] & 0xFF 129 if b >= 0x20 { 130 if b < 0x7F { 131 sys_write(fd, buf + pos, 1) 132 } else { 133 sys_write(fd, "." as *u8, 1) 134 } 135 } 136 if b < 0x20 { 137 sys_write(fd, "." as *u8, 1) 138 } 139 } 140 if pos >= n { 141 sys_write(fd, " " as *u8, 1) 142 } 143 col = col + 1 144 } 145 sys_write(fd, "|\n" as *u8, 2) 146 147 row = row + 1 148 } 149 return 0 150} 151 152// --- self-test ------------------------------------------------------ 153 154func main() -> i64 { 155 // nx_hex_nibble basic cases 156 if nx_hex_nibble(0) != 0x30 { return __syscall(93, 10, 0, 0, 0, 0, 0) } 157 if nx_hex_nibble(9) != 0x39 { return __syscall(93, 11, 0, 0, 0, 0, 0) } 158 if nx_hex_nibble(10) != 0x61 { return __syscall(93, 12, 0, 0, 0, 0, 0) } 159 if nx_hex_nibble(15) != 0x66 { return __syscall(93, 13, 0, 0, 0, 0, 0) } 160 161 // Build a test buffer with known bytes 162 let buf: *u8 = sys_mmap(64) 163 // 'deadbeef' pattern + ASCII + control chars 164 buf[0] = 0xDE 165 buf[1] = 0xAD 166 buf[2] = 0xBE 167 buf[3] = 0xEF 168 buf[4] = 0x48 // 'H' 169 buf[5] = 0x65 // 'e' 170 buf[6] = 0x6C // 'l' 171 buf[7] = 0x6C // 'l' 172 buf[8] = 0x6F // 'o' 173 buf[9] = 0x0A // '\n' (control, -> '.') 174 buf[10] = 0x00 // NUL (control, -> '.') 175 176 // Demonstrate the three emitters. 177 sys_write(2, "i64 hex: " as *u8, 9) 178 nx_hex_i64(2, 0xDEADBEEFCAFEBABE) 179 sys_write(2, "\nbytes: " as *u8, 8) 180 nx_hex_bytes(2, buf, 11) 181 sys_write(2, "\ndump:\n" as *u8, 7) 182 nx_hex_dump(2, buf, 11) 183 184 return 0 185}