hex_conservative/
iter.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Iterator that converts hex to bytes.
4
5use core::convert::TryInto;
6use core::iter::FusedIterator;
7use core::str;
8#[cfg(feature = "std")]
9use std::io;
10
11#[cfg(all(feature = "core2", not(feature = "std")))]
12use core2::io;
13
14use crate::error::{InvalidCharError, OddLengthStringError};
15
16/// Convenience alias for `HexToBytesIter<HexDigitsIter<'a>>`.
17pub type HexSliceToBytesIter<'a> = HexToBytesIter<HexDigitsIter<'a>>;
18
19/// Iterator yielding bytes decoded from an iterator of pairs of hex digits.
20pub struct HexToBytesIter<T: Iterator<Item = [u8; 2]>> {
21    iter: T,
22}
23
24impl<'a> HexToBytesIter<HexDigitsIter<'a>> {
25    /// Constructs a new `HexToBytesIter` from a string slice.
26    ///
27    /// # Errors
28    ///
29    /// If the input string is of odd length.
30    #[inline]
31    pub fn new(s: &'a str) -> Result<Self, OddLengthStringError> {
32        if s.len() % 2 != 0 {
33            Err(OddLengthStringError { len: s.len() })
34        } else {
35            Ok(Self::new_unchecked(s))
36        }
37    }
38
39    pub(crate) fn new_unchecked(s: &'a str) -> Self {
40        Self::from_pairs(HexDigitsIter::new_unchecked(s.as_bytes()))
41    }
42}
43
44impl<T: Iterator<Item = [u8; 2]>> HexToBytesIter<T> {
45    /// Constructs a custom hex decoding iterator from another iterator.
46    #[inline]
47    pub fn from_pairs(iter: T) -> Self { Self { iter } }
48}
49
50impl<T: Iterator<Item = [u8; 2]>> Iterator for HexToBytesIter<T> {
51    type Item = Result<u8, InvalidCharError>;
52
53    #[inline]
54    fn next(&mut self) -> Option<Self::Item> {
55        let [hi, lo] = self.iter.next()?;
56        Some(hex_chars_to_byte(hi, lo))
57    }
58
59    #[inline]
60    fn size_hint(&self) -> (usize, Option<usize>) {
61        let (min, max) = self.iter.size_hint();
62        (min / 2, max.map(|x| x / 2))
63    }
64
65    #[inline]
66    fn nth(&mut self, n: usize) -> Option<Self::Item> {
67        let [hi, lo] = self.iter.nth(n)?;
68        Some(hex_chars_to_byte(hi, lo))
69    }
70}
71
72impl<T: Iterator<Item = [u8; 2]> + DoubleEndedIterator> DoubleEndedIterator for HexToBytesIter<T> {
73    #[inline]
74    fn next_back(&mut self) -> Option<Self::Item> {
75        let [hi, lo] = self.iter.next_back()?;
76        Some(hex_chars_to_byte(hi, lo))
77    }
78
79    #[inline]
80    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
81        let [hi, lo] = self.iter.nth_back(n)?;
82        Some(hex_chars_to_byte(hi, lo))
83    }
84}
85
86impl<T: Iterator<Item = [u8; 2]> + ExactSizeIterator> ExactSizeIterator for HexToBytesIter<T> {}
87
88impl<T: Iterator<Item = [u8; 2]> + FusedIterator> FusedIterator for HexToBytesIter<T> {}
89
90#[cfg(any(feature = "std", feature = "core2"))]
91impl<T: Iterator<Item = [u8; 2]> + FusedIterator> io::Read for HexToBytesIter<T> {
92    #[inline]
93    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
94        let mut bytes_read = 0usize;
95        for dst in buf {
96            match self.next() {
97                Some(Ok(src)) => {
98                    *dst = src;
99                    bytes_read += 1;
100                }
101                _ => break,
102            }
103        }
104        Ok(bytes_read)
105    }
106}
107
108/// An internal iterator returning hex digits from a string.
109///
110/// Generally you shouldn't need to refer to this or bother with it and just use
111/// [`HexToBytesIter::new`] consuming the returned value and use `HexSliceToBytesIter` if you need
112/// to refer to the iterator in your types.
113pub struct HexDigitsIter<'a> {
114    // Invariant: the length of the chunks is 2.
115    // Technically, this is `iter::Map` but we can't use it because fn is anonymous.
116    // We can swap this for actual `ArrayChunks` once it's stable.
117    iter: core::slice::ChunksExact<'a, u8>,
118}
119
120impl<'a> HexDigitsIter<'a> {
121    #[inline]
122    fn new_unchecked(digits: &'a [u8]) -> Self { Self { iter: digits.chunks_exact(2) } }
123}
124
125impl<'a> Iterator for HexDigitsIter<'a> {
126    type Item = [u8; 2];
127
128    #[inline]
129    fn next(&mut self) -> Option<Self::Item> {
130        self.iter.next().map(|digits| digits.try_into().expect("HexDigitsIter invariant"))
131    }
132
133    #[inline]
134    fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
135
136    #[inline]
137    fn nth(&mut self, n: usize) -> Option<Self::Item> {
138        self.iter.nth(n).map(|digits| digits.try_into().expect("HexDigitsIter invariant"))
139    }
140}
141
142impl<'a> DoubleEndedIterator for HexDigitsIter<'a> {
143    #[inline]
144    fn next_back(&mut self) -> Option<Self::Item> {
145        self.iter.next_back().map(|digits| digits.try_into().expect("HexDigitsIter invariant"))
146    }
147
148    #[inline]
149    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
150        self.iter.nth_back(n).map(|digits| digits.try_into().expect("HexDigitsIter invariant"))
151    }
152}
153
154impl<'a> ExactSizeIterator for HexDigitsIter<'a> {}
155
156impl<'a> core::iter::FusedIterator for HexDigitsIter<'a> {}
157
158/// `hi` and `lo` are bytes representing hex characters.
159fn hex_chars_to_byte(hi: u8, lo: u8) -> Result<u8, InvalidCharError> {
160    let hih = (hi as char).to_digit(16).ok_or(InvalidCharError { invalid: hi })?;
161    let loh = (lo as char).to_digit(16).ok_or(InvalidCharError { invalid: lo })?;
162
163    let ret = (hih << 4) + loh;
164    Ok(ret as u8)
165}
166
167/// Iterator over bytes which encodes the bytes and yields hex characters.
168pub struct BytesToHexIter<I: Iterator<Item = u8>> {
169    /// The iterator whose next byte will be encoded to yield hex characters.
170    iter: I,
171    /// The low character of the pair (high, low) of hex characters encoded per byte.
172    low: Option<char>,
173}
174
175impl<I> BytesToHexIter<I>
176where
177    I: Iterator<Item = u8>,
178{
179    /// Constructs a new `BytesToHexIter` from a byte iterator.
180    pub fn new(iter: I) -> BytesToHexIter<I> { Self { iter, low: None } }
181}
182
183impl<I> Iterator for BytesToHexIter<I>
184where
185    I: Iterator<Item = u8>,
186{
187    type Item = char;
188
189    #[inline]
190    fn next(&mut self) -> Option<char> {
191        match self.low {
192            Some(c) => {
193                self.low = None;
194                Some(c)
195            }
196            None => self.iter.next().map(|b| {
197                let (high, low) = byte_to_hex_chars(b);
198                self.low = Some(low);
199                high
200            }),
201        }
202    }
203
204    #[inline]
205    fn size_hint(&self) -> (usize, Option<usize>) {
206        let (min, max) = self.iter.size_hint();
207        match self.low {
208            Some(_) => (min * 2 + 1, max.map(|max| max * 2 + 1)),
209            None => (min * 2, max.map(|max| max * 2)),
210        }
211    }
212}
213
214impl<I> DoubleEndedIterator for BytesToHexIter<I>
215where
216    I: DoubleEndedIterator + Iterator<Item = u8>,
217{
218    #[inline]
219    fn next_back(&mut self) -> Option<char> {
220        match self.low {
221            Some(c) => {
222                self.low = None;
223                Some(c)
224            }
225            None => self.iter.next_back().map(|b| {
226                let (high, low) = byte_to_hex_chars(b);
227                self.low = Some(low);
228                high
229            }),
230        }
231    }
232}
233
234impl<I> ExactSizeIterator for BytesToHexIter<I>
235where
236    I: ExactSizeIterator + Iterator<Item = u8>,
237{
238    #[inline]
239    fn len(&self) -> usize { self.iter.len() * 2 }
240}
241
242impl<I> FusedIterator for BytesToHexIter<I> where I: FusedIterator + Iterator<Item = u8> {}
243
244/// Returns the (high, low) hex characters encoding `b`.
245fn byte_to_hex_chars(b: u8) -> (char, char) {
246    const HEX_TABLE: [u8; 16] = *b"0123456789abcdef";
247
248    let high = HEX_TABLE[usize::from(b >> 4)];
249    let low = HEX_TABLE[usize::from(b & 0b00001111)];
250
251    (char::from(high), char::from(low))
252}
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257
258    #[test]
259    fn encode_byte() {
260        let tcs =
261            vec![(0x00, ('0', '0')), (0x0a, ('0', 'a')), (0xad, ('a', 'd')), (0xff, ('f', 'f'))];
262        for (b, (high, low)) in tcs {
263            assert_eq!(byte_to_hex_chars(b), (high, low));
264        }
265        assert_eq!(byte_to_hex_chars(0x00), ('0', '0'));
266        assert_eq!(byte_to_hex_chars(0x0a), ('0', 'a'));
267        assert_eq!(byte_to_hex_chars(0xad), ('a', 'd'));
268        assert_eq!(byte_to_hex_chars(0xff), ('f', 'f'));
269    }
270
271    #[test]
272    fn decode_iter_forward() {
273        let hex = "deadbeef";
274        let bytes = [0xde, 0xad, 0xbe, 0xef];
275
276        for (i, b) in HexToBytesIter::new(hex).unwrap().enumerate() {
277            assert_eq!(b.unwrap(), bytes[i]);
278        }
279    }
280
281    #[test]
282    fn decode_iter_backward() {
283        let hex = "deadbeef";
284        let bytes = [0xef, 0xbe, 0xad, 0xde];
285
286        for (i, b) in HexToBytesIter::new(hex).unwrap().rev().enumerate() {
287            assert_eq!(b.unwrap(), bytes[i]);
288        }
289    }
290
291    #[test]
292    fn encode_iter() {
293        let bytes = [0xde, 0xad, 0xbe, 0xef];
294        let hex = "deadbeef";
295
296        for (i, c) in BytesToHexIter::new(bytes.iter().cloned()).enumerate() {
297            assert_eq!(c, hex.chars().nth(i).unwrap());
298        }
299    }
300
301    #[test]
302    fn encode_iter_backwards() {
303        let bytes = [0xde, 0xad, 0xbe, 0xef];
304        let hex = "efbeadde";
305
306        for (i, c) in BytesToHexIter::new(bytes.iter().cloned()).rev().enumerate() {
307            assert_eq!(c, hex.chars().nth(i).unwrap());
308        }
309    }
310
311    #[test]
312    fn roundtrip_forward() {
313        let hex = "deadbeefcafebabe";
314        let bytes_iter = HexToBytesIter::new(hex).unwrap().map(|res| res.unwrap());
315        let got = BytesToHexIter::new(bytes_iter).collect::<String>();
316        assert_eq!(got, hex);
317    }
318
319    #[test]
320    fn roundtrip_backward() {
321        let hex = "deadbeefcafebabe";
322        let bytes_iter = HexToBytesIter::new(hex).unwrap().rev().map(|res| res.unwrap());
323        let got = BytesToHexIter::new(bytes_iter).rev().collect::<String>();
324        assert_eq!(got, hex);
325    }
326}