|  | #![allow(unused)] | 
        
          |  |  | 
        
          |  | use std::fmt::{self, Display}; | 
        
          |  | use std::iter::repeat; | 
        
          |  |  | 
        
          |  | #[macro_export] | 
        
          |  | macro_rules! hex_compare { | 
        
          |  | ($($name:expr => $data:expr;)*) => { | 
        
          |  | HexCompare::new(vec![$( | 
        
          |  | HexColumn::new($name, $data, false), | 
        
          |  | )*]) | 
        
          |  | }; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | pub struct HexColumn<'a> { | 
        
          |  | name: String, | 
        
          |  | data: &'a [u8], | 
        
          |  | text: bool, | 
        
          |  | } | 
        
          |  |  | 
        
          |  | impl<'a> HexColumn<'a> { | 
        
          |  | pub fn new<S: Into<String>>(name: S, data: &'a [u8], text: bool) -> Self { | 
        
          |  | HexColumn{ | 
        
          |  | name: name.into(), | 
        
          |  | data, | 
        
          |  | text: false, | 
        
          |  | } | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | pub struct HexCompare<'a> { | 
        
          |  | ident: String, | 
        
          |  | tab_head: bool, | 
        
          |  | line_num: bool, | 
        
          |  | data_cols: Option<usize>, | 
        
          |  | max_line: Option<usize>, | 
        
          |  | max_width: Option<usize>, | 
        
          |  | groups: Vec<HexColumn<'a>>, | 
        
          |  | } | 
        
          |  |  | 
        
          |  | impl<'a> HexCompare<'a> { | 
        
          |  | pub fn new(groups: Vec<HexColumn<'a>>) -> Self { | 
        
          |  | HexCompare { | 
        
          |  | ident: String::new(), | 
        
          |  | tab_head: false, | 
        
          |  | line_num: true, | 
        
          |  | data_cols: None, | 
        
          |  | max_line: None, | 
        
          |  | max_width: None, | 
        
          |  | groups, | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | pub fn ident(mut self, ident: &str) -> Self { | 
        
          |  | self.ident = ident.to_owned(); | 
        
          |  | self | 
        
          |  | } | 
        
          |  |  | 
        
          |  | pub fn tab_head(mut self, th: bool) -> Self { | 
        
          |  | self.tab_head = th; | 
        
          |  | self | 
        
          |  | } | 
        
          |  |  | 
        
          |  | pub fn line_num(mut self, ln: bool) -> Self { | 
        
          |  | self.line_num = ln; | 
        
          |  | self | 
        
          |  | } | 
        
          |  |  | 
        
          |  | pub fn auto_data_cols(mut self) -> Self { | 
        
          |  | self.data_cols = None; | 
        
          |  | self | 
        
          |  | } | 
        
          |  |  | 
        
          |  | pub fn data_cols(mut self, pl: usize) -> Self { | 
        
          |  | self.data_cols = Some(pl); | 
        
          |  | self | 
        
          |  | } | 
        
          |  |  | 
        
          |  | pub fn no_max_line(mut self) -> Self { | 
        
          |  | self.max_line = None; | 
        
          |  | self | 
        
          |  | } | 
        
          |  |  | 
        
          |  | pub fn max_line(mut self, ml: usize) -> Self { | 
        
          |  | self.max_line = Some(ml); | 
        
          |  | self | 
        
          |  | } | 
        
          |  |  | 
        
          |  | pub fn max_width(mut self, mw: usize) -> Self { | 
        
          |  | self.max_width = Some(mw); | 
        
          |  | self | 
        
          |  | } | 
        
          |  |  | 
        
          |  | pub fn auto_width(mut self) -> Self { | 
        
          |  | self.max_width = None; | 
        
          |  | self | 
        
          |  | } | 
        
          |  |  | 
        
          |  | pub fn text(mut self, name: &str, text: bool) -> Self { | 
        
          |  | for i in 0..self.groups.len() { | 
        
          |  | if self.groups[i].name == name { | 
        
          |  | self.groups[i].text = text; | 
        
          |  | } | 
        
          |  | } | 
        
          |  | self | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | impl<'a> Display for HexCompare<'a> { | 
        
          |  | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 
        
          |  | if self.groups.len() == 0 { | 
        
          |  | return Ok(()); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // g: count of groups, t: count of groups with text, n: maximun data length, | 
        
          |  | // p: identation length, k: line number column length, w: maximum width, | 
        
          |  | // c: data column count, l: line count, omit: range of lines to be omited, | 
        
          |  | let g = self.groups.len(); | 
        
          |  | let t = self.groups.iter().filter(|d| d.text).count(); | 
        
          |  | let n = self.groups.iter().map(|d| d.data.len()).max().unwrap(); | 
        
          |  | let p = self.ident.len(); | 
        
          |  | let k = if !self.line_num { | 
        
          |  | 0 | 
        
          |  | } else { | 
        
          |  | hex_width(n).max(4) + 2 | 
        
          |  | }; | 
        
          |  | let mut w = self.max_width.unwrap_or(80); | 
        
          |  | if w + 1 < p + k + t + 3 * g + t { | 
        
          |  | w = p + k + t + 3 * g + t - 1; | 
        
          |  | } | 
        
          |  | let c = self | 
        
          |  | .data_cols | 
        
          |  | .unwrap_or_else(|| (w + 1 - p - k - t) / (3 * g + t)) | 
        
          |  | .max(1); | 
        
          |  | let l = if n % c == 0 { n / c } else { n / c + 1 }; | 
        
          |  |  | 
        
          |  | let mut omit = 0..0; | 
        
          |  | if let Some(mut m) = self.max_line { | 
        
          |  | if self.tab_head && m > 1 { | 
        
          |  | m -= 1; | 
        
          |  | } | 
        
          |  | if m > 1 && m < l { | 
        
          |  | omit = m / 2..m / 2 + (l - m); | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | if self.tab_head { | 
        
          |  | write!(f, "{}", self.ident)?; | 
        
          |  | write!(f, "{:>width$}", "|", width = k)?; | 
        
          |  | for (i, grp) in self.groups.iter().enumerate() { | 
        
          |  | if i != 0 { | 
        
          |  | write!(f, "|")?; | 
        
          |  | } | 
        
          |  | let w = if grp.text { 4 * c } else { 3 * c - 1 }; | 
        
          |  | write!(f, "{:^width$}", grp.name, width = w)?; | 
        
          |  | } | 
        
          |  | writeln!(f, "")?; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | let mut xbuf: Vec<u8> = Vec::with_capacity(c); | 
        
          |  | let mut tbuf = String::with_capacity(c); | 
        
          |  | let mut ln = 0; | 
        
          |  | while ln < l { | 
        
          |  | write!(f, "{}", self.ident)?; | 
        
          |  | if ln >= omit.start && ln < omit.end { | 
        
          |  | write!(f, "{:^width$}|", "...", width = k - 1)?; | 
        
          |  | for (i, grp) in self.groups.iter().enumerate() { | 
        
          |  | if i != 0 { | 
        
          |  | write!(f, "|")?; | 
        
          |  | } | 
        
          |  | let w = if grp.text { 4 * c } else { 3 * c - 1 }; | 
        
          |  | write!(f, "{:^width$}", "...", width = w)?; | 
        
          |  | } | 
        
          |  | writeln!(f, "")?; | 
        
          |  | ln = omit.end; | 
        
          |  | continue; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | let off = ln * c; | 
        
          |  | if self.line_num { | 
        
          |  | write!(f, "{:0width$x} |", off, width = k - 2)?; | 
        
          |  | } | 
        
          |  | for (i, grp) in self.groups.iter().enumerate() { | 
        
          |  | if i != 0 { | 
        
          |  | write!(f, "|")?; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | xbuf.truncate(0); | 
        
          |  | tbuf.truncate(0); | 
        
          |  | if off + c <= grp.data.len() { | 
        
          |  | xbuf.extend(&grp.data[off..off + c]); | 
        
          |  | tbuf.extend(grp.data[off..off + c].iter().map(|&b| view_acsii(b))); | 
        
          |  | } else if off < grp.data.len() { | 
        
          |  | xbuf.extend(&grp.data[off..]); | 
        
          |  | tbuf.extend(grp.data[off..].iter().map(|&b| view_acsii(b))); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | for (j, mayb) in xbuf | 
        
          |  | .iter() | 
        
          |  | .map(Some) | 
        
          |  | .chain(repeat(None).take(c - xbuf.len())) | 
        
          |  | .enumerate() | 
        
          |  | { | 
        
          |  | if j != 0 { | 
        
          |  | write!(f, " ")?; | 
        
          |  | } | 
        
          |  | if let Some(&b) = mayb { | 
        
          |  | write!(f, "{:02x}", b)?; | 
        
          |  | } else { | 
        
          |  | write!(f, "  ")?; | 
        
          |  | } | 
        
          |  | } | 
        
          |  | if grp.text { | 
        
          |  | write!(f, " {:<width$}", tbuf, width = c)?; | 
        
          |  | } | 
        
          |  | } | 
        
          |  | writeln!(f, "")?; | 
        
          |  | ln += 1; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | Ok(()) | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | fn hex_width(x: usize) -> usize { | 
        
          |  | let mut mask = std::usize::MAX; | 
        
          |  | while mask != 0 && x & (mask >> 1) == x { | 
        
          |  | mask >>= 1; | 
        
          |  | } | 
        
          |  | let mut n = 0; | 
        
          |  | while mask != 0 { | 
        
          |  | mask >>= 1; | 
        
          |  | n += 1; | 
        
          |  | } | 
        
          |  | if n % 4 == 0 { | 
        
          |  | n / 4 | 
        
          |  | } else { | 
        
          |  | n / 4 + 1 | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | fn view_acsii(byte: u8) -> char { | 
        
          |  | if byte < 32 || byte >= 127 { | 
        
          |  | '.' | 
        
          |  | } else { | 
        
          |  | byte as char | 
        
          |  | } | 
        
          |  | } |