1use anyhow::Result;
24use clap::ValueEnum;
25use log::{trace, warn};
26use std::cmp;
27use std::io::{self, Write};
28
29const WIDTH: usize = 16;
30
31#[derive(Clone, Copy, Debug, ValueEnum)]
32pub enum Format {
33 Raw,
34 Hex,
35}
36
37impl Format {
38 pub fn create_writer(&self) -> Writer {
39 match self {
40 Self::Raw => Writer(None),
41 Self::Hex => Writer(Some(Hex::new())),
42 }
43 }
44}
45
46pub struct Writer(Option<Hex>);
47
48impl Writer {
49 pub fn print(&mut self, buf: &[u8]) -> Result<()> {
50 match self.0.as_mut() {
51 Some(hex) => {
52 hex.fill(buf);
53 hex.print();
54 }
55 None => io::stdout().write_all(buf)?,
56 }
57
58 Ok(())
59 }
60
61 pub fn flush(&mut self) -> Result<()> {
62 match self.0.as_mut() {
63 Some(hex) => hex.flush(),
64 None => io::stdout().flush()?,
65 }
66
67 Ok(())
68 }
69}
70
71#[derive(Debug)]
72struct Hex {
73 buf: Vec<u8>,
74 offset: usize,
75}
76
77impl Hex {
78 fn new() -> Hex {
79 Hex {
80 buf: vec![],
81 offset: 0,
82 }
83 }
84
85 fn fill<T: AsRef<[u8]>>(&mut self, buf: T) {
86 self.buf.extend_from_slice(buf.as_ref());
87 }
88
89 fn print(&mut self) {
90 while self.buf.len() >= WIDTH {
91 self.print_line(false);
92 }
93 }
94
95 fn flush(&mut self) {
96 self.print();
97 self.print_line(true);
98 }
99
100 fn print_line(&mut self, force: bool) {
101 if self.buf.is_empty() {
102 return;
103 }
104
105 let width = if force {
106 cmp::min(self.buf.len(), WIDTH)
107 } else {
108 WIDTH
109 };
110
111 trace!(
112 "print_line: width = {}, avail = {}, force = {}",
113 width,
114 self.buf.len(),
115 force
116 );
117
118 if self.buf.len() < width {
119 warn!(
120 "insufficient data available, need {}, got {} (force: {})",
121 width,
122 self.buf.len(),
123 force
124 );
125 return;
126 }
127
128 let (hex, ascii) = self.buf.drain(..width).enumerate().fold(
129 (String::new(), String::new()),
130 |(mut hex, mut ascii), (idx, n)| {
131 hex += &format!("{:02x} ", n);
132
133 if idx % 4 == 3 {
134 hex.push(' ');
135 }
136
137 if n.is_ascii() && !n.is_ascii_control() {
138 ascii.push(n.into());
139 } else {
140 ascii.push('.');
141 }
142
143 (hex, ascii)
144 },
145 );
146
147 println!("{:>04x}: {:<52} {}", self.offset, hex, ascii);
148
149 self.offset += WIDTH;
150 }
151}