code wiki / (root) / hexdump.nx

hexdump.nx source

↩ module page · 117 lines · 3723 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 23import "syscalls.nx" 24 25// Lowercase hex encode single byte to out[pos..pos+2]. 26func hd_emit_hex_byte(out: *u8, pos: i64, b: i64) -> i64 { 27 let hi: i64 = (b >> 4) & 0xF 28 let lo: i64 = b & 0xF 29 var hc: i64 = 0x30 + hi 30 if hi > 9 { hc = 0x61 + hi - 10 } 31 var lc: i64 = 0x30 + lo 32 if lo > 9 { lc = 0x61 + lo - 10 } 33 out[pos] = hc 34 out[pos + 1] = lc 35 return 2 36} 37 38// 8-hex-digit offset. 39func hd_emit_offset(out: *u8, pos: i64, off: i64) -> i64 { 40 var i: i64 = 7 41 var v: i64 = off 42 while i >= 0 { 43 let nib: i64 = v & 0xF 44 var c: i64 = 0x30 + nib 45 if nib > 9 { c = 0x61 + nib - 10 } 46 out[pos + i] = c 47 v = (v >> 4) & 0x0FFFFFFFFFFFFFFF 48 i = i - 1 49 } 50 return 8 51} 52 53// Dump `n` bytes from `data` into `out`, xxd-style. Returns 54// number of bytes written. 55func hexdump(data: *u8, n: i64, out: *u8) -> i64 { 56 var op: i64 = 0 57 var off: i64 = 0 58 while off < n { 59 // Offset + ": ". 60 op = op + hd_emit_offset(out, op, off) 61 out[op] = 0x3A; op = op + 1 62 out[op] = 0x20; op = op + 1 63 64 // 16 bytes; pairs separated by spaces. Bytes 0..7, space, 65 // 8..15 (standard xxd layout uses one space per pair). 66 let row_end: i64 = off + 16 67 var i: i64 = 0 68 while i < 16 { 69 if off + i < n { 70 op = op + hd_emit_hex_byte(out, op, data[off + i]) 71 } else { 72 // Padding blanks for alignment. 73 out[op] = 0x20; op = op + 1 74 out[op] = 0x20; op = op + 1 75 } 76 if (i & 1) == 1 { 77 out[op] = 0x20; op = op + 1 78 } 79 i = i + 1 80 } 81 82 // ASCII column: 2 extra spaces before. 83 out[op] = 0x20; op = op + 1 84 i = 0 85 while i < 16 { 86 if off + i < n { 87 let b: i64 = data[off + i] 88 if b < 0x20 { out[op] = 0x2E } 89 else { 90 if b > 0x7E { out[op] = 0x2E } 91 else { out[op] = b } 92 } 93 op = op + 1 94 } else { 95 out[op] = 0x20; op = op + 1 96 } 97 i = i + 1 98 } 99 out[op] = 0x0A; op = op + 1 100 101 off = off + 16 102 } 103 return op 104} 105 106// Compile-only smoke: dump "Hello, World!\n" (14 bytes). 107func main() -> i64 { 108 let data: *u8 = "Hello, World!\n" 109 let out: *u8 = sys_mmap(256) 110 let n: i64 = hexdump(data, 14, out) 111 // Should produce exactly one row: "00000000: ..." + newline. 112 if n == 0 { return 1 } 113 if out[0] != 0x30 { return 2 } // '0' 114 if out[8] != 0x3A { return 3 } // ':' 115 if out[10] != 0x34 { return 4 } // '4' of "48" ('H' = 0x48) 116 return 0 117}