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