@@ -56,7 +56,31 @@ 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+ /// Similar to `fes_to_bytes`, but in this variant trailing bits are kept.
70+ ///
71+ /// If the last bits do not fill up the last output byte completely, they
72+ /// are not dropped, but kept, and the last byte is padded with zero bits.
73+ /// In effect this is the same as if the input was padded with zero values
74+ /// (1 or 2), such that there are enough zero bits to fill the last byte.
75+ /// The output is either the same as of `fes_to_bytes`, or has 1 extra byte.
76+ #[ inline]
77+ fn fes_to_bytes_zeropad ( mut self ) -> FesToBytes < Self > {
78+ FesToBytes {
79+ last_fe : self . next ( ) ,
80+ bit_offset : 0 ,
81+ iter : self ,
82+ output_incomplete_bits_zeropad : true ,
83+ }
6084 }
6185
6286 /// Adapts the Fe32 iterator to encode the field elements into a bech32 address.
@@ -148,7 +172,8 @@ where
148172
149173/// Iterator adaptor that converts GF32 elements to bytes.
150174///
151- /// If the total number of bits is not a multiple of 8, any trailing bits are dropped.
175+ /// If the total number of bits is not a multiple of 8. Any trailing bits are dropped,
176+ /// unless `output_incomplete_bits_zeropad` is set, in which case they are padded with zeroes.
152177///
153178/// Note that if there are 5 or more trailing bits, the result will be that an entire field element
154179/// is dropped. If this occurs, the input was an invalid length for a bech32 string, but this
@@ -158,6 +183,7 @@ pub struct FesToBytes<I: Iterator<Item = Fe32>> {
158183 last_fe : Option < Fe32 > ,
159184 bit_offset : usize ,
160185 iter : I ,
186+ output_incomplete_bits_zeropad : bool ,
161187}
162188
163189impl < I > Iterator for FesToBytes < I >
@@ -177,10 +203,18 @@ where
177203 let mut ret = last. 0 << ( 3 + bit_offset) ;
178204
179205 self . last_fe = self . iter . next ( ) ;
180- let next1 = self . last_fe ?;
206+ let next1 = if !self . output_incomplete_bits_zeropad {
207+ self . last_fe ?
208+ } else {
209+ self . last_fe . unwrap_or_default ( )
210+ } ;
181211 if bit_offset > 2 {
182212 self . last_fe = self . iter . next ( ) ;
183- let next2 = self . last_fe ?;
213+ let next2 = if !self . output_incomplete_bits_zeropad {
214+ self . last_fe ?
215+ } else {
216+ self . last_fe . unwrap_or_default ( )
217+ } ;
184218 ret |= next1. 0 << ( bit_offset - 2 ) ;
185219 ret |= next2. 0 >> ( 7 - bit_offset) ;
186220 } else {
@@ -503,4 +537,32 @@ mod tests {
503537 const FES : [ Fe32 ; 3 ] = [ Fe32 :: Q , Fe32 :: P , Fe32 :: Q ] ;
504538 assert ! ( FES . iter( ) . copied( ) . fes_to_bytes( ) . bytes_to_fes( ) . eq( FES . iter( ) . copied( ) ) )
505539 }
540+
541+ #[ test]
542+ fn fe32_iter_ext_zeropad_and_nozeropad ( ) {
543+ use std:: convert:: TryFrom ;
544+ {
545+ // Difference is 1 output byte, containing 4 trailing bits
546+ let fes_iter = [ 0 , 1 , 2 , 1 ] . iter ( ) . copied ( ) . map ( |b| Fe32 :: try_from ( b) . unwrap ( ) ) ;
547+ assert_eq ! ( fes_iter. clone( ) . fes_to_bytes_zeropad( ) . collect:: <Vec <_>>( ) , [ 0 , 68 , 16 ] ) ;
548+ assert_eq ! ( fes_iter. clone( ) . fes_to_bytes( ) . collect:: <Vec <_>>( ) , [ 0 , 68 ] ) ;
549+ }
550+ {
551+ // Difference is 1 output byte, containing 1 trailing bit
552+ let fes_iter = [ 0 , 1 , 2 , 3 , 31 ] . iter ( ) . copied ( ) . map ( |b| Fe32 :: try_from ( b) . unwrap ( ) ) ;
553+ assert_eq ! (
554+ fes_iter. clone( ) . fes_to_bytes_zeropad( ) . collect:: <Vec <_>>( ) ,
555+ [ 0 , 68 , 63 , 128 ]
556+ ) ;
557+ assert_eq ! ( fes_iter. clone( ) . fes_to_bytes( ) . collect:: <Vec <_>>( ) , [ 0 , 68 , 63 ] ) ;
558+ }
559+ {
560+ // No difference here, as the input (32*5=160 bits) has no trailing bits
561+ let fes_iter = "w508d6qejxtdg4y5r3zarvary0c5xw7k"
562+ . bytes ( )
563+ . map ( |b| Fe32 :: from_char ( char:: from ( b) ) . unwrap ( ) ) ;
564+ assert_eq ! ( fes_iter. clone( ) . fes_to_bytes_zeropad( ) . collect:: <Vec <_>>( ) , DATA ) ;
565+ assert_eq ! ( fes_iter. clone( ) . fes_to_bytes( ) . collect:: <Vec <_>>( ) , DATA ) ;
566+ }
567+ }
506568}
0 commit comments