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