bitcoin_internals/hex/
display.rs1use core::borrow::Borrow;
9use core::fmt;
10
11use super::buf_encoder::{BufEncoder, OutBytes};
12use super::Case;
13use crate::hex::buf_encoder::FixedLenBuf;
14#[cfg(feature = "alloc")]
15use crate::prelude::*;
16
17pub trait DisplayHex: Copy + sealed::IsRef {
26 type Display: fmt::LowerHex + fmt::UpperHex;
30
31 fn as_hex(self) -> Self::Display;
33
34 #[cfg(feature = "alloc")]
40 fn to_lower_hex_string(self) -> String { self.to_hex_string(Case::Lower) }
41
42 #[cfg(feature = "alloc")]
48 fn to_upper_hex_string(self) -> String { self.to_hex_string(Case::Upper) }
49
50 #[cfg(feature = "alloc")]
54 fn to_hex_string(self, case: Case) -> String {
55 let mut string = String::new();
56 self.append_hex_to_string(case, &mut string);
57 string
58 }
59
60 #[cfg(feature = "alloc")]
65 fn append_hex_to_string(self, case: Case, string: &mut String) {
66 use fmt::Write;
67
68 string.reserve(self.hex_reserve_suggestion());
69 match case {
70 Case::Lower => write!(string, "{:x}", self.as_hex()),
71 Case::Upper => write!(string, "{:X}", self.as_hex()),
72 }
73 .unwrap_or_else(|_| {
74 let name = core::any::type_name::<Self::Display>();
75 panic!("The implementation of Display for {} returned an error when it shouldn't", name)
78 })
79 }
80
81 fn hex_reserve_suggestion(self) -> usize { 0 }
88}
89
90mod sealed {
91 pub trait IsRef: Copy {}
93
94 impl<T: ?Sized> IsRef for &'_ T {}
95}
96
97impl<'a> DisplayHex for &'a [u8] {
98 type Display = DisplayByteSlice<'a>;
99
100 #[inline]
101 fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } }
102
103 #[inline]
104 fn hex_reserve_suggestion(self) -> usize {
105 self.len().checked_mul(2).expect("the string wouldn't fit into address space")
109 }
110}
111
112#[cfg(feature = "alloc")]
113impl<'a> DisplayHex for &'a alloc::vec::Vec<u8> {
114 type Display = DisplayByteSlice<'a>;
115
116 #[inline]
117 fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } }
118
119 #[inline]
120 fn hex_reserve_suggestion(self) -> usize {
121 self.len().checked_mul(2).expect("the string wouldn't fit into address space")
125 }
126}
127
128pub struct DisplayByteSlice<'a> {
132 pub(crate) bytes: &'a [u8],
134}
135
136impl<'a> DisplayByteSlice<'a> {
137 fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
138 let mut buf = [0u8; 1024];
139 let mut encoder = super::BufEncoder::new(&mut buf);
140
141 let mut chunks = self.bytes.chunks_exact(512);
142 for chunk in &mut chunks {
143 encoder.put_bytes(chunk, case);
144 f.write_str(encoder.as_str())?;
145 encoder.clear();
146 }
147 encoder.put_bytes(chunks.remainder(), case);
148 f.write_str(encoder.as_str())
149 }
150}
151
152impl<'a> fmt::LowerHex for DisplayByteSlice<'a> {
153 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
154}
155
156impl<'a> fmt::UpperHex for DisplayByteSlice<'a> {
157 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
158}
159
160pub struct DisplayArray<A: Clone + IntoIterator, B: FixedLenBuf>
164where
165 A::Item: Borrow<u8>,
166{
167 array: A,
168 _buffer_marker: core::marker::PhantomData<B>,
169}
170
171impl<A: Clone + IntoIterator, B: FixedLenBuf> DisplayArray<A, B>
172where
173 A::Item: Borrow<u8>,
174{
175 pub fn new(array: A) -> Self { DisplayArray { array, _buffer_marker: Default::default() } }
177
178 fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
179 let mut buf = B::uninit();
180 let mut encoder = super::BufEncoder::new(&mut buf);
181 encoder.put_bytes(self.array.clone(), case);
182 f.pad_integral(true, "0x", encoder.as_str())
183 }
184}
185
186impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::LowerHex for DisplayArray<A, B>
187where
188 A::Item: Borrow<u8>,
189{
190 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
191}
192
193impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::UpperHex for DisplayArray<A, B>
194where
195 A::Item: Borrow<u8>,
196{
197 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
198}
199
200#[macro_export]
219macro_rules! fmt_hex_exact {
220 ($formatter:expr, $len:expr, $bytes:expr, $case:expr) => {{
221 #[allow(deprecated)]
223 const _: () = [()][($len > usize::MAX / 2) as usize];
224 assert_eq!($bytes.len(), $len);
225 let mut buf = [0u8; $len * 2];
226 let buf = $crate::hex::buf_encoder::AsOutBytes::as_mut_out_bytes(&mut buf);
227 $crate::hex::display::fmt_hex_exact_fn($formatter, buf, $bytes, $case)
228 }};
229}
230pub use fmt_hex_exact;
231
232#[doc(hidden)]
234#[inline]
235pub fn fmt_hex_exact_fn<I>(
236 f: &mut fmt::Formatter,
237 buf: &mut OutBytes,
238 bytes: I,
239 case: Case,
240) -> fmt::Result
241where
242 I: IntoIterator,
243 I::Item: Borrow<u8>,
244{
245 let mut encoder = BufEncoder::new(buf);
246 encoder.put_bytes(bytes, case);
247 f.pad_integral(true, "0x", encoder.as_str())
248}
249
250#[cfg(test)]
251mod tests {
252 #[cfg(feature = "alloc")]
253 use super::*;
254
255 #[cfg(feature = "alloc")]
256 mod alloc {
257 use super::*;
258
259 fn check_encoding(bytes: &[u8]) {
260 use core::fmt::Write;
261
262 let s1 = bytes.to_lower_hex_string();
263 let mut s2 = String::with_capacity(bytes.len() * 2);
264 for b in bytes {
265 write!(s2, "{:02x}", b).unwrap();
266 }
267 assert_eq!(s1, s2);
268 }
269
270 #[test]
271 fn empty() { check_encoding(b""); }
272
273 #[test]
274 fn single() { check_encoding(b"*"); }
275
276 #[test]
277 fn two() { check_encoding(b"*x"); }
278
279 #[test]
280 fn just_below_boundary() { check_encoding(&[42; 512]); }
281
282 #[test]
283 fn just_above_boundary() { check_encoding(&[42; 513]); }
284
285 #[test]
286 fn just_above_double_boundary() { check_encoding(&[42; 1025]); }
287
288 #[test]
289 fn fmt_exact_macro() {
290 use crate::alloc::string::ToString;
291
292 struct Dummy([u8; 32]);
293
294 impl fmt::Display for Dummy {
295 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
296 fmt_hex_exact!(f, 32, &self.0, Case::Lower)
297 }
298 }
299
300 assert_eq!(Dummy([42; 32]).to_string(), "2a".repeat(32));
301 }
302 }
303}