1use crate::primitives::checksum::{self, Checksum, PackedFe32};
34use crate::primitives::encode::Encoder;
35use crate::primitives::gf32::Fe32;
36use crate::primitives::hrp::Hrp;
37
38pub trait ByteIterExt: Sized + Iterator<Item = u8> {
40 #[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
51pub trait Fe32IterExt: Sized + Iterator<Item = Fe32> {
53 #[inline]
58 fn fes_to_bytes(mut self) -> FesToBytes<Self> {
59 FesToBytes { last_fe: self.next(), bit_offset: 0, iter: self }
60 }
61
62 #[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#[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 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
129fn 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#[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 let fes_len_to_bytes_len = |n| n * 5 / 8;
203
204 let (fes_min, fes_max) = self.iter.size_hint();
205 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
212fn 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#[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 #[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 #[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 #[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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 #[test]
460 #[should_panic]
461 fn padding_fes_trailing_5_bits_zeros_does_not_roundtrip() {
462 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 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 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 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 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 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}