1#[cfg(feature = "alloc")]
2use alloc::string::String;
3use core::{default::Default, fmt};
4
5#[cfg(feature = "alloc")]
8pub fn simple_hex<T: AsRef<[u8]>>(source: &T) -> String {
9 let mut writer = String::new();
10 hex_write(&mut writer, source, HexConfig::simple()).unwrap_or(());
11 writer
12}
13
14pub fn simple_hex_write<T, W>(writer: &mut W, source: &T) -> fmt::Result
16where
17 T: AsRef<[u8]>,
18 W: fmt::Write,
19{
20 hex_write(writer, source, HexConfig::simple())
21}
22
23#[cfg(feature = "alloc")]
26pub fn pretty_hex<T: AsRef<[u8]>>(source: &T) -> String {
27 let mut writer = String::new();
28 hex_write(&mut writer, source, HexConfig::default()).unwrap_or(());
29 writer
30}
31
32pub fn pretty_hex_write<T, W>(writer: &mut W, source: &T) -> fmt::Result
35where
36 T: AsRef<[u8]>,
37 W: fmt::Write,
38{
39 hex_write(writer, source, HexConfig::default())
40}
41
42#[cfg(feature = "alloc")]
44pub fn config_hex<T: AsRef<[u8]>>(source: &T, cfg: HexConfig) -> String {
45 let mut writer = String::new();
46 hex_write(&mut writer, source, cfg).unwrap_or(());
47 writer
48}
49
50#[derive(Clone, Copy, Debug)]
52pub struct HexConfig {
53 pub title: bool,
55 pub ascii: bool,
57 pub width: usize,
59 pub group: usize,
61 pub chunk: usize,
63 pub max_bytes: usize,
65 pub display_offset: usize,
67}
68
69impl Default for HexConfig {
72 fn default() -> HexConfig {
73 HexConfig {
74 title: true,
75 ascii: true,
76 width: 16,
77 group: 4,
78 chunk: 1,
79 max_bytes: usize::MAX,
80 display_offset: 0,
81 }
82 }
83}
84
85impl HexConfig {
86 pub fn simple() -> Self {
88 HexConfig::default().to_simple()
89 }
90
91 fn delimiter(&self, i: usize) -> &'static str {
92 if i > 0 && self.chunk > 0 && i % self.chunk == 0 {
93 if self.group > 0 && i % (self.group * self.chunk) == 0 {
94 " "
95 } else {
96 " "
97 }
98 } else {
99 ""
100 }
101 }
102
103 fn to_simple(self) -> Self {
104 HexConfig {
105 title: false,
106 ascii: false,
107 width: 0,
108 ..self
109 }
110 }
111}
112
113const NON_ASCII: char = '.';
114
115type AddressWriter = dyn Fn(&mut dyn fmt::Write, usize) -> fmt::Result;
116
117fn get_address_writer(max_addr: usize) -> &'static AddressWriter {
118 match max_addr {
119 0x0000..=0xffff => &|w: &mut dyn fmt::Write, a| write!(w, "{:04x}: ", a),
120 0x010000..=0xffffff => &|w: &mut dyn fmt::Write, a| write!(w, "{:06x}: ", a),
121 0x01000000..=0xffffffff => &|w: &mut dyn fmt::Write, a| write!(w, "{:08x}: ", a),
122 _ => &|w: &mut dyn fmt::Write, a| write!(w, "{:016x}: ", a),
123 }
124}
125
126pub fn hex_write<T, W>(writer: &mut W, source: &T, cfg: HexConfig) -> fmt::Result
128where
129 T: AsRef<[u8]> + ?Sized,
130 W: fmt::Write,
131{
132 let mut source = source.as_ref();
133 if cfg.title {
134 writeln!(writer, "Length: {0} (0x{0:x}) bytes", source.len())?;
135 }
136
137 if source.is_empty() {
138 return Ok(());
139 }
140
141 let omitted = source
142 .len()
143 .checked_sub(cfg.max_bytes)
144 .and_then(|o| (o > 0).then_some(o));
145 if omitted.is_some() {
146 source = &source[..cfg.max_bytes];
147 }
148 let lines = source.chunks(if cfg.width > 0 {
149 cfg.width
150 } else {
151 source.len()
152 });
153
154 let lines_len = lines.len();
155
156 let max_address = if source.len() <= cfg.width {
157 source.len() + cfg.display_offset
158 } else {
159 source.len() - cfg.width + cfg.display_offset
160 };
161 let write_address = get_address_writer(max_address);
162
163 for (i, row) in lines.enumerate() {
164 if cfg.width > 0 {
165 write_address(writer, i * cfg.width + cfg.display_offset)?;
166 }
167 for (i, x) in row.as_ref().iter().enumerate() {
168 write!(writer, "{}{:02x}", cfg.delimiter(i), x)?;
169 }
170 if cfg.ascii {
171 for j in row.len()..cfg.width {
172 write!(writer, "{} ", cfg.delimiter(j))?;
173 }
174 write!(writer, " ")?;
175 for x in row {
176 if x.is_ascii() && !x.is_ascii_control() {
177 writer.write_char((*x).into())?;
178 } else {
179 writer.write_char(NON_ASCII)?;
180 }
181 }
182 }
183 if i + 1 < lines_len {
184 writeln!(writer)?;
185 }
186 }
187 if let Some(o) = omitted {
188 write!(writer, "\n... {0} (0x{0:x}) bytes not shown ...", o)?;
189 }
190 Ok(())
191}
192
193pub struct Hex<'a, T: 'a + ?Sized>(&'a T, HexConfig);
195
196impl<'a, T: 'a + AsRef<[u8]> + ?Sized> fmt::Display for Hex<'a, T> {
197 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199 hex_write(f, self.0, self.1.to_simple())
200 }
201}
202
203impl<'a, T: 'a + AsRef<[u8]> + ?Sized> fmt::Debug for Hex<'a, T> {
204 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206 hex_write(f, self.0, self.1)
207 }
208}
209
210pub trait PrettyHex {
212 fn hex_dump(&self) -> Hex<Self>;
215
216 fn hex_conf(&self, cfg: HexConfig) -> Hex<Self>;
219}
220
221impl<T> PrettyHex for T
222where
223 T: AsRef<[u8]> + ?Sized,
224{
225 fn hex_dump(&self) -> Hex<Self> {
226 Hex(self, HexConfig::default())
227 }
228 fn hex_conf(&self, cfg: HexConfig) -> Hex<Self> {
229 Hex(self, cfg)
230 }
231}