Skip to content

Commit 7a6d9cc

Browse files
committed
Merge #201: Alternative fes_to_bytes_zeropad with no dropping of last bits
06762e0 Update doc (optout) 475d04b Alternative fes_to_bytes_zeropad with no dropping of last bits (optout) Pull request description: Fixes #198 , as discussed there ACKs for top commit: apoelstra: ACK 06762e0 successfully ran local tests clarkmoody: ACK 06762e0 Tree-SHA512: 3d207e3bdc721dbb7e365ec6c14a431bb2ac4b70aea3ad68b978bafc778b1471c73c628c839137316b3f23397cd39b46d85cf2483a98ab332fd52a837937cd5a
2 parents 8173170 + 06762e0 commit 7a6d9cc

File tree

1 file changed

+66
-4
lines changed

1 file changed

+66
-4
lines changed

src/primitives/iter.rs

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

163189
impl<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

Comments
 (0)