113 lines
2.7 KiB
Rust
113 lines
2.7 KiB
Rust
|
|
use std::fmt::Write as _;
|
||
|
|
|
||
|
|
pub fn hex_full(bytes: &[u8]) -> String {
|
||
|
|
let mut s = String::with_capacity(bytes.len() * 2);
|
||
|
|
for b in bytes {
|
||
|
|
let _ = write!(&mut s, "{b:02x}");
|
||
|
|
}
|
||
|
|
s
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn xxd_dump(bytes: &[u8]) -> String {
|
||
|
|
let mut s = String::new();
|
||
|
|
let rows = (bytes.len() + 15) / 16;
|
||
|
|
for row in 0..rows {
|
||
|
|
let offset = row * 16;
|
||
|
|
let end = (offset + 16).min(bytes.len());
|
||
|
|
let slice = &bytes[offset..end];
|
||
|
|
let _ = write!(&mut s, "{offset:08x} ");
|
||
|
|
for i in 0..16 {
|
||
|
|
if i < slice.len() {
|
||
|
|
let _ = write!(&mut s, "{:02x} ", slice[i]);
|
||
|
|
} else {
|
||
|
|
s.push_str(" ");
|
||
|
|
}
|
||
|
|
if i == 7 {
|
||
|
|
s.push(' ');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
s.push_str(" |");
|
||
|
|
for b in slice {
|
||
|
|
if (0x20..0x7f).contains(b) {
|
||
|
|
s.push(*b as char);
|
||
|
|
} else {
|
||
|
|
s.push('.');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
s.push_str("|\n");
|
||
|
|
}
|
||
|
|
s
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn print_section(title: &str) {
|
||
|
|
println!();
|
||
|
|
println!("================================================================================");
|
||
|
|
println!("== {title}");
|
||
|
|
println!("================================================================================");
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn print_subsection(title: &str) {
|
||
|
|
println!();
|
||
|
|
println!("-- {title} --");
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn print_field(label: &str, value: &str) {
|
||
|
|
println!(" {label:<24} {value}");
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn print_kv(label: &str, value: impl std::fmt::Display) {
|
||
|
|
println!(" {label:<24} {value}");
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn print_note(text: &str) {
|
||
|
|
println!(" [note] {text}");
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn print_warn(text: &str) {
|
||
|
|
eprintln!(" [warn] {text}");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use super::*;
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn hex_full_empty() {
|
||
|
|
assert_eq!(hex_full(&[]), "");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn hex_full_single_byte() {
|
||
|
|
assert_eq!(hex_full(&[0xAB]), "ab");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn hex_full_preserves_order() {
|
||
|
|
assert_eq!(hex_full(&[0x01, 0x23, 0xAB, 0xCD]), "0123abcd");
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn xxd_dump_exact_16_bytes() {
|
||
|
|
let bytes: [u8; 16] = [
|
||
|
|
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
|
||
|
|
0xEE, 0xFF,
|
||
|
|
];
|
||
|
|
let out = xxd_dump(&bytes);
|
||
|
|
assert!(out.starts_with("00000000 "));
|
||
|
|
assert!(out.contains("00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff"));
|
||
|
|
assert!(out.contains("|"));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn xxd_dump_short_row_padded() {
|
||
|
|
let out = xxd_dump(&[0xAA, 0xBB]);
|
||
|
|
assert!(out.starts_with("00000000 aa bb "));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn xxd_dump_ascii_column() {
|
||
|
|
let out = xxd_dump(b"Hello");
|
||
|
|
assert!(out.contains("|Hello|"));
|
||
|
|
}
|
||
|
|
}
|