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}