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