hex_conservative/
iter.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Iterator that converts hex to bytes.
4
5use core::borrow::Borrow;
6use core::convert::TryInto;
7use core::iter::FusedIterator;
8use core::str;
9#[cfg(feature = "std")]
10use std::io;
11
12#[cfg(all(feature = "alloc", not(feature = "std")))]
13use crate::alloc::vec::Vec;
14use crate::error::{InvalidCharError, OddLengthStringError};
15use crate::{Case, Table};
16
17/// Convenience alias for `HexToBytesIter<HexDigitsIter<'a>>`.
18pub type HexSliceToBytesIter<'a> = HexToBytesIter<HexDigitsIter<'a>>;
19
20/// Iterator yielding bytes decoded from an iterator of pairs of hex digits.
21pub struct HexToBytesIter<T: Iterator<Item = [u8; 2]>> {
22    iter: T,
23    original_len: usize,
24}
25
26impl<'a> HexToBytesIter<HexDigitsIter<'a>> {
27    /// Constructs a new `HexToBytesIter` from a string slice.
28    ///
29    /// # Errors
30    ///
31    /// If the input string is of odd length.
32    #[inline]
33    pub fn new(s: &'a str) -> Result<Self, OddLengthStringError> {
34        if s.len() % 2 != 0 {
35            Err(OddLengthStringError { len: s.len() })
36        } else {
37            Ok(Self::new_unchecked(s))
38        }
39    }
40
41    pub(crate) fn new_unchecked(s: &'a str) -> Self {
42        Self::from_pairs(HexDigitsIter::new_unchecked(s.as_bytes()))
43    }
44
45    /// Writes all the bytes yielded by this `HexToBytesIter` to the provided slice.
46    ///
47    /// Stops writing if this `HexToBytesIter` yields an `InvalidCharError`.
48    ///
49    /// # Panics
50    ///
51    /// Panics if the length of this `HexToBytesIter` is not equal to the length of the provided
52    /// slice.
53    pub(crate) fn drain_to_slice(self, buf: &mut [u8]) -> Result<(), InvalidCharError> {
54        assert_eq!(self.len(), buf.len());
55        let mut ptr = buf.as_mut_ptr();
56        for byte in self {
57            // SAFETY: for loop iterates `len` times, and `buf` has length `len`
58            unsafe {
59                core::ptr::write(ptr, byte?);
60                ptr = ptr.add(1);
61            }
62        }
63        Ok(())
64    }
65
66    /// Writes all the bytes yielded by this `HexToBytesIter` to a `Vec<u8>`.
67    ///
68    /// This is equivalent to the combinator chain `iter().map().collect()` but was found by
69    /// benchmarking to be faster.
70    #[cfg(any(test, feature = "std", feature = "alloc"))]
71    pub(crate) fn drain_to_vec(self) -> Result<Vec<u8>, InvalidCharError> {
72        let len = self.len();
73        let mut ret = Vec::with_capacity(len);
74        let mut ptr = ret.as_mut_ptr();
75        for byte in self {
76            // SAFETY: for loop iterates `len` times, and `ret` has a capacity of at least `len`
77            unsafe {
78                // docs: "`core::ptr::write` is appropriate for initializing uninitialized memory"
79                core::ptr::write(ptr, byte?);
80                ptr = ptr.add(1);
81            }
82        }
83        // SAFETY: `len` elements have been initialized, and `ret` has a capacity of at least `len`
84        unsafe {
85            ret.set_len(len);
86        }
87        Ok(ret)
88    }
89}
90
91impl<T: Iterator<Item = [u8; 2]> + ExactSizeIterator> HexToBytesIter<T> {
92    /// Constructs a custom hex decoding iterator from another iterator.
93    #[inline]
94    pub fn from_pairs(iter: T) -> Self { Self { original_len: iter.len(), iter } }
95}
96
97impl<T: Iterator<Item = [u8; 2]> + ExactSizeIterator> Iterator for HexToBytesIter<T> {
98    type Item = Result<u8, InvalidCharError>;
99
100    #[inline]
101    fn next(&mut self) -> Option<Self::Item> {
102        let [hi, lo] = self.iter.next()?;
103        Some(hex_chars_to_byte(hi, lo).map_err(|(c, is_high)| InvalidCharError {
104            invalid: c,
105            pos: if is_high {
106                (self.original_len - self.iter.len() - 1) * 2
107            } else {
108                (self.original_len - self.iter.len() - 1) * 2 + 1
109            },
110        }))
111    }
112
113    #[inline]
114    fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
115
116    #[inline]
117    fn nth(&mut self, n: usize) -> Option<Self::Item> {
118        let [hi, lo] = self.iter.nth(n)?;
119        Some(hex_chars_to_byte(hi, lo).map_err(|(c, is_high)| InvalidCharError {
120            invalid: c,
121            pos: if is_high {
122                (self.original_len - self.iter.len() - 1) * 2
123            } else {
124                (self.original_len - self.iter.len() - 1) * 2 + 1
125            },
126        }))
127    }
128}
129
130impl<T: Iterator<Item = [u8; 2]> + DoubleEndedIterator + ExactSizeIterator> DoubleEndedIterator
131    for HexToBytesIter<T>
132{
133    #[inline]
134    fn next_back(&mut self) -> Option<Self::Item> {
135        let [hi, lo] = self.iter.next_back()?;
136        Some(hex_chars_to_byte(hi, lo).map_err(|(c, is_high)| InvalidCharError {
137            invalid: c,
138            pos: if is_high { self.iter.len() * 2 } else { self.iter.len() * 2 + 1 },
139        }))
140    }
141
142    #[inline]
143    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
144        let [hi, lo] = self.iter.nth_back(n)?;
145        Some(hex_chars_to_byte(hi, lo).map_err(|(c, is_high)| InvalidCharError {
146            invalid: c,
147            pos: if is_high { self.iter.len() * 2 } else { self.iter.len() * 2 + 1 },
148        }))
149    }
150}
151
152impl<T: Iterator<Item = [u8; 2]> + ExactSizeIterator> ExactSizeIterator for HexToBytesIter<T> {}
153
154impl<T: Iterator<Item = [u8; 2]> + ExactSizeIterator + FusedIterator> FusedIterator
155    for HexToBytesIter<T>
156{
157}
158
159#[cfg(feature = "std")]
160impl<T: Iterator<Item = [u8; 2]> + ExactSizeIterator + FusedIterator> io::Read
161    for HexToBytesIter<T>
162{
163    #[inline]
164    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
165        let mut bytes_read = 0usize;
166        for dst in buf {
167            match self.next() {
168                Some(Ok(src)) => {
169                    *dst = src;
170                    bytes_read += 1;
171                }
172                _ => break,
173            }
174        }
175        Ok(bytes_read)
176    }
177}
178
179/// An internal iterator returning hex digits from a string.
180///
181/// Generally you shouldn't need to refer to this or bother with it and just use
182/// [`HexToBytesIter::new`] consuming the returned value and use `HexSliceToBytesIter` if you need
183/// to refer to the iterator in your types.
184pub struct HexDigitsIter<'a> {
185    // Invariant: the length of the chunks is 2.
186    // Technically, this is `iter::Map` but we can't use it because fn is anonymous.
187    // We can swap this for actual `ArrayChunks` once it's stable.
188    iter: core::slice::ChunksExact<'a, u8>,
189}
190
191impl<'a> HexDigitsIter<'a> {
192    #[inline]
193    fn new_unchecked(digits: &'a [u8]) -> Self { Self { iter: digits.chunks_exact(2) } }
194}
195
196impl<'a> Iterator for HexDigitsIter<'a> {
197    type Item = [u8; 2];
198
199    #[inline]
200    fn next(&mut self) -> Option<Self::Item> {
201        self.iter.next().map(|digits| digits.try_into().expect("HexDigitsIter invariant"))
202    }
203
204    #[inline]
205    fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
206
207    #[inline]
208    fn nth(&mut self, n: usize) -> Option<Self::Item> {
209        self.iter.nth(n).map(|digits| digits.try_into().expect("HexDigitsIter invariant"))
210    }
211}
212
213impl<'a> DoubleEndedIterator for HexDigitsIter<'a> {
214    #[inline]
215    fn next_back(&mut self) -> Option<Self::Item> {
216        self.iter.next_back().map(|digits| digits.try_into().expect("HexDigitsIter invariant"))
217    }
218
219    #[inline]
220    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
221        self.iter.nth_back(n).map(|digits| digits.try_into().expect("HexDigitsIter invariant"))
222    }
223}
224
225impl<'a> ExactSizeIterator for HexDigitsIter<'a> {}
226
227impl<'a> core::iter::FusedIterator for HexDigitsIter<'a> {}
228
229/// `hi` and `lo` are bytes representing hex characters.
230///
231/// Returns the valid byte or the invalid input byte and a bool indicating error for `hi` or `lo`.
232fn hex_chars_to_byte(hi: u8, lo: u8) -> Result<u8, (u8, bool)> {
233    let hih = (hi as char).to_digit(16).ok_or((hi, true))?;
234    let loh = (lo as char).to_digit(16).ok_or((lo, false))?;
235
236    let ret = (hih << 4) + loh;
237    Ok(ret as u8)
238}
239
240/// Iterator over bytes which encodes the bytes and yields hex characters.
241pub struct BytesToHexIter<I>
242where
243    I: Iterator,
244    I::Item: Borrow<u8>,
245{
246    /// The iterator whose next byte will be encoded to yield hex characters.
247    iter: I,
248    /// The low character of the pair (high, low) of hex characters encoded per byte.
249    low: Option<char>,
250    /// The byte-to-hex conversion table.
251    table: &'static Table,
252}
253
254impl<I> BytesToHexIter<I>
255where
256    I: Iterator,
257    I::Item: Borrow<u8>,
258{
259    /// Constructs a `BytesToHexIter` that will yield hex characters in the given case from a byte
260    /// iterator.
261    pub fn new(iter: I, case: Case) -> BytesToHexIter<I> {
262        Self { iter, low: None, table: case.table() }
263    }
264}
265
266impl<I> Iterator for BytesToHexIter<I>
267where
268    I: Iterator,
269    I::Item: Borrow<u8>,
270{
271    type Item = char;
272
273    #[inline]
274    fn next(&mut self) -> Option<char> {
275        match self.low {
276            Some(c) => {
277                self.low = None;
278                Some(c)
279            }
280            None => self.iter.next().map(|b| {
281                let [high, low] = self.table.byte_to_chars(*b.borrow());
282                self.low = Some(low);
283                high
284            }),
285        }
286    }
287
288    #[inline]
289    fn size_hint(&self) -> (usize, Option<usize>) {
290        let (min, max) = self.iter.size_hint();
291        match self.low {
292            Some(_) => (min * 2 + 1, max.map(|max| max * 2 + 1)),
293            None => (min * 2, max.map(|max| max * 2)),
294        }
295    }
296}
297
298impl<I> DoubleEndedIterator for BytesToHexIter<I>
299where
300    I: DoubleEndedIterator,
301    I::Item: Borrow<u8>,
302{
303    #[inline]
304    fn next_back(&mut self) -> Option<char> {
305        match self.low {
306            Some(c) => {
307                self.low = None;
308                Some(c)
309            }
310            None => self.iter.next_back().map(|b| {
311                let [high, low] = self.table.byte_to_chars(*b.borrow());
312                self.low = Some(low);
313                high
314            }),
315        }
316    }
317}
318
319impl<I> ExactSizeIterator for BytesToHexIter<I>
320where
321    I: ExactSizeIterator,
322    I::Item: Borrow<u8>,
323{
324    #[inline]
325    fn len(&self) -> usize { self.iter.len() * 2 }
326}
327
328impl<I> FusedIterator for BytesToHexIter<I>
329where
330    I: FusedIterator,
331    I::Item: Borrow<u8>,
332{
333}
334
335#[cfg(test)]
336mod tests {
337    use super::*;
338
339    #[test]
340    fn encode_byte() {
341        assert_eq!(Table::LOWER.byte_to_chars(0x00), ['0', '0']);
342        assert_eq!(Table::LOWER.byte_to_chars(0x0a), ['0', 'a']);
343        assert_eq!(Table::LOWER.byte_to_chars(0xad), ['a', 'd']);
344        assert_eq!(Table::LOWER.byte_to_chars(0xff), ['f', 'f']);
345
346        assert_eq!(Table::UPPER.byte_to_chars(0x00), ['0', '0']);
347        assert_eq!(Table::UPPER.byte_to_chars(0x0a), ['0', 'A']);
348        assert_eq!(Table::UPPER.byte_to_chars(0xad), ['A', 'D']);
349        assert_eq!(Table::UPPER.byte_to_chars(0xff), ['F', 'F']);
350
351        let mut buf = [0u8; 2];
352        assert_eq!(Table::LOWER.byte_to_str(&mut buf, 0x00), "00");
353        assert_eq!(Table::LOWER.byte_to_str(&mut buf, 0x0a), "0a");
354        assert_eq!(Table::LOWER.byte_to_str(&mut buf, 0xad), "ad");
355        assert_eq!(Table::LOWER.byte_to_str(&mut buf, 0xff), "ff");
356
357        assert_eq!(Table::UPPER.byte_to_str(&mut buf, 0x00), "00");
358        assert_eq!(Table::UPPER.byte_to_str(&mut buf, 0x0a), "0A");
359        assert_eq!(Table::UPPER.byte_to_str(&mut buf, 0xad), "AD");
360        assert_eq!(Table::UPPER.byte_to_str(&mut buf, 0xff), "FF");
361    }
362
363    #[test]
364    fn decode_iter_forward() {
365        let hex = "deadbeef";
366        let bytes = [0xde, 0xad, 0xbe, 0xef];
367
368        for (i, b) in HexToBytesIter::new(hex).unwrap().enumerate() {
369            assert_eq!(b.unwrap(), bytes[i]);
370        }
371
372        let mut iter = HexToBytesIter::new(hex).unwrap();
373        for i in (0..=bytes.len()).rev() {
374            assert_eq!(iter.len(), i);
375            let _ = iter.next();
376        }
377    }
378
379    #[test]
380    fn decode_iter_backward() {
381        let hex = "deadbeef";
382        let bytes = [0xef, 0xbe, 0xad, 0xde];
383
384        for (i, b) in HexToBytesIter::new(hex).unwrap().rev().enumerate() {
385            assert_eq!(b.unwrap(), bytes[i]);
386        }
387
388        let mut iter = HexToBytesIter::new(hex).unwrap().rev();
389        for i in (0..=bytes.len()).rev() {
390            assert_eq!(iter.len(), i);
391            let _ = iter.next();
392        }
393    }
394
395    #[test]
396    fn hex_to_digits_size_hint() {
397        let hex = "deadbeef";
398        let iter = HexDigitsIter::new_unchecked(hex.as_bytes());
399        // HexDigitsIter yields two digits at a time `[u8; 2]`.
400        assert_eq!(iter.size_hint(), (4, Some(4)));
401    }
402
403    #[test]
404    fn hex_to_bytes_size_hint() {
405        let hex = "deadbeef";
406        let iter = HexToBytesIter::new_unchecked(hex);
407        assert_eq!(iter.size_hint(), (4, Some(4)));
408    }
409
410    #[test]
411    fn hex_to_bytes_slice_drain() {
412        let hex = "deadbeef";
413        let want = [0xde, 0xad, 0xbe, 0xef];
414        let iter = HexToBytesIter::new_unchecked(hex);
415        let mut got = [0u8; 4];
416        iter.drain_to_slice(&mut got).unwrap();
417        assert_eq!(got, want);
418
419        let hex = "";
420        let want = [];
421        let iter = HexToBytesIter::new_unchecked(hex);
422        let mut got = [];
423        iter.drain_to_slice(&mut got).unwrap();
424        assert_eq!(got, want);
425    }
426
427    #[test]
428    #[should_panic]
429    fn hex_to_bytes_slice_drain_panic_empty() {
430        let hex = "deadbeef";
431        let iter = HexToBytesIter::new_unchecked(hex);
432        let mut got = [];
433        iter.drain_to_slice(&mut got).unwrap();
434    }
435
436    #[test]
437    #[should_panic]
438    fn hex_to_bytes_slice_drain_panic_too_small() {
439        let hex = "deadbeef";
440        let iter = HexToBytesIter::new_unchecked(hex);
441        let mut got = [0u8; 3];
442        iter.drain_to_slice(&mut got).unwrap();
443    }
444
445    #[test]
446    #[should_panic]
447    fn hex_to_bytes_slice_drain_panic_too_big() {
448        let hex = "deadbeef";
449        let iter = HexToBytesIter::new_unchecked(hex);
450        let mut got = [0u8; 5];
451        iter.drain_to_slice(&mut got).unwrap();
452    }
453
454    #[test]
455    fn hex_to_bytes_slice_drain_first_char_error() {
456        let hex = "geadbeef";
457        let iter = HexToBytesIter::new_unchecked(hex);
458        let mut got = [0u8; 4];
459        assert_eq!(iter.drain_to_slice(&mut got), Err(InvalidCharError { invalid: b'g', pos: 0 }));
460    }
461
462    #[test]
463    fn hex_to_bytes_slice_drain_middle_char_error() {
464        let hex = "deadgeef";
465        let iter = HexToBytesIter::new_unchecked(hex);
466        let mut got = [0u8; 4];
467        assert_eq!(iter.drain_to_slice(&mut got), Err(InvalidCharError { invalid: b'g', pos: 4 }));
468    }
469
470    #[test]
471    fn hex_to_bytes_slice_drain_end_char_error() {
472        let hex = "deadbeeg";
473        let iter = HexToBytesIter::new_unchecked(hex);
474        let mut got = [0u8; 4];
475        assert_eq!(iter.drain_to_slice(&mut got), Err(InvalidCharError { invalid: b'g', pos: 7 }));
476    }
477
478    #[test]
479    fn hex_to_bytes_vec_drain() {
480        let hex = "deadbeef";
481        let want = [0xde, 0xad, 0xbe, 0xef];
482        let iter = HexToBytesIter::new_unchecked(hex);
483        let got = iter.drain_to_vec().unwrap();
484        assert_eq!(got, want);
485
486        let hex = "";
487        let iter = HexToBytesIter::new_unchecked(hex);
488        let got = iter.drain_to_vec().unwrap();
489        assert!(got.is_empty());
490    }
491
492    #[test]
493    fn hex_to_bytes_vec_drain_first_char_error() {
494        let hex = "geadbeef";
495        let iter = HexToBytesIter::new_unchecked(hex);
496        assert_eq!(iter.drain_to_vec(), Err(InvalidCharError { invalid: b'g', pos: 0 }));
497    }
498
499    #[test]
500    fn hex_to_bytes_vec_drain_middle_char_error() {
501        let hex = "deadgeef";
502        let iter = HexToBytesIter::new_unchecked(hex);
503        assert_eq!(iter.drain_to_vec(), Err(InvalidCharError { invalid: b'g', pos: 4 }));
504    }
505
506    #[test]
507    fn hex_to_bytes_vec_drain_end_char_error() {
508        let hex = "deadbeeg";
509        let iter = HexToBytesIter::new_unchecked(hex);
510        assert_eq!(iter.drain_to_vec(), Err(InvalidCharError { invalid: b'g', pos: 7 }));
511    }
512
513    #[test]
514    fn encode_iter() {
515        let bytes = [0xde, 0xad, 0xbe, 0xef];
516        let lower_want = "deadbeef";
517        let upper_want = "DEADBEEF";
518
519        for (i, c) in BytesToHexIter::new(bytes.iter(), Case::Lower).enumerate() {
520            assert_eq!(c, lower_want.chars().nth(i).unwrap());
521        }
522        for (i, c) in BytesToHexIter::new(bytes.iter(), Case::Upper).enumerate() {
523            assert_eq!(c, upper_want.chars().nth(i).unwrap());
524        }
525    }
526
527    #[test]
528    fn encode_iter_backwards() {
529        let bytes = [0xde, 0xad, 0xbe, 0xef];
530        let lower_want = "efbeadde";
531        let upper_want = "EFBEADDE";
532
533        for (i, c) in BytesToHexIter::new(bytes.iter(), Case::Lower).rev().enumerate() {
534            assert_eq!(c, lower_want.chars().nth(i).unwrap());
535        }
536        for (i, c) in BytesToHexIter::new(bytes.iter(), Case::Upper).rev().enumerate() {
537            assert_eq!(c, upper_want.chars().nth(i).unwrap());
538        }
539    }
540
541    #[test]
542    fn roundtrip_forward() {
543        let lower_want = "deadbeefcafebabe";
544        let upper_want = "DEADBEEFCAFEBABE";
545        let lower_bytes_iter = HexToBytesIter::new(lower_want).unwrap().map(|res| res.unwrap());
546        let lower_got = BytesToHexIter::new(lower_bytes_iter, Case::Lower).collect::<String>();
547        assert_eq!(lower_got, lower_want);
548        let upper_bytes_iter = HexToBytesIter::new(upper_want).unwrap().map(|res| res.unwrap());
549        let upper_got = BytesToHexIter::new(upper_bytes_iter, Case::Upper).collect::<String>();
550        assert_eq!(upper_got, upper_want);
551    }
552
553    #[test]
554    fn roundtrip_backward() {
555        let lower_want = "deadbeefcafebabe";
556        let upper_want = "DEADBEEFCAFEBABE";
557        let lower_bytes_iter =
558            HexToBytesIter::new(lower_want).unwrap().rev().map(|res| res.unwrap());
559        let lower_got =
560            BytesToHexIter::new(lower_bytes_iter, Case::Lower).rev().collect::<String>();
561        assert_eq!(lower_got, lower_want);
562        let upper_bytes_iter =
563            HexToBytesIter::new(upper_want).unwrap().rev().map(|res| res.unwrap());
564        let upper_got =
565            BytesToHexIter::new(upper_bytes_iter, Case::Upper).rev().collect::<String>();
566        assert_eq!(upper_got, upper_want);
567    }
568}