hex_conservative/
buf_encoder.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Implements a buffered encoder.
4//!
5//! This is a low-level module, most uses should be satisfied by the `display` module instead.
6//!
7//! The main type in this module is [`BufEncoder`] which provides buffered hex encoding.
8//! `BufEncoder` is faster than the usual `write!(f, "{02x}", b)?` in a for loop because it reduces
9//! dynamic dispatch and decreases the number of allocations if a `String` is being created.
10
11use core::borrow::Borrow;
12
13use arrayvec::ArrayString;
14
15use super::{Case, Table};
16
17/// Hex-encodes bytes into the provided buffer.
18///
19/// This is an important building block for fast hex-encoding. Because string writing tools
20/// provided by `core::fmt` involve dynamic dispatch and don't allow reserving capacity in strings
21/// buffering the hex and then formatting it is significantly faster.
22pub struct BufEncoder<const CAP: usize> {
23    buf: ArrayString<CAP>,
24    table: &'static Table,
25}
26
27impl<const CAP: usize> BufEncoder<CAP> {
28    const _CHECK_EVEN_CAPACITY: () = [(); 1][CAP % 2];
29
30    /// Creates an empty `BufEncoder` that will encode bytes to hex characters in the given case.
31    #[inline]
32    pub fn new(case: Case) -> Self { BufEncoder { buf: ArrayString::new(), table: case.table() } }
33
34    /// Encodes `byte` as hex and appends it to the buffer.
35    ///
36    /// ## Panics
37    ///
38    /// The method panics if the buffer is full.
39    #[inline]
40    #[track_caller]
41    pub fn put_byte(&mut self, byte: u8) {
42        let mut hex_chars = [0u8; 2];
43        let hex_str = self.table.byte_to_str(&mut hex_chars, byte);
44        self.buf.push_str(hex_str);
45    }
46
47    /// Encodes `bytes` as hex and appends them to the buffer.
48    ///
49    /// ## Panics
50    ///
51    /// The method panics if the bytes wouldn't fit the buffer.
52    #[inline]
53    #[track_caller]
54    pub fn put_bytes<I>(&mut self, bytes: I)
55    where
56        I: IntoIterator,
57        I::Item: Borrow<u8>,
58    {
59        self.put_bytes_inner(bytes.into_iter())
60    }
61
62    #[inline]
63    #[track_caller]
64    fn put_bytes_inner<I>(&mut self, bytes: I)
65    where
66        I: Iterator,
67        I::Item: Borrow<u8>,
68    {
69        // May give the compiler better optimization opportunity
70        if let Some(max) = bytes.size_hint().1 {
71            assert!(max <= self.space_remaining());
72        }
73        for byte in bytes {
74            self.put_byte(*byte.borrow());
75        }
76    }
77
78    /// Encodes as many `bytes` as fit into the buffer as hex and return the remainder.
79    ///
80    /// This method works just like `put_bytes` but instead of panicking it returns the unwritten
81    /// bytes. The method returns an empty slice if all bytes were written
82    #[must_use = "this may write only part of the input buffer"]
83    #[inline]
84    #[track_caller]
85    pub fn put_bytes_min<'a>(&mut self, bytes: &'a [u8]) -> &'a [u8] {
86        let to_write = self.space_remaining().min(bytes.len());
87        self.put_bytes(&bytes[..to_write]);
88        &bytes[to_write..]
89    }
90
91    /// Returns true if no more bytes can be written into the buffer.
92    #[inline]
93    pub fn is_full(&self) -> bool { self.space_remaining() == 0 }
94
95    /// Returns the written bytes as a hex `str`.
96    #[inline]
97    pub fn as_str(&self) -> &str { &self.buf }
98
99    /// Resets the buffer to become empty.
100    #[inline]
101    pub fn clear(&mut self) { self.buf.clear(); }
102
103    /// How many bytes can be written to this buffer.
104    ///
105    /// Note that this returns the number of bytes before encoding, not number of hex digits.
106    #[inline]
107    pub fn space_remaining(&self) -> usize { self.buf.remaining_capacity() / 2 }
108
109    pub(crate) fn put_filler(&mut self, filler: char, max_count: usize) -> usize {
110        let mut buf = [0; 4];
111        let filler = filler.encode_utf8(&mut buf);
112        let max_capacity = self.buf.remaining_capacity() / filler.len();
113        let to_write = max_capacity.min(max_count);
114
115        for _ in 0..to_write {
116            self.buf.push_str(filler);
117        }
118
119        to_write
120    }
121}
122
123impl<const CAP: usize> Default for BufEncoder<CAP> {
124    fn default() -> Self { Self::new(Case::Lower) }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn empty() {
133        let encoder = BufEncoder::<2>::new(Case::Lower);
134        assert_eq!(encoder.as_str(), "");
135        assert!(!encoder.is_full());
136
137        let encoder = BufEncoder::<2>::new(Case::Upper);
138        assert_eq!(encoder.as_str(), "");
139        assert!(!encoder.is_full());
140    }
141
142    #[test]
143    fn single_byte_exact_buf() {
144        let mut encoder = BufEncoder::<2>::new(Case::Lower);
145        assert_eq!(encoder.space_remaining(), 1);
146        encoder.put_byte(42);
147        assert_eq!(encoder.as_str(), "2a");
148        assert_eq!(encoder.space_remaining(), 0);
149        assert!(encoder.is_full());
150        encoder.clear();
151        assert_eq!(encoder.space_remaining(), 1);
152        assert!(!encoder.is_full());
153
154        let mut encoder = BufEncoder::<2>::new(Case::Upper);
155        assert_eq!(encoder.space_remaining(), 1);
156        encoder.put_byte(42);
157        assert_eq!(encoder.as_str(), "2A");
158        assert_eq!(encoder.space_remaining(), 0);
159        assert!(encoder.is_full());
160        encoder.clear();
161        assert_eq!(encoder.space_remaining(), 1);
162        assert!(!encoder.is_full());
163    }
164
165    #[test]
166    fn single_byte_oversized_buf() {
167        let mut encoder = BufEncoder::<4>::new(Case::Lower);
168        assert_eq!(encoder.space_remaining(), 2);
169        encoder.put_byte(42);
170        assert_eq!(encoder.space_remaining(), 1);
171        assert_eq!(encoder.as_str(), "2a");
172        assert!(!encoder.is_full());
173        encoder.clear();
174        assert_eq!(encoder.space_remaining(), 2);
175        assert!(!encoder.is_full());
176
177        let mut encoder = BufEncoder::<4>::new(Case::Upper);
178        assert_eq!(encoder.space_remaining(), 2);
179        encoder.put_byte(42);
180        assert_eq!(encoder.space_remaining(), 1);
181        assert_eq!(encoder.as_str(), "2A");
182        assert!(!encoder.is_full());
183        encoder.clear();
184        assert_eq!(encoder.space_remaining(), 2);
185        assert!(!encoder.is_full());
186    }
187
188    #[test]
189    fn two_bytes() {
190        let mut encoder = BufEncoder::<4>::new(Case::Lower);
191        assert_eq!(encoder.space_remaining(), 2);
192        encoder.put_byte(42);
193        assert_eq!(encoder.space_remaining(), 1);
194        encoder.put_byte(255);
195        assert_eq!(encoder.space_remaining(), 0);
196        assert_eq!(encoder.as_str(), "2aff");
197        assert!(encoder.is_full());
198        encoder.clear();
199        assert_eq!(encoder.space_remaining(), 2);
200        assert!(!encoder.is_full());
201
202        let mut encoder = BufEncoder::<4>::new(Case::Upper);
203        assert_eq!(encoder.space_remaining(), 2);
204        encoder.put_byte(42);
205        assert_eq!(encoder.space_remaining(), 1);
206        encoder.put_byte(255);
207        assert_eq!(encoder.space_remaining(), 0);
208        assert_eq!(encoder.as_str(), "2AFF");
209        assert!(encoder.is_full());
210        encoder.clear();
211        assert_eq!(encoder.space_remaining(), 2);
212        assert!(!encoder.is_full());
213    }
214
215    #[test]
216    fn put_bytes_min() {
217        let mut encoder = BufEncoder::<2>::new(Case::Lower);
218        let remainder = encoder.put_bytes_min(b"");
219        assert_eq!(remainder, b"");
220        assert_eq!(encoder.as_str(), "");
221        let remainder = encoder.put_bytes_min(b"*");
222        assert_eq!(remainder, b"");
223        assert_eq!(encoder.as_str(), "2a");
224        encoder.clear();
225        let remainder = encoder.put_bytes_min(&[42, 255]);
226        assert_eq!(remainder, &[255]);
227        assert_eq!(encoder.as_str(), "2a");
228    }
229
230    #[test]
231    fn same_as_fmt() {
232        use core::fmt::{self, Write};
233
234        struct Writer {
235            buf: [u8; 2],
236            pos: usize,
237        }
238
239        impl Writer {
240            fn as_str(&self) -> &str { core::str::from_utf8(&self.buf[..self.pos]).unwrap() }
241        }
242
243        impl Write for Writer {
244            fn write_str(&mut self, s: &str) -> fmt::Result {
245                assert!(self.pos <= 2);
246                if s.len() > 2 - self.pos {
247                    Err(fmt::Error)
248                } else {
249                    self.buf[self.pos..(self.pos + s.len())].copy_from_slice(s.as_bytes());
250                    self.pos += s.len();
251                    Ok(())
252                }
253            }
254        }
255
256        let mut writer = Writer { buf: [0u8; 2], pos: 0 };
257
258        let mut encoder = BufEncoder::<2>::new(Case::Lower);
259        for i in 0..=255 {
260            write!(writer, "{:02x}", i).unwrap();
261            encoder.put_byte(i);
262            assert_eq!(encoder.as_str(), writer.as_str());
263            writer.pos = 0;
264            encoder.clear();
265        }
266
267        let mut encoder = BufEncoder::<2>::new(Case::Upper);
268        for i in 0..=255 {
269            write!(writer, "{:02X}", i).unwrap();
270            encoder.put_byte(i);
271            assert_eq!(encoder.as_str(), writer.as_str());
272            writer.pos = 0;
273            encoder.clear();
274        }
275    }
276}