hex_table/
hex_table.rs

1use std::io;
2
3pub struct HexTable {
4    columns: usize,
5    offset: usize,
6    header: bool,
7    ascii: bool,
8    zeros: bool,
9}
10
11impl Default for HexTable {
12    fn default() -> Self {
13        HexTable { columns: 16, offset: 0, header: false, ascii: true, zeros: true }
14    }
15}
16
17impl HexTable {
18    pub fn new(columns: usize, offset: usize, header: bool, ascii: bool, zeros: bool) -> HexTable {
19        HexTable { columns, offset, header, ascii, zeros }
20    }
21
22    pub fn format<Writer: io::Write>(&self, data: &[u8], out: &mut Writer) -> io::Result<()> {
23        let mut col_pos = 0;
24        let mut row_pos = 0;
25        let mut ascii_pos = 0;
26
27        let data_len = data.len();
28        let col_pad_len = Self::count_hex_digits(self.columns).max(2);
29        let columns_remaining = data_len % self.columns;
30
31        let columns_empty = if columns_remaining != 0 {
32            self.columns - columns_remaining
33        } else {
34            0
35        };
36
37        let offset_str_len = Self::count_hex_digits(
38            if columns_remaining != 0 {
39                self.offset + data.len() - columns_remaining
40            } else {
41                self.offset + data.len() - columns_remaining - 1
42            }
43        );
44
45        let last_row_pad_len = columns_empty * (col_pad_len + 1);
46        let table_len_needed = data_len + columns_empty;
47
48        if self.header {
49            let offset = " ".repeat(offset_str_len + 2);
50            let line = "-".repeat((self.columns * (col_pad_len + 1)) - 1);
51
52            write!(out, "{}", offset)?;
53
54            for i in 0..self.columns {
55                write!(out, "{:0>1$X} ", i, col_pad_len)?;
56            }
57
58            write!(out, "\n{}{}\n", offset, line)?;
59        }
60
61        for _ in 0..(table_len_needed / self.columns) {
62            write!(out, "{:0>1$X}: ", self.offset + row_pos, offset_str_len)?;
63
64            for _ in 0..self.columns {
65                if !self.zeros && data[col_pos] == 0 {
66                    write!(out, "{} ", ".".repeat(col_pad_len))?;
67                } else {
68                    write!(out, "{:0>1$X} ", data[col_pos], col_pad_len)?;
69                }
70
71                col_pos += 1;
72
73                if col_pos >= data_len {
74                    break
75                }
76            }
77
78            if self.ascii {
79                if col_pos >= data_len {
80                    write!(out, "{}", " ".repeat(last_row_pad_len))?;
81                }
82
83                write!(out, "| ")?;
84
85                for _ in 0..self.columns {
86                    if data[ascii_pos].is_ascii_alphanumeric() {
87                        write!(out, "{}", data[ascii_pos] as char)?;
88                    } else {
89                        write!(out, ".")?;
90                    }
91
92                    ascii_pos += 1;
93
94                    if ascii_pos >= data_len {
95                        break
96                    }
97                }
98            }
99
100            row_pos += self.columns;
101
102            if row_pos >= data_len {
103                break
104            }
105
106            write!(out, "\n")?;
107        }
108
109        write!(out, "\n")?;
110
111        Ok(())
112    }
113
114    // This was fun. It counts the number of hex digits with log16(x) + 1 for whole numbers. It uses
115    // the algorithm log2(x) = size_of(x) - 1 - clz(x) then a base conversion log2(x) / log2(16).
116    fn count_hex_digits(x: usize) -> usize {
117        (8 * std::mem::size_of::<usize>() - 1 - x.leading_zeros() as usize) / (7 - 16u8.leading_zeros() as usize) + 1
118    }
119}