diff options
| author | Wooster <wooster0@proton.me> | 2023-08-03 09:02:24 +0200 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2024-01-08 00:13:06 -0700 |
| commit | dfc4d618dd31c00e339344962a839db5208ade8f (patch) | |
| tree | 961ec373c4685a246cf746c9babc965dfe3a6dbe | |
| parent | f5978181e41e272b5c272440b9c543ead0357e2e (diff) | |
| download | zig-dfc4d618dd31c00e339344962a839db5208ade8f.tar.gz zig-dfc4d618dd31c00e339344962a839db5208ade8f.zip | |
general-use std.debug.hexdump for printing hexdumps
Recently, when I've been working with structures of data that is not
directly in RAM but rather laid out in bytes somewhere else,
it was always very useful to print out maybe the next 50 bytes or the
previous 50 bytes or so to see what's ahead or before me.
I would usually do this with a quick
`std.debug.print("{any}\n", .{bytes});` or something but the output is
not as nice obviously.
| -rw-r--r-- | lib/std/debug.zig | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 179e821da2..4d7c7ed9a3 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -104,6 +104,63 @@ pub fn getSelfDebugInfo() !*DebugInfo { } } +/// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned. +pub fn hexdump(bytes: []const u8) void { + hexdump_internal(bytes) catch {}; +} + +fn hexdump_internal(bytes: []const u8) !void { + const stderr = std.io.getStdErr(); + const ttyconf = std.io.tty.detectConfig(stderr); + const writer = stderr.writer(); + var chunks = mem.window(u8, bytes, 16, 16); + while (chunks.next()) |window| { + // 1. Print the address. + const address = (@intFromPtr(bytes.ptr) + 0x10 * (chunks.index orelse 0) / 16) - 0x10; + try ttyconf.setColor(writer, .dim); + // We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more. + // Also, make sure all lines are aligned by padding the address. + try writer.print("{x:0>[1]} ", .{ address, @sizeOf(usize) * 2 }); + try ttyconf.setColor(writer, .reset); + + // 2. Print the bytes. + for (window, 0..) |byte, index| { + try writer.print("{X:0>2} ", .{byte}); + if (index == 7) try writer.writeByte(' '); + } + try writer.writeByte(' '); + if (window.len < 16) { + var missing_columns = (16 - window.len) * 3; + if (window.len < 8) missing_columns += 1; + try writer.writeByteNTimes(' ', missing_columns); + } + + // 3. Print the characters. + for (window) |byte| { + if (std.ascii.isPrint(byte)) { + try writer.writeByte(byte); + } else { + // TODO: remove this `if` when https://github.com/ziglang/zig/issues/7600 is fixed + if (ttyconf == .windows_api) { + try writer.writeByte('.'); + continue; + } + + // Let's print some common control codes as graphical Unicode symbols. + // We don't want to do this for all control codes because most control codes apart from + // the ones that Zig has escape sequences for are likely not very useful to print as symbols. + switch (byte) { + '\n' => try writer.writeAll("␊"), + '\r' => try writer.writeAll("␍"), + '\t' => try writer.writeAll("␉"), + else => try writer.writeByte('.'), + } + } + } + try writer.writeByte('\n'); + } +} + /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. /// TODO multithreaded awareness pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
