bech32/primitives/
iter.rs

1// SPDX-License-Identifier: MIT
2
3//! Iterator Adaptors.
4//!
5//! Iterator extension traits and blanket implementations to convert:
6//!
7//! - `BytesToFes`: An iterator over bytes to an iterator over field elements.
8//! - `FesToBytes`: An iterator over field elements to an iterator over bytes.
9//! - `Checksummed`: An iterator over field elements that appends the checksum.
10//!
11//! WARNING: This module does not enforce the maximum length of an encoded bech32 string (90 chars).
12//!
13//! # Examples
14//!
15//! ```
16//! use bech32::{Bech32, ByteIterExt, Fe32IterExt, Fe32, Hrp};
17//!
18//! let data = [
19//!     0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4,
20//!     0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23,
21//!     0xf1, 0x43, 0x3b, 0xd6,
22//! ];
23//!
24//! // Convert byte data to GF32 field elements.
25//! let fe_iter = data.iter().copied().bytes_to_fes();
26//!
27//! // Convert field elements back to bytes.
28//! let byte_iter = fe_iter.fes_to_bytes();
29//!
30//! # assert!(data.iter().copied().eq(byte_iter));
31//! ```
32
33use crate::primitives::checksum::{self, Checksum, PackedFe32};
34use crate::primitives::encode::Encoder;
35use crate::primitives::gf32::Fe32;
36use crate::primitives::hrp::Hrp;
37
38/// Extension trait for byte iterators which provides an adaptor to GF32 elements.
39pub trait ByteIterExt: Sized + Iterator<Item = u8> {
40    /// Adapts the byte iterator to output GF32 field elements instead.
41    ///
42    /// If the total number of bits is not a multiple of 5 we pad with 0s
43    #[inline]
44    fn bytes_to_fes(mut self) -> BytesToFes<Self> {
45        BytesToFes { last_byte: self.next(), bit_offset: 0, iter: self }
46    }
47}
48
49impl<I> ByteIterExt for I where I: Iterator<Item = u8> {}
50
51/// Extension trait for field element iterators.
52pub trait Fe32IterExt: Sized + Iterator<Item = Fe32> {
53    /// Adapts the `Fe32` iterator to output bytes instead.
54    ///
55    /// If the total number of bits is not a multiple of 8, any trailing bits
56    /// are simply dropped.
57    #[inline]
58    fn fes_to_bytes(mut self) -> FesToBytes<Self> {
59        FesToBytes { last_fe: self.next(), bit_offset: 0, iter: self }
60    }
61
62    /// Adapts the Fe32 iterator to encode the field elements into a bech32 address.
63    #[inline]
64    fn with_checksum<Ck: Checksum>(self, hrp: &Hrp) -> Encoder<'_, Self, Ck> {
65        Encoder::new(self, hrp)
66    }
67}
68
69impl<I> Fe32IterExt for I where I: Iterator<Item = Fe32> {}
70
71/// Iterator adaptor that converts bytes to GF32 elements.
72///
73/// If the total number of bits is not a multiple of 5, it right-pads with 0 bits.
74#[derive(Clone, PartialEq, Eq)]
75pub struct BytesToFes<I: Iterator<Item = u8>> {
76    last_byte: Option<u8>,
77    bit_offset: usize,
78    iter: I,
79}
80
81impl<I> Iterator for BytesToFes<I>
82where
83    I: Iterator<Item = u8>,
84{
85    type Item = Fe32;
86
87    #[inline]
88    fn next(&mut self) -> Option<Fe32> {
89        use core::cmp::Ordering::*;
90
91        let bit_offset = {
92            let ret = self.bit_offset;
93            self.bit_offset = (self.bit_offset + 5) % 8;
94            ret
95        };
96
97        if let Some(last) = self.last_byte {
98            match bit_offset.cmp(&3) {
99                Less => Some(Fe32((last >> (3 - bit_offset)) & 0x1f)),
100                Equal => {
101                    self.last_byte = self.iter.next();
102                    Some(Fe32(last & 0x1f))
103                }
104                Greater => {
105                    self.last_byte = self.iter.next();
106                    let next = self.last_byte.unwrap_or(0);
107                    Some(Fe32(((last << (bit_offset - 3)) | (next >> (11 - bit_offset))) & 0x1f))
108                }
109            }
110        } else {
111            None
112        }
113    }
114
115    #[inline]
116    fn size_hint(&self) -> (usize, Option<usize>) {
117        let (min, max) = self.iter.size_hint();
118        let (min, max) = match self.last_byte {
119            // +1 because we set last_byte with call to `next`.
120            Some(_) => (min + 1, max.map(|max| max + 1)),
121            None => (min, max),
122        };
123
124        let min = bytes_len_to_fes_len(min);
125        let max = max.map(bytes_len_to_fes_len);
126
127        (min, max)
128    }
129}
130
131/// The number of fes encoded by n bytes, rounded up because we pad the fes.
132fn bytes_len_to_fes_len(bytes: usize) -> usize {
133    let bits = bytes * 8;
134    (bits + 4) / 5
135}
136
137impl<I> ExactSizeIterator for BytesToFes<I>
138where
139    I: Iterator<Item = u8> + ExactSizeIterator,
140{
141    #[inline]
142    fn len(&self) -> usize {
143        let len = match self.last_byte {
144            Some(_) => self.iter.len() + 1,
145            None => self.iter.len(),
146        };
147        bytes_len_to_fes_len(len)
148    }
149}
150
151/// Iterator adaptor that converts GF32 elements to bytes.
152///
153/// If the total number of bits is not a multiple of 8, any trailing bits are dropped.
154///
155/// Note that if there are 5 or more trailing bits, the result will be that an entire field element
156/// is dropped. If this occurs, the input was an invalid length for a bech32 string, but this
157/// iterator does not do any checks for this.
158#[derive(Clone, PartialEq, Eq)]
159pub struct FesToBytes<I: Iterator<Item = Fe32>> {
160    last_fe: Option<Fe32>,
161    bit_offset: usize,
162    iter: I,
163}
164
165impl<I> Iterator for FesToBytes<I>
166where
167    I: Iterator<Item = Fe32>,
168{
169    type Item = u8;
170
171    fn next(&mut self) -> Option<u8> {
172        let bit_offset = {
173            let ret = self.bit_offset;
174            self.bit_offset = (self.bit_offset + 8) % 5;
175            ret
176        };
177
178        if let Some(last) = self.last_fe {
179            let mut ret = last.0 << (3 + bit_offset);
180
181            self.last_fe = self.iter.next();
182            let next1 = self.last_fe?;
183            if bit_offset > 2 {
184                self.last_fe = self.iter.next();
185                let next2 = self.last_fe?;
186                ret |= next1.0 << (bit_offset - 2);
187                ret |= next2.0 >> (7 - bit_offset);
188            } else {
189                ret |= next1.0 >> (2 - bit_offset);
190                if self.bit_offset == 0 {
191                    self.last_fe = self.iter.next();
192                }
193            }
194
195            Some(ret)
196        } else {
197            None
198        }
199    }
200
201    #[inline]
202    fn size_hint(&self) -> (usize, Option<usize>) {
203        // If the total number of bits is not a multiple of 8, any trailing bits are dropped.
204        let fes_len_to_bytes_len = |n| n * 5 / 8;
205
206        let (fes_min, fes_max) = self.iter.size_hint();
207        // +1 because we set last_fe with call to `next`.
208        let min = fes_len_to_bytes_len(fes_min + 1);
209        let max = fes_max.map(|max| fes_len_to_bytes_len(max + 1));
210        (min, max)
211    }
212}
213
214// If the total number of bits is not a multiple of 8, any trailing bits are dropped.
215fn fes_len_to_bytes_len(n: usize) -> usize { n * 5 / 8 }
216
217impl<I> ExactSizeIterator for FesToBytes<I>
218where
219    I: Iterator<Item = Fe32> + ExactSizeIterator,
220{
221    #[inline]
222    fn len(&self) -> usize {
223        let len = match self.last_fe {
224            Some(_) => self.iter.len() + 1,
225            None => self.iter.len(),
226        };
227        fes_len_to_bytes_len(len)
228    }
229}
230
231/// Iterator adaptor for field-element-yielding iterator, which tacks a checksum onto the end of the
232/// yielded data.
233#[derive(Clone, PartialEq, Eq)]
234pub struct Checksummed<I, Ck>
235where
236    I: Iterator<Item = Fe32>,
237    Ck: Checksum,
238{
239    iter: I,
240    checksum_remaining: usize,
241    checksum_engine: checksum::Engine<Ck>,
242}
243
244impl<I, Ck> Checksummed<I, Ck>
245where
246    I: Iterator<Item = Fe32>,
247    Ck: Checksum,
248{
249    /// Creates a new checksummed iterator which adapts a data iterator of field elements by
250    /// appending a checksum.
251    #[inline]
252    pub fn new(data: I) -> Checksummed<I, Ck> {
253        Checksummed {
254            iter: data,
255            checksum_remaining: Ck::CHECKSUM_LENGTH,
256            checksum_engine: checksum::Engine::new(),
257        }
258    }
259
260    /// Creates a new checksummed iterator which adapts a data iterator of field elements by
261    /// first inputting the [`Hrp`] and then appending a checksum.
262    #[inline]
263    pub fn new_hrp(hrp: Hrp, data: I) -> Checksummed<I, Ck> {
264        let mut ret = Self::new(data);
265        ret.checksum_engine.input_hrp(hrp);
266        ret
267    }
268}
269
270impl<I, Ck> Iterator for Checksummed<I, Ck>
271where
272    I: Iterator<Item = Fe32>,
273    Ck: Checksum,
274{
275    type Item = Fe32;
276
277    #[inline]
278    fn next(&mut self) -> Option<Fe32> {
279        match self.iter.next() {
280            Some(fe) => {
281                self.checksum_engine.input_fe(fe);
282                Some(fe)
283            }
284            None =>
285                if self.checksum_remaining == 0 {
286                    None
287                } else {
288                    if self.checksum_remaining == Ck::CHECKSUM_LENGTH {
289                        self.checksum_engine.input_target_residue();
290                    }
291                    self.checksum_remaining -= 1;
292                    Some(Fe32(self.checksum_engine.residue().unpack(self.checksum_remaining)))
293                },
294        }
295    }
296
297    #[inline]
298    fn size_hint(&self) -> (usize, Option<usize>) {
299        let add = self.checksum_remaining;
300        let (min, max) = self.iter.size_hint();
301
302        (min + add, max.map(|max| max + add))
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309
310    // Tests below using this data, are based on the test vector (from BIP-173):
311    // BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4: 0014751e76e8199196d454941c45d1b3a323f1433bd6
312    #[rustfmt::skip]
313    const DATA: [u8; 20] = [
314        0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4,
315        0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23,
316        0xf1, 0x43, 0x3b, 0xd6,
317    ];
318
319    #[test]
320    fn byte_iter_ext() {
321        assert!(DATA
322            .iter()
323            .copied()
324            .bytes_to_fes()
325            .map(Fe32::to_char)
326            .eq("w508d6qejxtdg4y5r3zarvary0c5xw7k".chars()));
327    }
328
329    #[test]
330    fn bytes_to_fes_size_hint() {
331        let char_len = "w508d6qejxtdg4y5r3zarvary0c5xw7k".len();
332        assert_eq!(DATA.iter().copied().bytes_to_fes().size_hint(), (char_len, Some(char_len)));
333    }
334
335    #[test]
336    fn fe32_iter_ext() {
337        let fe_iter = "w508d6qejxtdg4y5r3zarvary0c5xw7k"
338            .bytes()
339            .map(|b| Fe32::from_char(char::from(b)).unwrap());
340
341        assert!(fe_iter.clone().fes_to_bytes().eq(DATA.iter().copied()));
342    }
343
344    #[test]
345    fn fes_to_bytes_size_hint() {
346        let fe_iter = "w508d6qejxtdg4y5r3zarvary0c5xw7k"
347            .bytes()
348            .map(|b| Fe32::from_char(char::from(b)).unwrap());
349
350        let got_hint = fe_iter.clone().fes_to_bytes().size_hint();
351        let want_hint = DATA.iter().size_hint();
352
353        assert_eq!(got_hint, want_hint)
354    }
355
356    #[test]
357    fn padding_bytes_trailing_0_bits_roundtrips() {
358        // 5 * 8 % 5 = 0
359        const BYTES: [u8; 5] = [0x75, 0x1e, 0x76, 0xe8, 0x19];
360        assert!(BYTES.iter().copied().bytes_to_fes().fes_to_bytes().eq(BYTES.iter().copied()))
361    }
362
363    #[test]
364    fn padding_bytes_trailing_1_bit_roundtrips() {
365        // 2 * 8 % 5 = 1
366        const BYTES: [u8; 2] = [0x75, 0x1e];
367        assert!(BYTES.iter().copied().bytes_to_fes().fes_to_bytes().eq(BYTES.iter().copied()))
368    }
369
370    #[test]
371    fn padding_bytes_trailing_2_bits_roundtrips() {
372        // 4 * 8 % 5 = 2
373        const BYTES: [u8; 4] = [0x75, 0x1e, 0x76, 0xe8];
374        assert!(BYTES.iter().copied().bytes_to_fes().fes_to_bytes().eq(BYTES.iter().copied()))
375    }
376
377    #[test]
378    fn padding_bytes_trailing_3_bits_roundtrips() {
379        // 6 * 8 % 5 = 3
380        const BYTES: [u8; 6] = [0x75, 0x1e, 0x76, 0xe8, 0x19, 0xab];
381        assert!(BYTES.iter().copied().bytes_to_fes().fes_to_bytes().eq(BYTES.iter().copied()))
382    }
383
384    #[test]
385    fn padding_bytes_trailing_4_bits_roundtrips() {
386        // 3 * 8 % 5 = 4
387        const BYTES: [u8; 3] = [0x75, 0x1e, 0x76];
388        assert!(BYTES.iter().copied().bytes_to_fes().fes_to_bytes().eq(BYTES.iter().copied()))
389    }
390
391    #[test]
392    fn padding_fes_trailing_0_bits_roundtrips() {
393        // 8 * 5 % 8 = 0
394        const FES: [Fe32; 8] =
395            [Fe32::Q, Fe32::P, Fe32::Z, Fe32::R, Fe32::Y, Fe32::X, Fe32::G, Fe32::F];
396        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
397    }
398
399    #[test]
400    fn padding_fes_trailing_1_bit_zero_roundtrips() {
401        // 5 * 5 % 8 = 1
402        const FES: [Fe32; 5] = [Fe32::Q, Fe32::P, Fe32::Z, Fe32::R, Fe32::Q];
403        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
404    }
405
406    #[test]
407    #[should_panic]
408    fn padding_fes_trailing_1_bit_non_zero_does_not_roundtrip() {
409        // 5 * 5 % 8 = 1
410        const FES: [Fe32; 5] = [Fe32::Q, Fe32::P, Fe32::Z, Fe32::R, Fe32::L];
411        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
412    }
413
414    #[test]
415    fn padding_fes_trailing_2_bits_zeros_roundtrips() {
416        // 2 * 5 % 8 = 2
417        const FES: [Fe32; 2] = [Fe32::P, Fe32::Q];
418        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
419    }
420
421    #[test]
422    #[should_panic]
423    fn padding_fes_trailing_2_bits_non_zero_does_not_roundtrip() {
424        // 2 * 5 % 8 = 2
425        const FES: [Fe32; 2] = [Fe32::Q, Fe32::P];
426        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
427    }
428
429    #[test]
430    fn padding_fes_trailing_3_bits_zeros_roundtrips() {
431        // 7 * 5 % 8 = 3
432        const FES: [Fe32; 7] = [Fe32::Q, Fe32::P, Fe32::Z, Fe32::R, Fe32::Y, Fe32::X, Fe32::Q];
433        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
434    }
435
436    #[test]
437    #[should_panic]
438    fn padding_fes_trailing_3_bits_non_zero_does_not_roundtrip() {
439        // 7 * 5 % 8 = 3
440        const FES: [Fe32; 7] = [Fe32::Q, Fe32::P, Fe32::Z, Fe32::R, Fe32::Y, Fe32::X, Fe32::P];
441        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
442    }
443
444    #[test]
445    fn padding_fes_trailing_4_bits_zeros_roundtrips() {
446        // 4 * 5 % 8 = 4
447        const FES: [Fe32; 4] = [Fe32::Q, Fe32::P, Fe32::Z, Fe32::Q];
448        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
449    }
450
451    #[test]
452    #[should_panic]
453    fn padding_fes_trailing_4_bits_non_zero_does_not_roundtrip() {
454        // 4 * 5 % 8 = 4
455        const FES: [Fe32; 4] = [Fe32::Q, Fe32::P, Fe32::Z, Fe32::P];
456        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
457    }
458
459    // Padding is never more than 4 bits so any additional bits will always fail to roundtrip.
460
461    #[test]
462    #[should_panic]
463    fn padding_fes_trailing_5_bits_zeros_does_not_roundtrip() {
464        // 1 * 5 % 8 = 5
465        const FES: [Fe32; 1] = [Fe32::Q];
466        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
467    }
468
469    #[test]
470    #[should_panic]
471    fn padding_fes_trailing_5_bits_non_zero_does_not_roundtrip() {
472        // 1 * 5 % 8 = 5
473        const FES: [Fe32; 1] = [Fe32::P];
474        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
475    }
476
477    #[test]
478    #[should_panic]
479    fn padding_fes_trailing_6_bits_zeros_does_not_roundtrip() {
480        // 6 * 5 % 8 = 6
481        const FES: [Fe32; 6] = [Fe32::Q, Fe32::P, Fe32::Z, Fe32::R, Fe32::Q, Fe32::Q];
482        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
483    }
484
485    #[test]
486    #[should_panic]
487    fn padding_fes_trailing_6_bits_non_zero_does_not_roundtrip() {
488        // 6 * 5 % 8 = 6
489        const FES: [Fe32; 6] = [Fe32::Q, Fe32::P, Fe32::Z, Fe32::R, Fe32::Y, Fe32::X];
490        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
491    }
492
493    #[test]
494    #[should_panic]
495    fn padding_fes_trailing_7_bits_zeros_does_not_roundtrip() {
496        // 3 * 5 % 8 = 7
497        const FES: [Fe32; 3] = [Fe32::P, Fe32::Q, Fe32::Q];
498        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
499    }
500
501    #[test]
502    #[should_panic]
503    fn padding_fes_trailing_7_bits_non_zero_does_not_roundtrip() {
504        // 3 * 5 % 8 = 7
505        const FES: [Fe32; 3] = [Fe32::Q, Fe32::P, Fe32::Q];
506        assert!(FES.iter().copied().fes_to_bytes().bytes_to_fes().eq(FES.iter().copied()))
507    }
508}