@@ -56,7 +56,26 @@ pub trait Fe32IterExt: Sized + Iterator<Item = Fe32> {
5656 /// are simply dropped.
5757 #[ inline]
5858 fn fes_to_bytes ( mut self ) -> FesToBytes < Self > {
59- FesToBytes { last_fe : self . next ( ) , bit_offset : 0 , iter : self }
59+ FesToBytes {
60+ last_fe : self . next ( ) ,
61+ bit_offset : 0 ,
62+ iter : self ,
63+ output_incomplete_bits_zeropad : false ,
64+ }
65+ }
66+
67+ /// Adapts the `Fe32` iterator to output bytes instead.
68+ ///
69+ /// If the total number of bits is not a multiple of 8, trailing bits
70+ /// are padded with the needed amount of zeroes and converted.
71+ #[ inline]
72+ fn fes_to_bytes_zeropad ( mut self ) -> FesToBytes < Self > {
73+ FesToBytes {
74+ last_fe : self . next ( ) ,
75+ bit_offset : 0 ,
76+ iter : self ,
77+ output_incomplete_bits_zeropad : true ,
78+ }
6079 }
6180
6281 /// Adapts the Fe32 iterator to encode the field elements into a bech32 address.
@@ -148,7 +167,8 @@ where
148167
149168/// Iterator adaptor that converts GF32 elements to bytes.
150169///
151- /// If the total number of bits is not a multiple of 8, any trailing bits are dropped.
170+ /// If the total number of bits is not a multiple of 8. Any trailing bits are dropped,
171+ /// unless `output_incomplete_bits_zeropad` is set, in which case they are padded with zeroes.
152172///
153173/// Note that if there are 5 or more trailing bits, the result will be that an entire field element
154174/// is dropped. If this occurs, the input was an invalid length for a bech32 string, but this
@@ -158,6 +178,7 @@ pub struct FesToBytes<I: Iterator<Item = Fe32>> {
158178 last_fe : Option < Fe32 > ,
159179 bit_offset : usize ,
160180 iter : I ,
181+ output_incomplete_bits_zeropad : bool ,
161182}
162183
163184impl < I > Iterator for FesToBytes < I >
@@ -177,10 +198,18 @@ where
177198 let mut ret = last. 0 << ( 3 + bit_offset) ;
178199
179200 self . last_fe = self . iter . next ( ) ;
180- let next1 = self . last_fe ?;
201+ let next1 = if !self . output_incomplete_bits_zeropad {
202+ self . last_fe ?
203+ } else {
204+ self . last_fe . unwrap_or_default ( )
205+ } ;
181206 if bit_offset > 2 {
182207 self . last_fe = self . iter . next ( ) ;
183- let next2 = self . last_fe ?;
208+ let next2 = if !self . output_incomplete_bits_zeropad {
209+ self . last_fe ?
210+ } else {
211+ self . last_fe . unwrap_or_default ( )
212+ } ;
184213 ret |= next1. 0 << ( bit_offset - 2 ) ;
185214 ret |= next2. 0 >> ( 7 - bit_offset) ;
186215 } else {
@@ -503,4 +532,32 @@ mod tests {
503532 const FES : [ Fe32 ; 3 ] = [ Fe32 :: Q , Fe32 :: P , Fe32 :: Q ] ;
504533 assert ! ( FES . iter( ) . copied( ) . fes_to_bytes( ) . bytes_to_fes( ) . eq( FES . iter( ) . copied( ) ) )
505534 }
535+
536+ #[ test]
537+ fn fe32_iter_ext_zeropad_and_nozeropad ( ) {
538+ use std:: convert:: TryFrom ;
539+ {
540+ // Difference is 1 output byte, containing 4 trailing bits
541+ let fes_iter = [ 0 , 1 , 2 , 1 ] . iter ( ) . copied ( ) . map ( |b| Fe32 :: try_from ( b) . unwrap ( ) ) ;
542+ assert_eq ! ( fes_iter. clone( ) . fes_to_bytes_zeropad( ) . collect:: <Vec <_>>( ) , [ 0 , 68 , 16 ] ) ;
543+ assert_eq ! ( fes_iter. clone( ) . fes_to_bytes( ) . collect:: <Vec <_>>( ) , [ 0 , 68 ] ) ;
544+ }
545+ {
546+ // Difference is 1 output byte, containing 1 trailing bit
547+ let fes_iter = [ 0 , 1 , 2 , 3 , 31 ] . iter ( ) . copied ( ) . map ( |b| Fe32 :: try_from ( b) . unwrap ( ) ) ;
548+ assert_eq ! (
549+ fes_iter. clone( ) . fes_to_bytes_zeropad( ) . collect:: <Vec <_>>( ) ,
550+ [ 0 , 68 , 63 , 128 ]
551+ ) ;
552+ assert_eq ! ( fes_iter. clone( ) . fes_to_bytes( ) . collect:: <Vec <_>>( ) , [ 0 , 68 , 63 ] ) ;
553+ }
554+ {
555+ // No difference here, as the input (32*5=160 bits) has no trailing bits
556+ let fes_iter = "w508d6qejxtdg4y5r3zarvary0c5xw7k"
557+ . bytes ( )
558+ . map ( |b| Fe32 :: from_char ( char:: from ( b) ) . unwrap ( ) ) ;
559+ assert_eq ! ( fes_iter. clone( ) . fes_to_bytes_zeropad( ) . collect:: <Vec <_>>( ) , DATA ) ;
560+ assert_eq ! ( fes_iter. clone( ) . fes_to_bytes( ) . collect:: <Vec <_>>( ) , DATA ) ;
561+ }
562+ }
506563}
0 commit comments