bitcoin_private/hex/
buf_encoder.rs

1//! Implements a buffered encoder.
2//!
3//! The main type of this module is [`BufEncoder`] which provides buffered hex encoding. Such is
4//! faster than the usual `write!(f, "{02x}", b)?` in a for loop because it reduces dynamic
5//! dispatch and decreases the number of allocations if a `String` is being created.
6
7use core::borrow::Borrow;
8
9pub use out_bytes::OutBytes;
10
11use super::Case;
12
13/// Trait for types that can be soundly converted to `OutBytes`.
14///
15/// To protect the API from future breakage this sealed trait guards which types can be used with
16/// the `Encoder`. Currently it is implemented for byte arrays of various interesting lengths.
17///
18/// ## Safety
19///
20/// This is not `unsafe` yet but the `as_out_bytes` should always return the same reference if the
21/// same reference is supplied. IOW the returned memory address and length should be the same if
22/// the input memory address and length are the same.
23///
24/// If the trait ever becomes `unsafe` this will be required for soundness.
25pub trait AsOutBytes: out_bytes::Sealed {
26    /// Performs the conversion.
27    fn as_out_bytes(&self) -> &OutBytes;
28
29    /// Performs the conversion.
30    fn as_mut_out_bytes(&mut self) -> &mut OutBytes;
31}
32
33/// A buffer with compile-time-known length.
34///
35/// This is essentially `Default + AsOutBytes` but supports lengths 1.41 doesn't.
36pub trait FixedLenBuf: Sized + AsOutBytes {
37    /// Creates an uninitialized buffer.
38    ///
39    /// The current implementtions initialize the buffer with zeroes but it should be treated a
40    /// uninitialized anyway.
41    fn uninit() -> Self;
42}
43
44/// Implements `OutBytes`
45///
46/// This prevents the rest of the crate from accessing the field of `OutBytes`.
47mod out_bytes {
48    use super::AsOutBytes;
49
50    /// A byte buffer that can only be written-into.
51    ///
52    /// You shouldn't concern yourself with this, just call `BufEncoder::new` with your array.
53    ///
54    /// This prepares the API for potential future support of `[MaybeUninit<u8>]`. We don't want to use
55    /// `unsafe` until it's proven to be needed but if it does we have an easy, compatible upgrade
56    /// option.
57    ///
58    /// Warning: `repr(transparent)` is an internal implementation detail and **must not** be
59    /// relied on!
60    #[repr(transparent)]
61    pub struct OutBytes([u8]);
62
63    impl OutBytes {
64        /// Returns the first `len` bytes as initialized.
65        ///
66        /// Not `unsafe` because we don't use `unsafe` (yet).
67        ///
68        /// ## Panics
69        ///
70        /// The method panics if `len` is out of bounds.
71        #[cfg_attr(rust_v_1_46, track_caller)]
72        pub(crate) fn assume_init(&self, len: usize) -> &[u8] { &self.0[..len] }
73
74        /// Writes given bytes into the buffer.
75        ///
76        /// ## Panics
77        ///
78        /// The method panics if pos is out of bounds or `bytes` don't fit into the buffer.
79        #[cfg_attr(rust_v_1_46, track_caller)]
80        pub(crate) fn write(&mut self, pos: usize, bytes: &[u8]) {
81            self.0[pos..(pos + bytes.len())].copy_from_slice(bytes);
82        }
83
84        /// Returns the length of the buffer.
85        pub(crate) fn len(&self) -> usize { self.0.len() }
86
87        fn from_bytes(slice: &[u8]) -> &Self {
88            // SAFETY: copied from std
89            // conversion of reference to pointer of the same referred type is always sound,
90            // including in unsized types.
91            // Thanks to repr(transparent) the types have the same layout making the other
92            // conversion sound.
93            // The pointer was just created from a reference that's still alive so dereferencing is
94            // sound.
95            unsafe { &*(slice as *const [u8] as *const Self) }
96        }
97
98        fn from_mut_bytes(slice: &mut [u8]) -> &mut Self {
99            // SAFETY: copied from std
100            // conversion of reference to pointer of the same referred type is always sound,
101            // including in unsized types.
102            // Thanks to repr(transparent) the types have the same layout making the other
103            // conversion sound.
104            // The pointer was just created from a reference that's still alive so dereferencing is
105            // sound.
106            unsafe { &mut *(slice as *mut [u8] as *mut Self) }
107        }
108    }
109
110    macro_rules! impl_from_array {
111        ($($len:expr),* $(,)?) => {
112            $(
113                impl super::FixedLenBuf for [u8; $len] {
114                    fn uninit() -> Self {
115                        [0u8; $len]
116                    }
117                }
118
119                impl AsOutBytes for [u8; $len] {
120                    fn as_out_bytes(&self) -> &OutBytes {
121                        OutBytes::from_bytes(self)
122                    }
123
124                    fn as_mut_out_bytes(&mut self) -> &mut OutBytes {
125                        OutBytes::from_mut_bytes(self)
126                    }
127                }
128
129                impl Sealed for [u8; $len] {}
130
131                impl<'a> super::super::display::DisplayHex for &'a [u8; $len / 2] {
132                    type Display = super::super::display::DisplayArray<core::slice::Iter<'a, u8>, [u8; $len]>;
133                    fn as_hex(self) -> Self::Display {
134                        super::super::display::DisplayArray::new(self.iter())
135                    }
136
137                    fn hex_reserve_suggestion(self) -> usize {
138                        $len
139                    }
140                }
141            )*
142        }
143    }
144
145    impl<T: AsOutBytes + ?Sized> AsOutBytes for &'_ mut T {
146        fn as_out_bytes(&self) -> &OutBytes { (**self).as_out_bytes() }
147
148        fn as_mut_out_bytes(&mut self) -> &mut OutBytes { (**self).as_mut_out_bytes() }
149    }
150
151    impl<T: AsOutBytes + ?Sized> Sealed for &'_ mut T {}
152
153    impl AsOutBytes for OutBytes {
154        fn as_out_bytes(&self) -> &OutBytes { self }
155
156        fn as_mut_out_bytes(&mut self) -> &mut OutBytes { self }
157    }
158
159    impl Sealed for OutBytes {}
160
161    // As a sanity check we only provide conversions for even, non-empty arrays.
162    // Weird lengths 66 and 130 are provided for serialized public keys.
163    impl_from_array!(
164        2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 40, 64, 66, 128, 130, 256, 512,
165        1024, 2048, 4096, 8192
166    );
167
168    /// Prevents outside crates from implementing the trait
169    pub trait Sealed {}
170}
171
172/// Hex-encodes bytes into the provided buffer.
173///
174/// This is an important building block for fast hex-encoding. Because string writing tools
175/// provided by `core::fmt` involve dynamic dispatch and don't allow reserving capacity in strings
176/// buffering the hex and then formatting it is significantly faster.
177pub struct BufEncoder<T: AsOutBytes> {
178    buf: T,
179    pos: usize,
180}
181
182impl<T: AsOutBytes> BufEncoder<T> {
183    /// Creates an empty `BufEncoder`.
184    ///
185    /// This is usually used with uninitialized (zeroed) byte array allocated on stack.
186    /// This can only be constructed with an even-length, non-empty array.
187    #[inline]
188    pub fn new(buf: T) -> Self { BufEncoder { buf, pos: 0 } }
189
190    /// Encodes `byte` as hex in given `case` and appends it to the buffer.
191    ///
192    /// ## Panics
193    ///
194    /// The method panics if the buffer is full.
195    #[inline]
196    #[cfg_attr(rust_v_1_46, track_caller)]
197    pub fn put_byte(&mut self, byte: u8, case: Case) {
198        self.buf.as_mut_out_bytes().write(self.pos, &super::byte_to_hex(byte, case.table()));
199        self.pos += 2;
200    }
201
202    /// Encodes `bytes` as hex in given `case` and appends them to the buffer.
203    ///
204    /// ## Panics
205    ///
206    /// The method panics if the bytes wouldn't fit the buffer.
207    #[inline]
208    #[cfg_attr(rust_v_1_46, track_caller)]
209    pub fn put_bytes<I>(&mut self, bytes: I, case: Case)
210    where
211        I: IntoIterator,
212        I::Item: Borrow<u8>,
213    {
214        self.put_bytes_inner(bytes.into_iter(), case)
215    }
216
217    #[inline]
218    #[cfg_attr(rust_v_1_46, track_caller)]
219    fn put_bytes_inner<I>(&mut self, bytes: I, case: Case)
220    where
221        I: Iterator,
222        I::Item: Borrow<u8>,
223    {
224        // May give the compiler better optimization opportunity
225        if let Some(max) = bytes.size_hint().1 {
226            assert!(max <= self.space_remaining());
227        }
228        for byte in bytes {
229            self.put_byte(*byte.borrow(), case);
230        }
231    }
232
233    /// Encodes as many `bytes` as fit into the buffer as hex and return the remainder.
234    ///
235    /// This method works just like `put_bytes` but instead of panicking it returns the unwritten
236    /// bytes. The method returns an empty slice if all bytes were written
237    #[must_use = "this may write only part of the input buffer"]
238    #[inline]
239    #[cfg_attr(rust_v_1_46, track_caller)]
240    pub fn put_bytes_min<'a>(&mut self, bytes: &'a [u8], case: Case) -> &'a [u8] {
241        let to_write = self.space_remaining().min(bytes.len());
242        self.put_bytes(&bytes[..to_write], case);
243        &bytes[to_write..]
244    }
245
246    /// Returns true if no more bytes can be written into the buffer.
247    #[inline]
248    pub fn is_full(&self) -> bool { self.pos == self.buf.as_out_bytes().len() }
249
250    /// Returns the written bytes as a hex `str`.
251    #[inline]
252    pub fn as_str(&self) -> &str {
253        core::str::from_utf8(self.buf.as_out_bytes().assume_init(self.pos))
254            .expect("we only write ASCII")
255    }
256
257    /// Resets the buffer to become empty.
258    #[inline]
259    pub fn clear(&mut self) { self.pos = 0; }
260
261    /// How many bytes can be written to this buffer.
262    ///
263    /// Note that this returns the number of bytes before encoding, not number of hex digits.
264    #[inline]
265    pub fn space_remaining(&self) -> usize { (self.buf.as_out_bytes().len() - self.pos) / 2 }
266}
267
268#[cfg(test)]
269mod tests {
270    use super::*;
271
272    #[test]
273    fn empty() {
274        let mut buf = [0u8; 2];
275        let encoder = BufEncoder::new(&mut buf);
276        assert_eq!(encoder.as_str(), "");
277        assert!(!encoder.is_full());
278    }
279
280    #[test]
281    fn single_byte_exact_buf() {
282        let mut buf = [0u8; 2];
283        let mut encoder = BufEncoder::new(&mut buf);
284        assert_eq!(encoder.space_remaining(), 1);
285        encoder.put_byte(42, Case::Lower);
286        assert_eq!(encoder.as_str(), "2a");
287        assert_eq!(encoder.space_remaining(), 0);
288        assert!(encoder.is_full());
289        encoder.clear();
290        assert_eq!(encoder.space_remaining(), 1);
291        assert!(!encoder.is_full());
292        encoder.put_byte(42, Case::Upper);
293        assert_eq!(encoder.as_str(), "2A");
294        assert_eq!(encoder.space_remaining(), 0);
295        assert!(encoder.is_full());
296    }
297
298    #[test]
299    fn single_byte_oversized_buf() {
300        let mut buf = [0u8; 4];
301        let mut encoder = BufEncoder::new(&mut buf);
302        assert_eq!(encoder.space_remaining(), 2);
303        encoder.put_byte(42, Case::Lower);
304        assert_eq!(encoder.space_remaining(), 1);
305        assert_eq!(encoder.as_str(), "2a");
306        assert!(!encoder.is_full());
307        encoder.clear();
308        assert_eq!(encoder.space_remaining(), 2);
309        encoder.put_byte(42, Case::Upper);
310        assert_eq!(encoder.as_str(), "2A");
311        assert_eq!(encoder.space_remaining(), 1);
312        assert!(!encoder.is_full());
313    }
314
315    #[test]
316    fn two_bytes() {
317        let mut buf = [0u8; 4];
318        let mut encoder = BufEncoder::new(&mut buf);
319        encoder.put_byte(42, Case::Lower);
320        assert_eq!(encoder.space_remaining(), 1);
321        encoder.put_byte(255, Case::Lower);
322        assert_eq!(encoder.space_remaining(), 0);
323        assert_eq!(encoder.as_str(), "2aff");
324        assert!(encoder.is_full());
325        encoder.clear();
326        assert!(!encoder.is_full());
327        encoder.put_byte(42, Case::Upper);
328        encoder.put_byte(255, Case::Upper);
329        assert_eq!(encoder.as_str(), "2AFF");
330        assert!(encoder.is_full());
331    }
332
333    #[test]
334    fn put_bytes_min() {
335        let mut buf = [0u8; 2];
336        let mut encoder = BufEncoder::new(&mut buf);
337        let remainder = encoder.put_bytes_min(b"", Case::Lower);
338        assert_eq!(remainder, b"");
339        assert_eq!(encoder.as_str(), "");
340        let remainder = encoder.put_bytes_min(b"*", Case::Lower);
341        assert_eq!(remainder, b"");
342        assert_eq!(encoder.as_str(), "2a");
343        encoder.clear();
344        let remainder = encoder.put_bytes_min(&[42, 255], Case::Lower);
345        assert_eq!(remainder, &[255]);
346        assert_eq!(encoder.as_str(), "2a");
347    }
348
349    #[test]
350    fn same_as_fmt() {
351        use core::fmt::{self, Write};
352
353        struct Writer {
354            buf: [u8; 2],
355            pos: usize,
356        }
357
358        impl Writer {
359            fn as_str(&self) -> &str { core::str::from_utf8(&self.buf[..self.pos]).unwrap() }
360        }
361
362        impl Write for Writer {
363            fn write_str(&mut self, s: &str) -> fmt::Result {
364                assert!(self.pos <= 2);
365                if s.len() > 2 - self.pos {
366                    Err(fmt::Error)
367                } else {
368                    self.buf[self.pos..(self.pos + s.len())].copy_from_slice(s.as_bytes());
369                    self.pos += s.len();
370                    Ok(())
371                }
372            }
373        }
374
375        let mut writer = Writer { buf: [0u8; 2], pos: 0 };
376        let mut buf = [0u8; 2];
377        let mut encoder = BufEncoder::new(&mut buf);
378
379        for i in 0..=255 {
380            write!(writer, "{:02x}", i).unwrap();
381            encoder.put_byte(i, Case::Lower);
382            assert_eq!(encoder.as_str(), writer.as_str());
383            writer.pos = 0;
384            encoder.clear();
385        }
386        for i in 0..=255 {
387            write!(writer, "{:02X}", i).unwrap();
388            encoder.put_byte(i, Case::Upper);
389            assert_eq!(encoder.as_str(), writer.as_str());
390            writer.pos = 0;
391            encoder.clear();
392        }
393    }
394}