Skip to main content

binocular/preview/binary/
hex_dump.rs

1//! Hex dump rendering.
2
3use crate::preview::doc::PreviewDoc;
4use ratatui::style::{Color, Style};
5use ratatui::text::{Line, Span};
6
7pub fn append_hex_dump(header: &[u8], doc: &mut PreviewDoc) {
8    doc.push_section(super::SECTION_HEX_DUMP);
9    for line in create_hex_dump(header, super::HEX_DUMP_BYTES) {
10        doc.push_line(line);
11    }
12    doc.push_blank_line();
13}
14
15pub fn create_hex_dump(data: &[u8], max_bytes: usize) -> Vec<Line<'static>> {
16    let mut lines = Vec::new();
17    let bytes_to_show = data.len().min(max_bytes);
18
19    for (i, chunk) in data[..bytes_to_show].chunks(16).enumerate() {
20        let offset = format!("   {:08x}  ", i * 16);
21
22        let hex: String = chunk
23            .iter()
24            .enumerate()
25            .map(|(j, b)| {
26                if j == 8 {
27                    format!(" {:02x}", b)
28                } else {
29                    format!("{:02x} ", b)
30                }
31            })
32            .collect();
33
34        let padding = "   ".repeat(16 - chunk.len()) + if chunk.len() <= 8 { " " } else { "" };
35
36        let ascii: String = chunk
37            .iter()
38            .map(|&b| {
39                if b.is_ascii_graphic() || b == b' ' {
40                    b as char
41                } else {
42                    '.'
43                }
44            })
45            .collect();
46
47        lines.push(Line::from(vec![
48            Span::styled(offset, Style::default().fg(Color::DarkGray)),
49            Span::styled(hex, Style::default().fg(Color::Cyan)),
50            Span::styled(padding, Style::default()),
51            Span::styled(" │", Style::default().fg(Color::DarkGray)),
52            Span::styled(ascii, Style::default().fg(Color::Green)),
53            Span::styled("│", Style::default().fg(Color::DarkGray)),
54        ]));
55    }
56
57    lines
58}