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 170 171 172 173 174 175 176 177 178 179
//! This crate provides functions to create human readable hex
//! dumps of binary data.
//!
const HEX_DIGIT : &[u8;16] = b"0123456789abcdef";
/// Write a simple hex dump of the given data to the given target.
/// The dump contains pairs of hex digits, separated by spaces, no
/// line breaks, decorations, etc.
///
/// # Examples
/// ```
/// let data = [0x00, 0x01, 0x02, 0x03];
/// let mut target = Vec::new();
/// qdhex::write_bare_dump_to_vec(&data, &mut target);
/// assert_eq!(target, b"00 01 02 03");
/// ```
pub fn write_bare_dump_to_vec(data: &[u8], target: &mut Vec<u8>) {
for byte in data {
target.push(HEX_DIGIT[(byte >> 4) as usize]);
target.push(HEX_DIGIT[(byte & 0x0f) as usize]);
target.push(b' ');
}
target.pop();
}
/// Create a simple hex dump of the given data. The dump contains pairs of
/// hex digits, separated by spaces, no line breaks, decorations, etc.
///
/// # Examples
/// ```
/// let data = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
/// let target = qdhex::bare_dump(&data);
/// assert_eq!(target, b"00 01 02 03 04 05");
/// ```
pub fn bare_dump(data: &[u8]) -> Vec<u8> {
let mut target = Vec::with_capacity(data.len() * 3 + 1);
write_bare_dump_to_vec(data, &mut target);
target
}
/// Create a simple hex dump of the given data. The dump contains pairs of
/// hex digits, separated by spaces, no line breaks, decorations, etc.
///
/// # Examples
/// ```
/// let data = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
/// let target = qdhex::bare_dump_string(&data);
/// assert_eq!(target, "00 01 02 03 04 05");
/// ```
///
pub fn bare_dump_string(data: &[u8]) -> String {
let vec = bare_dump(data);
// SAFETY: The dump is always valid UTF-8 since it only contains ASCII characters
unsafe { String::from_utf8_unchecked(vec) }
}
/// Write a formatted multi-line hex dump of the given data to the given target.
/// Dump lines are prefixed with the given offset. Each line contains up to 16
/// bytes, separated by spaces, followed by a space and the ASCII representation.
///
/// # Examples
/// ```
/// let data = &b"baadfood\xba\xad\xf0\x0dASDFasdf;lkj."[..];
/// let mut target = Vec::new();
/// qdhex::write_formatted_dump_to_vec(0x1000, &data, &mut target);
/// assert_eq!(target, br"1000 62 61 61 64 66 6f 6f 64 ba ad f0 0d 41 53 44 46 baadfood....ASDF
/// 1010 61 73 64 66 3b 6c 6b 6a 2e asdf;lkj.
/// ");
/// ```
pub fn write_formatted_dump_to_vec(offset: u32, data: &[u8], target: &mut Vec<u8>) {
let mut line_offset = offset;
for chunk in data.chunks(16) {
// Write the line offset
for i in 0..4 {
target.push(HEX_DIGIT[((line_offset >> (4 * (3 - i))) & 0x0f) as usize]);
}
target.push(b' ');
// Write the hex representation
write_bare_dump_to_vec(chunk, target);
// Pad the last line with spaces
for _ in chunk.len()..16 {
target.push(b' ');
target.push(b' ');
target.push(b' ');
}
// Write the ASCII representation
target.push(b' ');
for byte in chunk {
if *byte >= 0x20 && *byte <= 0x7e {
target.push(*byte);
}
else {
target.push(b'.');
}
}
target.push(b'\n');
line_offset += 16;
}
}
/// Create a formatted multi-line hex dump of the given data.
/// Dump lines are prefixed with the given offset. Each line contains up to 16
/// bytes, separated by spaces, followed by a space and the ASCII representation.
///
/// # Examples
/// ```
/// let data = &b"baadfood\xba\xad\xf0\x0dASDFasdf;lkj."[..];
/// let target = qdhex::formatted_dump(0x1000, &data);
/// assert_eq!(target, br"1000 62 61 61 64 66 6f 6f 64 ba ad f0 0d 41 53 44 46 baadfood....ASDF
/// 1010 61 73 64 66 3b 6c 6b 6a 2e asdf;lkj.
/// ");
/// ```
pub fn formatted_dump(offset: u32, data: &[u8]) -> Vec<u8> {
let lines = (data.len() + 15) / 16;
let size = lines * 70;
let mut target = Vec::with_capacity(size);
write_formatted_dump_to_vec(offset, data, &mut target);
target
}
/// Create a formatted multi-line hex dump of the given data.
/// Dump lines are prefixed with the given offset. Each line contains up to 16
/// bytes, separated by spaces, followed by a space and the ASCII representation.
///
/// # Examples
/// ```
/// let data = &b"baadfood\xba\xad\xf0\x0dASDFasdf;lkj."[..];
/// let target = qdhex::formatted_dump_string(0x1000, &data);
/// assert_eq!(target, "1000 62 61 61 64 66 6f 6f 64 ba ad f0 0d 41 53 44 46 baadfood....ASDF\n1010 61 73 64 66 3b 6c 6b 6a 2e asdf;lkj.\n");
/// ```
pub fn formatted_dump_string(offset: u32, data: &[u8]) -> String {
let vec = formatted_dump(offset, data);
// SAFETY: The dump is always valid UTF-8 since it only contains ASCII characters
unsafe { String::from_utf8_unchecked(vec) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bare_dump() {
let data: [u8; 9] = [0x12, 0x34, 0x56, 0x78, 0xab, 0xcd, 0xef, 0xa0, 0x0b];
let mut target = Vec::new();
write_bare_dump_to_vec(&data, &mut target);
assert_eq!(target, b"12 34 56 78 ab cd ef a0 0b");
let data: &[u8] = &b"Hello, World!"[..];
let mut target = Vec::new();
write_bare_dump_to_vec(&data, &mut target);
assert_eq!(target, b"48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21");
// Test with empty data
let data: &[u8] = &b""[..];
let mut target = Vec::new();
write_bare_dump_to_vec(&data, &mut target);
assert_eq!(target, b"");
// Test with one byte
let data: &[u8] = &b"\xab"[..];
let mut target = Vec::new();
write_bare_dump_to_vec(&data, &mut target);
assert_eq!(target, b"ab");
// Verify that the target is not cleared
let data: &[u8] = &b"\xab\x30"[..];
let mut target = b"Hello, World!".to_vec();
write_bare_dump_to_vec(&data, &mut target);
assert_eq!(target, b"Hello, World!ab 30");
}
}