fibonacci_codec/
decode.rs1use num::{CheckedAdd, One, Zero};
2use std::fmt::Debug;
3
4#[derive(Fail, Debug, PartialEq)]
7pub enum DecodeError {
8 #[fail(
11 display = "fibonacci sequence element would overflow result type at bit position {:?}",
12 bit_pos
13 )]
14 FibonacciElementOverflow { bit_pos: usize },
15
16 #[fail(
20 display = "constructing number would overflow at bit position {:?}",
21 bit_pos
22 )]
23 ConstructionOverflow { bit_pos: usize },
24}
25
26fn multiplier<T>(bit: bool) -> T
27where
28 T: Zero + One,
29{
30 if bit {
31 T::one()
32 } else {
33 T::zero()
34 }
35}
36
37fn is_terminator(bit: bool, last: bool) -> bool {
38 bit && last
39}
40
41fn consume_overflow<I>(elt: bool, iterator: &mut I)
42where
43 I: Iterator<Item = bool>,
44{
45 let mut last = elt;
46 for elt in iterator {
47 if is_terminator(elt, last) {
48 break;
49 }
50 last = elt;
51 }
52}
53
54#[cfg_attr(feature = "cargo-clippy", allow(clippy::while_let_on_iterator))]
57#[inline]
58pub(crate) fn decode_from<I, T>(
59 iterator: &mut I,
60 table: &'static [T],
61) -> Option<Result<T, DecodeError>>
62where
63 I: Iterator<Item = bool>,
64 T: CheckedAdd + PartialOrd + Debug + Copy + Zero + One,
65{
66 let mut i = 0;
67 let mut accumulator: T = T::zero();
68 let mut last = false;
69 while let Some(elt) = iterator.next() {
70 if is_terminator(elt, last) {
71 return Some(Ok(accumulator));
72 }
73
74 if let Some(fib) = table.get(i) {
75 let digit = multiplier::<T>(elt) * *fib;
76 if let Some(new_acc) = accumulator.checked_add(&digit) {
77 accumulator = new_acc;
78 } else {
79 consume_overflow(elt, iterator);
80 return Some(Err(DecodeError::ConstructionOverflow { bit_pos: i }));
81 }
82 } else {
83 consume_overflow(elt, iterator);
84 return Some(Err(DecodeError::FibonacciElementOverflow { bit_pos: i }));
85 }
86 i += 1;
87 last = elt;
88 }
89 None
91}