1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
//! Common serialization utilities use core::{convert::TryFrom, fmt}; // The reason this is here is because I need the free functions in `awint_core` // for speeding up certain serialization tasks, but the free functions also need // `SerdeError` to make them more ergonomic in `awint_ext`. /// A serialization or deserialization error #[derive(Debug, Clone, Copy)] pub enum SerdeError { /// If an input bitwidth is zero ZeroBitwidth, /// If some kind of width does not match in contexts that require equal /// widths NonEqualWidths, /// A radix is not in the range `2..=36` InvalidRadix, /// An input is empty. This could be part of a string, e.x. calling /// `<awint_ext::ExtAwi as FromStr>::from_str` with no integral "i64", or no /// bitwidth "-100", instead of the full "-100i64". Empty, /// There is an unrecognized character that is not `_`, `-`, `0..=9`, /// `a..=z`, or `A..=Z` depending on the radix and other context InvalidChar, /// The value represented by the string cannot fit in the specified unsigned /// or signed integer. This may also be thrown in case of internal /// algorithms failing from extreme string lengths approaching memory /// exhaustion. Overflow, } impl fmt::Display for SerdeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) } } use SerdeError::*; /// Binary logarithms of the integers 2..=36 rounded up and in u16p13 fixed /// point format pub const LB_I3F13: [u16; 37] = [ 0, 0, 8192, 12985, 16384, 19022, 21177, 22998, 24576, 25969, 27214, 28340, 29369, 30315, 31190, 32006, 32768, 33485, 34161, 34800, 35406, 35982, 36532, 37058, 37561, 38043, 38507, 38953, 39382, 39797, 40198, 40585, 40960, 41324, 41677, 42020, 42353, ]; #[test] fn lb_u16p13() { use core::ops::Mul; for i in 2..=36 { assert_eq!( LB_I3F13[i], (i as f64).log2().mul((1 << 13) as f64).ceil() as u16 ) } } /// Reciprocal binary logarithms of the numbers 2..=36 rounded up and in u16p15 /// fixed point format pub const INV_LB_I1F15: [u16; 37] = [ 0, 0, 32768, 20675, 16384, 14113, 12677, 11673, 10923, 10338, 9865, 9473, 9141, 8856, 8607, 8388, 8192, 8017, 7859, 7714, 7582, 7461, 7349, 7244, 7147, 7057, 6972, 6892, 6817, 6746, 6678, 6615, 6554, 6496, 6441, 6389, 6339, ]; #[test] fn inv_lb_u16p15() { use core::ops::Mul; for i in 2..=36 { assert_eq!( INV_LB_I1F15[i], (i as f64).log2().powi(-1).mul((1 << 15) as f64).ceil() as u16 ) } } /// This is used for quickly calculating the maximum number of bits /// needed for a string representation of a number in some radix to be /// represented. This may give more bits than needed, but is guaranteed to never /// underestimate the number of bits needed. /// Returns `None` if we see memory exhaustion pub fn bits_upper_bound(len: usize, radix: u8) -> Result<usize, SerdeError> { if radix < 2 || radix > 36 { return Err(InvalidRadix) } // For example, when the radix 10 string "123456789" is going to be converted to // an awint, `LB_I3F13` is indexed by 10 which gives 27214. 27214 multiplied // by the length of the string plus 1 is 27214 * (9 + 1) = 272140. This is // shifted right by 13 and added with 1 which produces 34. The actual number // of bits needed is 27. The increments are needed to guard against edge // cases such as all digits being the maximum for the given radix (e.g. // "999999999" which needs 30 bits). The relative overshoot seems quite // large in this example but improves for larger strings and odd bases. // The multiplication is checked as an extreme safeguard. if let Some(tmp) = (LB_I3F13[radix as usize] as u128).checked_mul((len as u128).wrapping_add(1)) { // `len` should not be larger than `isize::MAX`. if let Ok(estimate) = isize::try_from((tmp >> 13).wrapping_add(1) as usize) { return Ok(estimate as usize) } } Err(Overflow) } /// This takes an input of significant bits and gives an upper bound for the /// number of characters in the given `radix` needed to represent those bits. pub fn chars_upper_bound(significant_bits: usize, radix: u8) -> Result<usize, SerdeError> { if radix < 2 || radix > 36 { return Err(InvalidRadix) } if let Some(tmp) = (INV_LB_I1F15[radix as usize] as u128) .checked_mul((significant_bits as u128).wrapping_add(1)) { if let Ok(estimate) = isize::try_from((tmp >> 15).wrapping_add(1) as usize) { return Ok(estimate as usize) } } Err(Overflow) }