dbgtools_hexdump/lib.rs
1//! A hex dumper which calls a closure to allow the application to choose how
2//! to output the dump.
3//!
4//! # Example: Dumping a struct
5//! ```
6//! use dbgtools_hexdump::{Config, hexdump};
7//!
8//! struct MyStruct {
9//! eight: u8,
10//! sixteen: u16,
11//! thirtytwo: u32
12//! }
13//!
14//! let data = MyStruct { eight: 8, sixteen: 16, thirtytwo: 32 };
15//! hexdump(Config::default(), &data, |offs, hex, ascii| {
16//! println!("{:08x} {} {}", offs, hex, ascii);
17//! });
18//! ```
19//!
20//! # Example: Dumping a struct with addresses
21//! Sometimes it may be useful to include the real addresses in dumped buffers.
22//! This can be accomplished by adding a base offset to the configuration
23//! context.
24//! ```
25//! use dbgtools_hexdump::{Config, hexdump};
26//!
27//! struct MyStruct {
28//! eight: u8,
29//! sixteen: u16,
30//! thirtytwo: u32
31//! }
32//!
33//! let data = MyStruct { eight: 8, sixteen: 16, thirtytwo: 32 };
34//! hexdump(Config {
35//! offs: &data as *const _ as usize,
36//! ..Default::default()
37//! }, &data, |offs, hex, ascii| {
38//! println!("{:08x} {} {}", offs, hex, ascii);
39//! });
40//! ```
41
42#![deny(missing_docs)]
43#![deny(missing_crate_level_docs)]
44#![deny(missing_doc_code_examples)]
45
46use std::borrow::Borrow;
47
48/// Return a `Sized` object as a byte slice. 😬
49///
50/// Warning: Reading uninitialized memory is UB.
51pub fn asbuf<T: Sized>(buf: &T) -> &[u8] {
52 // SAFETY: No. :(
53 unsafe {
54 std::slice::from_raw_parts(
55 buf as *const T as *const u8,
56 std::mem::size_of::<T>()
57 )
58 }
59}
60
61/// Hex dumper configuration context.
62pub struct Config {
63 /// Number of columns in hex dump. Defaults to 16.
64 pub cols: usize,
65
66 /// A base offset. Defaults to 0. If it's useful to display the addresses
67 /// of a dumped buffer, this can be set to the initial address of the
68 /// buffer.
69 pub offs: usize
70}
71
72impl Default for Config {
73 fn default() -> Self {
74 Config { cols: 16, offs: 0 }
75 }
76}
77
78/// Generate a hex dump of a `Sized` object and call a closure to process each
79/// hex dump line.
80///
81/// ```
82/// use dbgtools_hexdump::{Config, hexdump};
83///
84/// struct MyStruct {
85/// eight: u8,
86/// sixteen: u16,
87/// thirtytwo: u32
88/// }
89///
90/// let data = MyStruct { eight: 8, sixteen: 16, thirtytwo: 32 };
91///
92/// hexdump(Config::default(), &data, |offs, hex, ascii| {
93/// println!("{:08x} {} {}", offs, hex, ascii);
94/// });
95/// ```
96pub fn hexdump<C, T, F>(cfg: C, buf: &T, f: F)
97where
98 C: Borrow<Config>,
99 T: Sized,
100 F: Fn(usize, &str, &str)
101{
102 let buf = asbuf(buf);
103
104 hexdump_buf(cfg, buf, f)
105}
106
107
108/// Generate a hex dump of a byte buffer (`&[u8]`) and call a closure to
109/// process each hex dump line.
110///
111/// ```
112/// use dbgtools_hexdump::{Config, hexdump_buf};
113///
114/// let data: &[u8] = &[1, 2, 3, 4];
115///
116/// hexdump_buf(Config::default(), &data, |offs, hex, ascii| {
117/// println!("{:08x} {} {}", offs, hex, ascii);
118/// });
119/// ```
120pub fn hexdump_buf<C, F>(cfg: C, buf: &[u8], f: F)
121where
122 C: Borrow<Config>,
123 F: Fn(usize, &str, &str)
124{
125 let cfg = cfg.borrow();
126
127 if cfg.cols == 0 {
128 // derpy caller
129 return;
130 }
131
132 let mut offset = cfg.offs;
133
134 let mut ascii = String::new();
135
136 for block in buf.chunks(cfg.cols) {
137 let this_offs = offset;
138
139 ascii.clear();
140
141 let mut vals = Vec::new();
142 for byte in block {
143 vals.push(format!("{:02x}", byte));
144
145 if *byte < 0x20 || *byte > 0x7e {
146 ascii.push('.');
147 } else {
148 ascii.push(char::from(*byte));
149 }
150
151 offset += 1;
152 }
153
154 let rem = cfg.cols - vals.len();
155 if rem > 0 {
156 let rest_it = std::iter::repeat(" ".to_string()).take(rem);
157 vals.extend(rest_it);
158
159 let rest_ascii = String::from(" ").repeat(rem);
160 ascii.push_str(&rest_ascii);
161 }
162
163 let hex_str = vals.join(" ");
164
165 f(this_offs, &hex_str, &ascii);
166 }
167}
168
169// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :