use crate::scannable::{ChunkIter, Scannable}; use core::ops::{Bound, Range, RangeBounds, RangeFull}; use core::fmt::{Display, Formatter}; const SEP: &str = " | "; pub struct HexDump<'s, T: Scannable + ?Sized, R: RangeBounds>(pub &'s T, pub R); pub fn hex< T: Scannable + ?Sized, R: RangeBounds >( data: &T, range:R ) -> HexDump { HexDump(data, range) } impl<'s, T: Scannable> HexDump<'s, T, RangeFull> { pub fn new(scannable: &'s T) -> Self { Self(scannable, ..) } } impl<'s, T: Scannable + ?Sized, R: RangeBounds> Display for HexDump<'s, T, R> { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { // calculate the end and the start addresses let (start, end) = { let r = self.0.range(); let start = match self.1.start_bound() { Bound::Included(i) => *i, Bound::Excluded(i) => i.saturating_add(1), Bound::Unbounded => 0, }.max(r.start); let end = match self.1.end_bound() { Bound::Included(i) => *i, Bound::Excluded(i) => i.saturating_sub(1), Bound::Unbounded => usize::MAX, }.min(r.end); (start, end) }; // the number of digits the address column should have let digits = if end == 0 { 4 } else { (end.ilog(16) as usize + 1).max(4) }; for (mut addr, chunk) in ChunkIter::new(self.0, start) { for chunk in chunk.chunks(16) { if addr > end { return Ok(()) } let chunk = &chunk[..(1 + end - addr).min(chunk.len())]; //╶───╴Column╶────────────────────────────────╴ write!(f, "{:0digits$X}{SEP}", addr, digits = digits)?; //╶───╴Bytes╶─────────────────────────────────╴ for (i, byte) in chunk.iter().enumerate() { if i != 0 { write!(f, " ")?; } write!(f, "{byte:02X}")?; } for i in (chunk.len()..16) { match i { 0 => write!(f, " ")?, _ => write!(f, " ")?, } } //╶───╴Text╶──────────────────────────────────╴ write!(f, "{SEP}")?; for &byte in chunk { match byte { 0x20..0x7e => write!(f, "{}", char::from(byte))?, _ => write!(f, ".")?, } } writeln!(f)?; addr += chunk.len() } } Ok(()) } }