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 :