1#![cfg_attr(not(test), no_std)]
41
42use core::fmt::{Alignment, Debug, Display, Formatter, LowerHex, Result, UpperHex, Write};
43
44const ELLIPSIS: &str = "..";
45
46pub struct HexFmt<T>(pub T);
49
50impl<T: AsRef<[u8]>> Debug for HexFmt<T> {
51 #[inline]
52 fn fmt(&self, f: &mut Formatter) -> Result {
53 LowerHex::fmt(self, f)
54 }
55}
56
57impl<T: AsRef<[u8]>> Display for HexFmt<T> {
58 #[inline]
59 fn fmt(&self, f: &mut Formatter) -> Result {
60 LowerHex::fmt(self, f)
61 }
62}
63
64impl<T: AsRef<[u8]>> LowerHex for HexFmt<T> {
65 #[inline]
66 fn fmt(&self, f: &mut Formatter) -> Result {
67 Lowercase::fmt(self.0.as_ref(), f)
68 }
69}
70
71impl<T: AsRef<[u8]>> UpperHex for HexFmt<T> {
72 #[inline]
73 fn fmt(&self, f: &mut Formatter) -> Result {
74 Uppercase::fmt(self.0.as_ref(), f)
75 }
76}
77
78pub struct HexList<T>(pub T);
81
82impl<T> Debug for HexList<T>
83where
84 T: Clone + IntoIterator,
85 T::Item: AsRef<[u8]>,
86{
87 #[inline]
88 fn fmt(&self, f: &mut Formatter) -> Result {
89 LowerHex::fmt(self, f)
90 }
91}
92
93impl<T> Display for HexList<T>
94where
95 T: Clone + IntoIterator,
96 T::Item: AsRef<[u8]>,
97{
98 #[inline]
99 fn fmt(&self, f: &mut Formatter) -> Result {
100 LowerHex::fmt(self, f)
101 }
102}
103
104impl<T> LowerHex for HexList<T>
105where
106 T: Clone + IntoIterator,
107 T::Item: AsRef<[u8]>,
108{
109 #[inline]
110 fn fmt(&self, f: &mut Formatter) -> Result {
111 let entries = self.0.clone().into_iter().map(HexFmt);
112 f.debug_list().entries(entries).finish()
113 }
114}
115
116impl<T> UpperHex for HexList<T>
117where
118 T: Clone + IntoIterator,
119 T::Item: AsRef<[u8]>,
120{
121 #[inline]
122 fn fmt(&self, f: &mut Formatter) -> Result {
123 let mut iter = self.0.clone().into_iter();
124 write!(f, "[")?;
125 if let Some(item) = iter.next() {
126 UpperHex::fmt(&HexFmt(item), f)?;
127 }
128 for item in iter {
129 write!(f, ", ")?;
130 UpperHex::fmt(&HexFmt(item), f)?;
131 }
132 write!(f, "]")
133 }
134}
135
136trait Case {
137 fn fmt_byte(f: &mut Formatter, byte: u8) -> Result;
138 fn fmt_digit(f: &mut Formatter, digit: u8) -> Result;
139
140 #[inline]
141 fn fmt(bytes: &[u8], f: &mut Formatter) -> Result {
142 let min_width = f.width().unwrap_or(0);
143 let max_width = f
144 .precision()
145 .or_else(|| f.width())
146 .unwrap_or_else(usize::max_value);
147 let align = f.align().unwrap_or(Alignment::Center);
148
149 if 2 * bytes.len() <= max_width {
151 let fill = f.fill();
152 let missing = min_width.saturating_sub(2 * bytes.len());
153 let (left, right) = match align {
154 Alignment::Left => (0, missing),
155 Alignment::Right => (missing, 0),
156 Alignment::Center => (missing / 2, missing - missing / 2),
157 };
158 for _ in 0..left {
159 f.write_char(fill)?;
160 }
161 for byte in bytes {
162 Self::fmt_byte(f, *byte)?;
163 }
164 for _ in 0..right {
165 f.write_char(fill)?;
166 }
167 return Ok(());
168 }
169
170 if max_width <= ELLIPSIS.len() {
172 return write!(f, "{:.*}", max_width, ELLIPSIS);
173 }
174
175 let digits = max_width.saturating_sub(ELLIPSIS.len());
177 let (left, right) = match align {
178 Alignment::Left => (digits, 0),
179 Alignment::Right => (0, digits),
180 Alignment::Center => (digits - digits / 2, digits / 2),
181 };
182
183 for byte in &bytes[..(left / 2)] {
185 Self::fmt_byte(f, *byte)?;
186 }
187 if left & 1 == 1 {
189 Self::fmt_digit(f, bytes[left / 2] >> 4)?;
190 }
191
192 f.write_str(ELLIPSIS)?;
194
195 if right & 1 == 1 {
197 Self::fmt_digit(f, bytes[(bytes.len() - right / 2 - 1)] & 0x0f)?;
198 }
199 for byte in &bytes[(bytes.len() - right / 2)..] {
201 Self::fmt_byte(f, *byte)?;
202 }
203 Ok(())
204 }
205}
206
207#[derive(Clone, Copy)]
208struct Uppercase;
209
210impl Case for Uppercase {
211 #[inline]
212 fn fmt_byte(f: &mut Formatter, byte: u8) -> Result {
213 write!(f, "{:02X}", byte)
214 }
215
216 #[inline]
217 fn fmt_digit(f: &mut Formatter, digit: u8) -> Result {
218 write!(f, "{:1X}", digit)
219 }
220}
221
222#[derive(Clone, Copy)]
223struct Lowercase;
224
225impl Case for Lowercase {
226 #[inline]
227 fn fmt_byte(f: &mut Formatter, byte: u8) -> Result {
228 write!(f, "{:02x}", byte)
229 }
230
231 #[inline]
232 fn fmt_digit(f: &mut Formatter, digit: u8) -> Result {
233 write!(f, "{:1x}", digit)
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::HexFmt;
240
241 #[test]
242 fn test_fmt() {
243 assert_eq!("", &format!("{:.0}", HexFmt(&[0x01])));
244 assert_eq!(".", &format!("{:.1}", HexFmt(&[0x01])));
245 assert_eq!("01", &format!("{:.2}", HexFmt(&[0x01])));
246 assert_eq!("..", &format!("{:.2}", HexFmt(&[0x01, 0x23])));
247 }
248}