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