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