Skip to main content

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(feature = "alloc")]
13use crate::alloc::vec::Vec;
14use crate::error::{InvalidCharError, OddLengthStringError};
15use crate::{Case, Char, Table};
16
17/// Iterator over bytes decoded from a hex string slice.
18///
19/// This is an iterator type returned when decoding a `&str` of hex digits. Each pair of hex
20/// characters is decoded into one byte.
21///
22/// Use [`HexToBytesIter`] if you need an iterator that is generic over the source of hex digit
23/// pairs.
24#[derive(Debug, Clone)]
25pub struct HexSliceToBytesIter<'a>(HexToBytesIter<HexDigitsIter<'a>>);
26
27impl<'a> HexSliceToBytesIter<'a> {
28    /// Constructs a new [`HexSliceToBytesIter`] from a string slice.
29    ///
30    /// # Errors
31    ///
32    /// If the input string is of odd length.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// # #[cfg(feature = "std")] {
38    /// # use hex_conservative::HexSliceToBytesIter;
39    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
40    /// let bytes: Vec<u8> = HexSliceToBytesIter::new("deadbeef")?
41    ///     .collect::<Result<_, _>>()?;
42    /// assert_eq!(bytes, [0xde, 0xad, 0xbe, 0xef]);
43    /// # Ok(())
44    /// # }
45    /// # }
46    /// ```
47    #[inline]
48    pub fn new(s: &'a str) -> Result<Self, OddLengthStringError> {
49        HexToBytesIter::new(s).map(Self)
50    }
51}
52
53impl Iterator for HexSliceToBytesIter<'_> {
54    type Item = Result<u8, InvalidCharError>;
55
56    #[inline]
57    fn next(&mut self) -> Option<Self::Item> { self.0.next() }
58
59    #[inline]
60    fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
61
62    #[inline]
63    fn nth(&mut self, n: usize) -> Option<Self::Item> { self.0.nth(n) }
64}
65
66impl DoubleEndedIterator for HexSliceToBytesIter<'_> {
67    #[inline]
68    fn next_back(&mut self) -> Option<Self::Item> { self.0.next_back() }
69
70    #[inline]
71    fn nth_back(&mut self, n: usize) -> Option<Self::Item> { self.0.nth_back(n) }
72}
73
74impl ExactSizeIterator for HexSliceToBytesIter<'_> {}
75
76impl FusedIterator for HexSliceToBytesIter<'_> {}
77
78#[cfg(feature = "std")]
79impl io::Read for HexSliceToBytesIter<'_> {
80    #[inline]
81    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) }
82}
83
84/// Iterator yielding bytes decoded from an iterator of pairs of hex digits.
85#[derive(Debug, Clone, PartialEq, Eq, Hash)]
86pub struct HexToBytesIter<I>
87where
88    I: Iterator<Item = [u8; 2]>,
89{
90    iter: I,
91    /// Number of (high, low) char pairs consumed from the front.
92    front_pos: usize,
93}
94
95impl<'a> HexToBytesIter<HexDigitsIter<'a>> {
96    /// Constructs a new `HexToBytesIter` from a string slice.
97    ///
98    /// # Errors
99    ///
100    /// If the input string is of odd length.
101    #[inline]
102    pub(crate) fn new(s: &'a str) -> Result<Self, OddLengthStringError> {
103        if s.len() % 2 != 0 {
104            Err(OddLengthStringError { len: s.len() })
105        } else {
106            Ok(Self::new_unchecked(s))
107        }
108    }
109
110    #[inline]
111    pub(crate) fn new_unchecked(s: &'a str) -> Self {
112        Self::from_pairs(HexDigitsIter::new_unchecked(s.as_bytes()))
113    }
114
115    /// Writes all the bytes yielded by this `HexToBytesIter` to the provided slice.
116    ///
117    /// Stops writing if this `HexToBytesIter` yields an `InvalidCharError`.
118    ///
119    /// # Panics
120    ///
121    /// Panics if the length of this `HexToBytesIter` is not equal to the length of the provided
122    /// slice.
123    pub(crate) fn drain_to_slice(self, buf: &mut [u8]) -> Result<(), InvalidCharError> {
124        assert_eq!(self.len(), buf.len());
125        let mut ptr = buf.as_mut_ptr();
126        for byte in self {
127            // SAFETY: for loop iterates `len` times, and `buf` has length `len`
128            unsafe {
129                core::ptr::write(ptr, byte?);
130                ptr = ptr.add(1);
131            }
132        }
133        Ok(())
134    }
135
136    /// Writes all the bytes yielded by this `HexToBytesIter` to a `Vec<u8>`.
137    ///
138    /// This is equivalent to the combinator chain `iter().map().collect()` but was found by
139    /// benchmarking to be faster.
140    #[cfg(feature = "alloc")]
141    pub(crate) fn drain_to_vec(self) -> Result<Vec<u8>, InvalidCharError> {
142        let len = self.len();
143        let mut ret = Vec::with_capacity(len);
144        let mut ptr = ret.as_mut_ptr();
145        for byte in self {
146            // SAFETY: for loop iterates `len` times, and `ret` has a capacity of at least `len`
147            unsafe {
148                // docs: "`core::ptr::write` is appropriate for initializing uninitialized memory"
149                core::ptr::write(ptr, byte?);
150                ptr = ptr.add(1);
151            }
152        }
153        // SAFETY: `len` elements have been initialized, and `ret` has a capacity of at least `len`
154        unsafe {
155            ret.set_len(len);
156        }
157        Ok(ret)
158    }
159}
160
161impl<I> HexToBytesIter<I>
162where
163    I: Iterator<Item = [u8; 2]> + ExactSizeIterator,
164{
165    /// Constructs a custom hex decoding iterator from another iterator.
166    ///
167    /// # Examples
168    ///
169    /// ```
170    /// # #[cfg(feature = "std")] {
171    /// # use hex_conservative::HexToBytesIter;
172    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
173    /// let hex_digits: Vec<u8> = b"deadbeef".iter().copied().collect();
174    /// let pairs = hex_digits.chunks_exact(2).map(|c| [c[0], c[1]]);
175    /// let decoded: Vec<u8> = HexToBytesIter::from_pairs(pairs)
176    ///     .collect::<Result<_, _>>()?;
177    /// assert_eq!(decoded, [0xde, 0xad, 0xbe, 0xef]);
178    /// # Ok(())
179    /// # }
180    /// # }
181    /// ```
182    #[inline]
183    pub fn from_pairs(iter: I) -> Self { Self { front_pos: 0, iter } }
184}
185
186impl<I> Iterator for HexToBytesIter<I>
187where
188    I: Iterator<Item = [u8; 2]> + ExactSizeIterator,
189{
190    type Item = Result<u8, InvalidCharError>;
191
192    #[inline]
193    fn next(&mut self) -> Option<Self::Item> { self.nth(0) }
194
195    #[inline]
196    fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
197
198    #[inline]
199    fn nth(&mut self, n: usize) -> Option<Self::Item> {
200        let [hi, lo] = self.iter.nth(n)?;
201        let pos = self.front_pos.saturating_add(n).saturating_mul(2);
202        self.front_pos = self.front_pos.saturating_add(n).saturating_add(1);
203        Some(hex_chars_to_byte(hi, lo).map_err(|(c, is_high)| InvalidCharError {
204            invalid: c,
205            pos: if is_high { pos } else { pos.saturating_add(1) },
206        }))
207    }
208}
209
210impl<I> DoubleEndedIterator for HexToBytesIter<I>
211where
212    I: Iterator<Item = [u8; 2]> + DoubleEndedIterator + ExactSizeIterator,
213{
214    #[inline]
215    fn next_back(&mut self) -> Option<Self::Item> { self.nth_back(0) }
216
217    #[inline]
218    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
219        let [hi, lo] = self.iter.nth_back(n)?;
220        let pos = (self.front_pos + self.iter.len()).saturating_mul(2);
221        Some(hex_chars_to_byte(hi, lo).map_err(|(c, is_high)| InvalidCharError {
222            invalid: c,
223            pos: if is_high { pos } else { pos.saturating_add(1) },
224        }))
225    }
226}
227
228impl<I> ExactSizeIterator for HexToBytesIter<I> where I: Iterator<Item = [u8; 2]> + ExactSizeIterator
229{}
230
231impl<I> FusedIterator for HexToBytesIter<I> where
232    I: Iterator<Item = [u8; 2]> + ExactSizeIterator + FusedIterator
233{
234}
235
236#[cfg(feature = "std")]
237impl<I> io::Read for HexToBytesIter<I>
238where
239    I: Iterator<Item = [u8; 2]> + ExactSizeIterator + FusedIterator,
240{
241    #[inline]
242    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
243        let mut bytes_read = 0usize;
244        for dst in buf {
245            match self.next() {
246                Some(Ok(src)) => {
247                    *dst = src;
248                    bytes_read += 1;
249                }
250                Some(Err(e)) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)),
251                None => break,
252            }
253        }
254        Ok(bytes_read)
255    }
256}
257
258/// An internal iterator returning hex digits from a string.
259///
260/// Generally you shouldn't need to refer to this or bother with it and just use
261/// [`HexToBytesIter::new`] consuming the returned value and use `HexSliceToBytesIter` if you need
262/// to refer to the iterator in your types.
263#[derive(Debug, Clone)]
264pub struct HexDigitsIter<'a> {
265    // Invariant: the length of the chunks is 2.
266    // Technically, this is `iter::Map` but we can't use it because fn is anonymous.
267    // We can swap this for actual `ArrayChunks` once it's stable.
268    iter: core::slice::ChunksExact<'a, u8>,
269}
270
271impl<'a> HexDigitsIter<'a> {
272    #[inline]
273    fn new_unchecked(digits: &'a [u8]) -> Self { Self { iter: digits.chunks_exact(2) } }
274}
275
276impl Iterator for HexDigitsIter<'_> {
277    type Item = [u8; 2];
278
279    #[inline]
280    fn next(&mut self) -> Option<Self::Item> {
281        self.iter.next().map(|digits| digits.try_into().expect("HexDigitsIter invariant"))
282    }
283
284    #[inline]
285    fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
286
287    #[inline]
288    fn nth(&mut self, n: usize) -> Option<Self::Item> {
289        self.iter.nth(n).map(|digits| digits.try_into().expect("HexDigitsIter invariant"))
290    }
291}
292
293impl DoubleEndedIterator for HexDigitsIter<'_> {
294    #[inline]
295    fn next_back(&mut self) -> Option<Self::Item> {
296        self.iter.next_back().map(|digits| digits.try_into().expect("HexDigitsIter invariant"))
297    }
298
299    #[inline]
300    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
301        self.iter.nth_back(n).map(|digits| digits.try_into().expect("HexDigitsIter invariant"))
302    }
303}
304
305impl ExactSizeIterator for HexDigitsIter<'_> {}
306
307impl core::iter::FusedIterator for HexDigitsIter<'_> {}
308
309/// `hi` and `lo` are bytes representing hex characters.
310///
311/// Returns the valid byte or the invalid input byte and a bool indicating error for `hi` or `lo`.
312fn hex_chars_to_byte(hi: u8, lo: u8) -> Result<u8, (u8, bool)> {
313    let hih = Char::decode_nibble(hi).ok_or((hi, true))?;
314    let loh = Char::decode_nibble(lo).ok_or((lo, false))?;
315    Ok((hih << 4) | loh)
316}
317
318/// Iterator over bytes which encodes the bytes and yields `[Char; 2]` pairs of hex characters.
319///
320/// Each call to [`Iterator::next`] consumes one byte and returns the two hex digits that encode
321/// it as `[high_nibble, low_nibble]`.
322///
323/// If you want to yield a stream of [`Char`] only, call [`flatten`].
324///
325/// # Examples
326///
327/// ```
328/// # #[cfg(feature = "alloc")]
329/// # {
330/// use hex_conservative::{BytesToHexIter, Case};
331///
332/// let bytes = [0xde, 0xad, 0xbe, 0xef].into_iter();
333/// let hex_string: String =
334///     BytesToHexIter::new(bytes, Case::Lower).flatten().map(char::from).collect();
335/// assert_eq!(hex_string, "deadbeef");
336/// # }
337///```
338///
339/// [`flatten`]: Iterator::flatten
340#[derive(Debug, Clone, PartialEq, Eq, Hash)]
341pub struct BytesToHexIter<I>
342where
343    I: Iterator,
344    I::Item: Borrow<u8>,
345{
346    /// The iterator whose next byte will be encoded to yield hex characters.
347    iter: I,
348    /// The byte-to-hex conversion table.
349    table: &'static Table,
350}
351
352impl<I> BytesToHexIter<I>
353where
354    I: Iterator,
355    I::Item: Borrow<u8>,
356{
357    /// Constructs a `BytesToHexIter` that will yield hex character pairs in the given case from a
358    /// byte iterator.
359    pub fn new(iter: I, case: Case) -> BytesToHexIter<I> { Self { iter, table: case.table() } }
360}
361
362impl<I> Iterator for BytesToHexIter<I>
363where
364    I: Iterator,
365    I::Item: Borrow<u8>,
366{
367    type Item = [Char; 2];
368
369    #[inline]
370    fn next(&mut self) -> Option<[Char; 2]> {
371        self.iter.next().map(|b| self.table.byte_to_hex_chars(*b.borrow()))
372    }
373
374    #[inline]
375    fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
376
377    #[inline]
378    fn nth(&mut self, n: usize) -> Option<[Char; 2]> {
379        self.iter.nth(n).map(|b| self.table.byte_to_hex_chars(*b.borrow()))
380    }
381}
382
383impl<I> DoubleEndedIterator for BytesToHexIter<I>
384where
385    I: DoubleEndedIterator,
386    I::Item: Borrow<u8>,
387{
388    #[inline]
389    fn next_back(&mut self) -> Option<[Char; 2]> {
390        self.iter.next_back().map(|b| self.table.byte_to_hex_chars(*b.borrow()))
391    }
392
393    #[inline]
394    fn nth_back(&mut self, n: usize) -> Option<[Char; 2]> {
395        self.iter.nth_back(n).map(|b| self.table.byte_to_hex_chars(*b.borrow()))
396    }
397}
398
399impl<I> ExactSizeIterator for BytesToHexIter<I>
400where
401    I: ExactSizeIterator,
402    I::Item: Borrow<u8>,
403{
404    #[inline]
405    fn len(&self) -> usize { self.iter.len() }
406}
407
408impl<I> FusedIterator for BytesToHexIter<I>
409where
410    I: FusedIterator,
411    I::Item: Borrow<u8>,
412{
413}
414
415#[cfg(test)]
416mod tests {
417    #[cfg(feature = "alloc")]
418    use alloc::string::String;
419
420    use super::*;
421
422    fn nth_slow<I: Iterator>(iter: &mut I, n: usize) -> Option<I::Item> {
423        for _ in 0..n {
424            iter.next()?;
425        }
426        iter.next()
427    }
428
429    fn nth_back_slow<I: DoubleEndedIterator>(iter: &mut I, n: usize) -> Option<I::Item> {
430        for _ in 0..n {
431            iter.next_back()?;
432        }
433        iter.next_back()
434    }
435
436    #[test]
437    fn encode_byte() {
438        assert_eq!(Table::LOWER.byte_to_chars(0x00), ['0', '0']);
439        assert_eq!(Table::LOWER.byte_to_chars(0x0a), ['0', 'a']);
440        assert_eq!(Table::LOWER.byte_to_chars(0xad), ['a', 'd']);
441        assert_eq!(Table::LOWER.byte_to_chars(0xff), ['f', 'f']);
442
443        assert_eq!(Table::UPPER.byte_to_chars(0x00), ['0', '0']);
444        assert_eq!(Table::UPPER.byte_to_chars(0x0a), ['0', 'A']);
445        assert_eq!(Table::UPPER.byte_to_chars(0xad), ['A', 'D']);
446        assert_eq!(Table::UPPER.byte_to_chars(0xff), ['F', 'F']);
447
448        let mut buf = [0u8; 2];
449        assert_eq!(Table::LOWER.byte_to_str(&mut buf, 0x00), "00");
450        assert_eq!(Table::LOWER.byte_to_str(&mut buf, 0x0a), "0a");
451        assert_eq!(Table::LOWER.byte_to_str(&mut buf, 0xad), "ad");
452        assert_eq!(Table::LOWER.byte_to_str(&mut buf, 0xff), "ff");
453
454        assert_eq!(Table::UPPER.byte_to_str(&mut buf, 0x00), "00");
455        assert_eq!(Table::UPPER.byte_to_str(&mut buf, 0x0a), "0A");
456        assert_eq!(Table::UPPER.byte_to_str(&mut buf, 0xad), "AD");
457        assert_eq!(Table::UPPER.byte_to_str(&mut buf, 0xff), "FF");
458    }
459
460    #[test]
461    fn decode_iter_forward() {
462        let hex = "deadbeef";
463        let bytes = [0xde, 0xad, 0xbe, 0xef];
464
465        for (i, b) in HexToBytesIter::new(hex).unwrap().enumerate() {
466            assert_eq!(b.unwrap(), bytes[i]);
467        }
468
469        let mut iter = HexToBytesIter::new(hex).unwrap();
470        for i in (0..=bytes.len()).rev() {
471            assert_eq!(iter.len(), i);
472            let _ = iter.next();
473        }
474    }
475
476    #[test]
477    fn decode_iter_backward() {
478        let hex = "deadbeef";
479        let bytes = [0xef, 0xbe, 0xad, 0xde];
480
481        for (i, b) in HexToBytesIter::new(hex).unwrap().rev().enumerate() {
482            assert_eq!(b.unwrap(), bytes[i]);
483        }
484
485        let mut iter = HexToBytesIter::new(hex).unwrap().rev();
486        for i in (0..=bytes.len()).rev() {
487            assert_eq!(iter.len(), i);
488            let _ = iter.next();
489        }
490    }
491
492    #[test]
493    fn hex_to_digits_size_hint() {
494        let hex = "deadbeef";
495        let iter = HexDigitsIter::new_unchecked(hex.as_bytes());
496        // HexDigitsIter yields two digits at a time `[u8; 2]`.
497        assert_eq!(iter.size_hint(), (4, Some(4)));
498    }
499
500    #[test]
501    fn hex_to_bytes_size_hint() {
502        let hex = "deadbeef";
503        let iter = HexToBytesIter::new_unchecked(hex);
504        assert_eq!(iter.size_hint(), (4, Some(4)));
505    }
506
507    #[test]
508    fn hex_to_bytes_slice_drain() {
509        let hex = "deadbeef";
510        let want = [0xde, 0xad, 0xbe, 0xef];
511        let iter = HexToBytesIter::new_unchecked(hex);
512        let mut got = [0u8; 4];
513        iter.drain_to_slice(&mut got).unwrap();
514        assert_eq!(got, want);
515
516        let hex = "";
517        let want: [u8; 0] = [];
518        let iter = HexToBytesIter::new_unchecked(hex);
519        let mut got = [];
520        iter.drain_to_slice(&mut got).unwrap();
521        assert_eq!(got, want);
522    }
523
524    #[test]
525    #[should_panic]
526    // Don't test panic message because it is from `debug_assert`.
527    #[allow(clippy::should_panic_without_expect)]
528    fn hex_to_bytes_slice_drain_panic_empty() {
529        let hex = "deadbeef";
530        let iter = HexToBytesIter::new_unchecked(hex);
531        let mut got = [];
532        iter.drain_to_slice(&mut got).unwrap();
533    }
534
535    #[test]
536    #[should_panic]
537    // Don't test panic message because it is from `debug_assert`.
538    #[allow(clippy::should_panic_without_expect)]
539    fn hex_to_bytes_slice_drain_panic_too_small() {
540        let hex = "deadbeef";
541        let iter = HexToBytesIter::new_unchecked(hex);
542        let mut got = [0u8; 3];
543        iter.drain_to_slice(&mut got).unwrap();
544    }
545
546    #[test]
547    #[should_panic]
548    // Don't test panic message because it is from `debug_assert`.
549    #[allow(clippy::should_panic_without_expect)]
550    fn hex_to_bytes_slice_drain_panic_too_big() {
551        let hex = "deadbeef";
552        let iter = HexToBytesIter::new_unchecked(hex);
553        let mut got = [0u8; 5];
554        iter.drain_to_slice(&mut got).unwrap();
555    }
556
557    #[test]
558    fn hex_to_bytes_slice_drain_first_char_error() {
559        let hex = "geadbeef";
560        let iter = HexToBytesIter::new_unchecked(hex);
561        let mut got = [0u8; 4];
562        assert_eq!(
563            iter.drain_to_slice(&mut got).unwrap_err(),
564            InvalidCharError { invalid: b'g', pos: 0 }
565        );
566    }
567
568    #[test]
569    fn hex_to_bytes_slice_drain_middle_char_error() {
570        let hex = "deadgeef";
571        let iter = HexToBytesIter::new_unchecked(hex);
572        let mut got = [0u8; 4];
573        assert_eq!(
574            iter.drain_to_slice(&mut got).unwrap_err(),
575            InvalidCharError { invalid: b'g', pos: 4 }
576        );
577    }
578
579    #[test]
580    fn hex_to_bytes_slice_drain_end_char_error() {
581        let hex = "deadbeeg";
582        let iter = HexToBytesIter::new_unchecked(hex);
583        let mut got = [0u8; 4];
584        assert_eq!(
585            iter.drain_to_slice(&mut got).unwrap_err(),
586            InvalidCharError { invalid: b'g', pos: 7 }
587        );
588    }
589
590    #[cfg(feature = "alloc")]
591    #[test]
592    fn hex_to_bytes_vec_drain() {
593        let hex = "deadbeef";
594        let want = [0xde, 0xad, 0xbe, 0xef];
595        let iter = HexToBytesIter::new_unchecked(hex);
596        let got = iter.drain_to_vec().unwrap();
597        assert_eq!(got, want);
598
599        let hex = "";
600        let iter = HexToBytesIter::new_unchecked(hex);
601        let got = iter.drain_to_vec().unwrap();
602        assert!(got.is_empty());
603    }
604
605    #[cfg(feature = "alloc")]
606    #[test]
607    fn hex_to_bytes_vec_drain_first_char_error() {
608        let hex = "geadbeef";
609        let iter = HexToBytesIter::new_unchecked(hex);
610        assert_eq!(iter.drain_to_vec().unwrap_err(), InvalidCharError { invalid: b'g', pos: 0 });
611    }
612
613    #[cfg(feature = "alloc")]
614    #[test]
615    fn hex_to_bytes_vec_drain_middle_char_error() {
616        let hex = "deadgeef";
617        let iter = HexToBytesIter::new_unchecked(hex);
618        assert_eq!(iter.drain_to_vec().unwrap_err(), InvalidCharError { invalid: b'g', pos: 4 });
619    }
620
621    #[cfg(feature = "alloc")]
622    #[test]
623    fn hex_to_bytes_vec_drain_end_char_error() {
624        let hex = "deadbeeg";
625        let iter = HexToBytesIter::new_unchecked(hex);
626        assert_eq!(iter.drain_to_vec().unwrap_err(), InvalidCharError { invalid: b'g', pos: 7 });
627    }
628
629    #[test]
630    fn decode_error_pos_after_next_back() {
631        let mut iter = HexToBytesIter::new("geadbeef").unwrap();
632        iter.next_back().unwrap().unwrap();
633        assert_eq!(iter.next().unwrap().unwrap_err(), InvalidCharError { invalid: b'g', pos: 0 },);
634    }
635
636    #[test]
637    fn decode_error_pos_after_next() {
638        let mut iter = HexToBytesIter::new("deadbeGf").unwrap();
639        iter.next().unwrap().unwrap();
640        assert_eq!(
641            iter.next_back().unwrap().unwrap_err(),
642            InvalidCharError { invalid: b'G', pos: 6 },
643        );
644    }
645
646    #[cfg(feature = "alloc")]
647    #[test]
648    fn encode_iter() {
649        let bytes = [0xde, 0xad, 0xbe, 0xef];
650        let lower_want = "deadbeef";
651        let upper_want = "DEADBEEF";
652
653        let lower_got: String =
654            BytesToHexIter::new(bytes.iter(), Case::Lower).flatten().map(char::from).collect();
655        assert_eq!(lower_got, lower_want);
656        let upper_got: String =
657            BytesToHexIter::new(bytes.iter(), Case::Upper).flatten().map(char::from).collect();
658        assert_eq!(upper_got, upper_want);
659    }
660
661    #[cfg(feature = "alloc")]
662    #[test]
663    fn encode_iter_backwards() {
664        let bytes = [0xde, 0xad, 0xbe, 0xef];
665        // .rev().flatten() yields pairs in reverse byte order but each pair remains [high, low].
666        let lower_want = "efbeadde";
667        let upper_want = "EFBEADDE";
668
669        let lower_got: String = BytesToHexIter::new(bytes.iter(), Case::Lower)
670            .rev()
671            .flatten()
672            .map(char::from)
673            .collect();
674        assert_eq!(lower_got, lower_want);
675        let upper_got: String = BytesToHexIter::new(bytes.iter(), Case::Upper)
676            .rev()
677            .flatten()
678            .map(char::from)
679            .collect();
680        assert_eq!(upper_got, upper_want);
681
682        // .flatten().rev() yields pairs in reverse byte order and each pair becomes [low, high].
683        let lower_want = "feebdaed";
684        let upper_want = "FEEBDAED";
685
686        let lower_got: String = BytesToHexIter::new(bytes.iter(), Case::Lower)
687            .flatten()
688            .rev()
689            .map(char::from)
690            .collect();
691        assert_eq!(lower_got, lower_want);
692        let upper_got: String = BytesToHexIter::new(bytes.iter(), Case::Upper)
693            .flatten()
694            .rev()
695            .map(char::from)
696            .collect();
697        assert_eq!(upper_got, upper_want);
698    }
699
700    #[test]
701    fn encode_iter_nth() {
702        let bytes = [0xde, 0xad, 0xbe, 0xef];
703
704        for n in 0..=bytes.len() + 1 {
705            let mut got = BytesToHexIter::new(bytes.iter(), Case::Lower);
706            let mut want = BytesToHexIter::new(bytes.iter(), Case::Lower);
707
708            assert_eq!(got.nth(n), nth_slow(&mut want, n));
709            assert_eq!(got.len(), want.len());
710            assert!(got.eq(want));
711        }
712    }
713
714    #[test]
715    fn encode_iter_nth_after_next_back() {
716        let bytes = [0xde, 0xad, 0xbe, 0xef];
717
718        for n in 0..=bytes.len() {
719            let mut got = BytesToHexIter::new(bytes.iter(), Case::Lower);
720            let mut want = BytesToHexIter::new(bytes.iter(), Case::Lower);
721
722            assert_eq!(got.next_back(), want.next_back());
723            assert_eq!(got.nth(n), nth_slow(&mut want, n));
724            assert_eq!(got.len(), want.len());
725            assert!(got.eq(want));
726        }
727    }
728
729    #[test]
730    fn encode_iter_nth_after_next() {
731        let bytes = [0xde, 0xad, 0xbe, 0xef];
732
733        for n in 0..=bytes.len() {
734            let mut got = BytesToHexIter::new(bytes.iter(), Case::Lower);
735            let mut want = BytesToHexIter::new(bytes.iter(), Case::Lower);
736
737            assert_eq!(got.next(), want.next());
738            assert_eq!(got.nth(n), nth_slow(&mut want, n));
739            assert_eq!(got.len(), want.len());
740            assert!(got.eq(want));
741        }
742    }
743
744    #[test]
745    fn encode_iter_nth_back() {
746        let bytes = [0xde, 0xad, 0xbe, 0xef];
747
748        for n in 0..=bytes.len() + 1 {
749            let mut got = BytesToHexIter::new(bytes.iter(), Case::Lower);
750            let mut want = BytesToHexIter::new(bytes.iter(), Case::Lower);
751
752            assert_eq!(got.nth_back(n), nth_back_slow(&mut want, n));
753            assert_eq!(got.len(), want.len());
754            assert!(got.eq(want));
755        }
756    }
757
758    #[test]
759    fn encode_iter_nth_back_after_next() {
760        let bytes = [0xde, 0xad, 0xbe, 0xef];
761
762        for n in 0..=bytes.len() {
763            let mut got = BytesToHexIter::new(bytes.iter(), Case::Lower);
764            let mut want = BytesToHexIter::new(bytes.iter(), Case::Lower);
765
766            assert_eq!(got.next(), want.next());
767            assert_eq!(got.nth_back(n), nth_back_slow(&mut want, n));
768            assert_eq!(got.len(), want.len());
769            assert!(got.eq(want));
770        }
771    }
772
773    #[test]
774    fn encode_iter_nth_back_after_next_back() {
775        let bytes = [0xde, 0xad, 0xbe, 0xef];
776
777        for n in 0..=bytes.len() {
778            let mut got = BytesToHexIter::new(bytes.iter(), Case::Lower);
779            let mut want = BytesToHexIter::new(bytes.iter(), Case::Lower);
780
781            assert_eq!(got.next_back(), want.next_back());
782            assert_eq!(got.nth_back(n), nth_back_slow(&mut want, n));
783            assert_eq!(got.len(), want.len());
784            assert!(got.eq(want));
785        }
786    }
787
788    #[cfg(feature = "alloc")]
789    #[test]
790    fn roundtrip_forward() {
791        let lower_want = "deadbeefcafebabe";
792        let upper_want = "DEADBEEFCAFEBABE";
793        let lower_bytes_iter = HexToBytesIter::new(lower_want).unwrap().map(|res| res.unwrap());
794        let lower_got: String =
795            BytesToHexIter::new(lower_bytes_iter, Case::Lower).flatten().map(char::from).collect();
796        assert_eq!(lower_got, lower_want);
797        let upper_bytes_iter = HexToBytesIter::new(upper_want).unwrap().map(|res| res.unwrap());
798        let upper_got: String =
799            BytesToHexIter::new(upper_bytes_iter, Case::Upper).flatten().map(char::from).collect();
800        assert_eq!(upper_got, upper_want);
801    }
802
803    #[cfg(feature = "alloc")]
804    #[test]
805    fn roundtrip_backward() {
806        let lower_want = "deadbeefcafebabe";
807        let upper_want = "DEADBEEFCAFEBABE";
808        let lower_bytes_iter =
809            HexToBytesIter::new(lower_want).unwrap().rev().map(|res| res.unwrap());
810        let lower_got: String = BytesToHexIter::new(lower_bytes_iter, Case::Lower)
811            .rev()
812            .flatten()
813            .map(char::from)
814            .collect();
815        assert_eq!(lower_got, lower_want);
816        let upper_bytes_iter =
817            HexToBytesIter::new(upper_want).unwrap().rev().map(|res| res.unwrap());
818        let upper_got: String = BytesToHexIter::new(upper_bytes_iter, Case::Upper)
819            .rev()
820            .flatten()
821            .map(char::from)
822            .collect();
823        assert_eq!(upper_got, upper_want);
824    }
825
826    #[test]
827    #[cfg(feature = "std")]
828    fn hex_to_bytes_iter_read() {
829        use std::io::Read;
830
831        let hex = "deadbeef";
832        let mut iter = HexToBytesIter::new(hex).unwrap();
833        let mut buf = [0u8; 4];
834        let bytes_read = iter.read(&mut buf).unwrap();
835        assert_eq!(bytes_read, 4);
836        assert_eq!(buf, [0xde, 0xad, 0xbe, 0xef]);
837
838        let hex = "deadbeef";
839        let mut iter = HexToBytesIter::new(hex).unwrap();
840        let mut buf = [0u8; 2];
841        let bytes_read = iter.read(&mut buf).unwrap();
842        assert_eq!(bytes_read, 2);
843        assert_eq!(buf, [0xde, 0xad]);
844
845        let hex = "deadbeef";
846        let mut iter = HexToBytesIter::new(hex).unwrap();
847        let mut buf = [0u8; 6];
848        let bytes_read = iter.read(&mut buf).unwrap();
849        assert_eq!(bytes_read, 4);
850        assert_eq!(buf[..4], [0xde, 0xad, 0xbe, 0xef]);
851
852        let hex = "deadbeefXX";
853        let mut iter = HexToBytesIter::new(hex).unwrap();
854        let mut buf = [0u8; 6];
855        let err = iter.read(&mut buf).unwrap_err();
856        assert_eq!(err.kind(), io::ErrorKind::InvalidData);
857    }
858}