From 852040e876aa318c0b6fa05c43b2cfa9ee2579c3 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Sat, 10 Aug 2019 15:34:49 +0200 Subject: [PATCH] simplify from_str and start adding support for 2, 8, 16 radixes --- src/from_str.rs | 347 +++++++++++++++++++----------------------------- 1 file changed, 140 insertions(+), 207 deletions(-) diff --git a/src/from_str.rs b/src/from_str.rs index 1f758d9..ba263e8 100644 --- a/src/from_str.rs +++ b/src/from_str.rs @@ -28,7 +28,7 @@ use core::{ // 5^3 × 2 < 2^8 => (10^3 - 1) × 2^(8-3+1) < 2^16 // Returns None for large fractions that are rounded to 1.0 -pub fn dec3_to_bin8(a: u16, dump_bits: u32) -> Option { +fn dec3_to_bin8(a: u16, dump_bits: u32) -> Option { debug_assert!(a < 10u16.pow(3)); debug_assert!(dump_bits <= 8); let divisor = 5u16.pow(3) * 2; @@ -42,7 +42,7 @@ pub fn dec3_to_bin8(a: u16, dump_bits: u32) -> Option { } // 5^6 × 2 < 2^16 => (10^6 - 1) × 2^(16-6+1) < 2^32 // Returns None for large fractions that are rounded to 1.0 -pub fn dec6_to_bin16(a: u32, dump_bits: u32) -> Option { +fn dec6_to_bin16(a: u32, dump_bits: u32) -> Option { debug_assert!(a < 10u32.pow(6)); debug_assert!(dump_bits <= 16); let divisor = 5u32.pow(6) * 2; @@ -56,7 +56,7 @@ pub fn dec6_to_bin16(a: u32, dump_bits: u32) -> Option { } // 5^13 × 2 < 2^32 => (10^13 - 1) × 2^(32-13+1) < 2^64 // Returns None for large fractions that are rounded to 1.0 -pub fn dec13_to_bin32(a: u64, dump_bits: u32) -> Option { +fn dec13_to_bin32(a: u64, dump_bits: u32) -> Option { debug_assert!(a < 10u64.pow(13)); debug_assert!(dump_bits <= 32); let divisor = 5u64.pow(13) * 2; @@ -70,7 +70,7 @@ pub fn dec13_to_bin32(a: u64, dump_bits: u32) -> Option { } // 5^27 × 2 < 2^64 => (10^27 - 1) × 2^(64-27+1) < 2^128 // Returns None for large fractions that are rounded to 1.0 -pub fn dec27_to_bin64(a: u128, dump_bits: u32) -> Option { +fn dec27_to_bin64(a: u128, dump_bits: u32) -> Option { debug_assert!(a < 10u128.pow(27)); debug_assert!(dump_bits <= 64); let divisor = 5u128.pow(27) * 2; @@ -84,7 +84,7 @@ pub fn dec27_to_bin64(a: u128, dump_bits: u32) -> Option { } // 5^54 × 2 < 2^128 => (10^54 - 1) × 2^(128-54+1) < 2^256 // Returns None for large fractions that are rounded to 1.0 -pub fn dec27_27_to_bin128(hi: u128, lo: u128, dump_bits: u32) -> Option { +fn dec27_27_to_bin128(hi: u128, lo: u128, dump_bits: u32) -> Option { debug_assert!(hi < 10u128.pow(27)); debug_assert!(lo < 10u128.pow(27)); debug_assert!(dump_bits <= 128); @@ -210,7 +210,7 @@ impl Display for ParseFixedError { } } -fn parse(s: &str, can_be_neg: bool) -> Result, ParseFixedError> { +fn parse(s: &str, can_be_neg: bool, radix: i32) -> Result, ParseFixedError> { let mut int = (0, 0); let mut frac = (0, 0); let mut has_sign = false; @@ -218,20 +218,20 @@ fn parse(s: &str, can_be_neg: bool) -> Result, ParseFixedError> { let mut has_digits = false; let mut has_point = false; for (index, c) in s.char_indices() { - if c == '.' { - err!(has_point, TooManyPoints); - has_digits = false; - has_point = true; - frac.0 = index + c.len_utf8(); - continue; - } - match c { - '+' => { + match (radix, c) { + (_, '.') => { + err!(has_point, TooManyPoints); + has_digits = false; + has_point = true; + frac.0 = index + c.len_utf8(); + continue; + } + (_, '+') => { err!(has_point || has_sign || has_digits, InvalidDigit); has_sign = true; continue; } - '-' => { + (_, '-') => { err!( has_point || has_sign || has_digits || !can_be_neg, InvalidDigit @@ -240,7 +240,12 @@ fn parse(s: &str, can_be_neg: bool) -> Result, ParseFixedError> { is_negative = true; continue; } - '0'..='9' => { + (2, '0'..='1') + | (8, '0'..='7') + | (10, '0'..='9') + | (16, '0'..='9') + | (16, 'a'..='f') + | (16, 'A'..='F') => { if !has_point && !has_digits { int.0 = index; } @@ -276,7 +281,7 @@ macro_rules! impl_from_str { type Err = ParseFixedError; #[inline] fn from_str(s: &str) -> Result { - $method(s, Self::int_nbits(), Self::frac_nbits()).map(Self::from_bits) + $method(s, 10, Self::int_nbits(), Self::frac_nbits()).map(Self::from_bits) } } }; @@ -284,16 +289,21 @@ macro_rules! impl_from_str { macro_rules! impl_from_str_signed { ( - ($Fixed:ident, $NBits:ident, $Bits:ident, $HalfBits:ident, $DoubleBits:ident, $one:expr); + $Fixed:ident, $NBits:ident, $Bits:ident; fn $all:ident; - fn $int:ident, $int_half:expr; + fn $int:ident, ($int_half:ident, $int_half_cond:expr); $frac:ident; ) => { impl_from_str! { $Fixed, $NBits, $all } - fn $all(s: &str, int_nbits: u32, frac_nbits: u32) -> Result<$Bits, ParseFixedError> { - let Parse { neg, int, frac } = parse(s, true)?; - let (frac, whole_frac) = match $frac(frac, frac_nbits) { + fn $all( + s: &str, + radix: i32, + int_nbits: u32, + frac_nbits: u32, + ) -> Result<$Bits, ParseFixedError> { + let Parse { neg, int, frac } = parse(s, true, 10)?; + let (frac, whole_frac) = match $frac(frac, radix, frac_nbits) { Some(frac) => (frac, false), None => (0, true), }; @@ -313,22 +323,19 @@ macro_rules! impl_from_str_signed { } else { frac as $Bits }; - let int = $int(neg, int, int_nbits, whole_frac)?; + let int = $int(neg, int, radix, int_nbits, whole_frac)?; Ok(int | frac) } fn $int( neg: bool, int: &str, + radix: i32, nbits: u32, whole_frac: bool, ) -> Result<$Bits, ParseFixedError> { - let half: Option Result<$HalfBits, ParseFixedError>> = - $int_half; - if let Some(half) = half { - if nbits <= <$Bits as SealedInt>::NBITS / 2 { - return half(neg, int, nbits, whole_frac).map($Bits::from); - } + if $int_half_cond && nbits <= <$Bits as SealedInt>::NBITS / 2 { + return $int_half(neg, int, radix, nbits, whole_frac).map($Bits::from); } let mut int = int; while int.starts_with('0') { @@ -339,11 +346,11 @@ macro_rules! impl_from_str_signed { return Ok(0); } let max_abs_int = if neg { - $one << (nbits - 1) + <$Bits as SealedInt>::Unsigned::MSB } else { - ($one << (nbits - 1)) - 1 + <$Bits as SealedInt>::Unsigned::MSB - 1 }; - let mut acc = match int.parse::<$DoubleBits>() { + let mut acc = match int.parse::<<$Bits as SealedInt>::Unsigned>() { Ok(i) => { err!(i > max_abs_int, Overflow); i @@ -366,30 +373,36 @@ macro_rules! impl_from_str_signed { macro_rules! impl_from_str_unsigned { ( - ($Fixed:ident, $NBits:ident, $Bits:ident, $HalfBits:ident, $DoubleBits:ident, $one:expr); + $Fixed:ident, $NBits:ident, $Bits:ident; fn $all:ident; - fn $int:ident, $int_half:expr; - fn $frac:ident, $frac_half:expr; - $decode_frac:ident, $dec_frac_digits:expr; + fn $int:ident, ($int_half:ident, $int_half_cond:expr); + $frac:ident; ) => { impl_from_str! { $Fixed, $NBits, $all } - fn $all(s: &str, int_nbits: u32, frac_nbits: u32) -> Result<$Bits, ParseFixedError> { - let Parse { int, frac, .. } = parse(s, false)?; - let (frac, whole_frac) = match $frac(frac, frac_nbits) { + fn $all( + s: &str, + radix: i32, + int_nbits: u32, + frac_nbits: u32, + ) -> Result<$Bits, ParseFixedError> { + let Parse { int, frac, .. } = parse(s, false, 10)?; + let (frac, whole_frac) = match $frac(frac, radix, frac_nbits) { Some(frac) => (frac, false), None => (0, true), }; - let int = $int(int, int_nbits, whole_frac)?; + let int = $int(int, radix, int_nbits, whole_frac)?; Ok(int | frac) } - fn $int(int: &str, nbits: u32, whole_frac: bool) -> Result<$Bits, ParseFixedError> { - let half: Option Result<$HalfBits, ParseFixedError>> = $int_half; - if let Some(half) = half { - if nbits <= <$Bits as SealedInt>::NBITS / 2 { - return half(int, nbits, whole_frac).map($Bits::from); - } + fn $int( + int: &str, + radix: i32, + nbits: u32, + whole_frac: bool, + ) -> Result<$Bits, ParseFixedError> { + if $int_half_cond && nbits <= <$Bits as SealedInt>::NBITS / 2 { + return $int_half(int, radix, nbits, whole_frac).map($Bits::from); } let mut int = int; while int.starts_with('0') { @@ -399,204 +412,124 @@ macro_rules! impl_from_str_unsigned { err!(whole_frac || !int.is_empty(), Overflow); return Ok(0); } - let max_abs_int = ($one << nbits) - 1; - let mut acc = match int.parse::<$DoubleBits>() { - Ok(i) => { - err!(i > max_abs_int, Overflow); - i - } + let mut acc = match int.parse::<$Bits>() { + Ok(i) => i, Err(_) => err!(Overflow), }; if whole_frac { - acc += 1; - err!(acc > max_abs_int, Overflow); + acc = match acc.overflowing_add(1) { + (acc, false) => acc, + (_, true) => err!(Overflow), + }; } - Ok((acc as $Bits) << (<$Bits as SealedInt>::NBITS - nbits)) + Ok(acc << (<$Bits as SealedInt>::NBITS - nbits)) + } + }; +} + +macro_rules! impl_from_str_unsigned_not128 { + ( + $Fixed:ident, $NBits:ident, $Bits:ident; + fn $all:ident; + fn $int:ident, ($int_half:ident, $int_half_cond:expr); + fn $frac:ident, ($frac_half:ident, $frac_half_cond:expr); + $decode_frac:ident, $dec_frac_digits:expr, $DoubleBits:ident; + ) => { + impl_from_str_unsigned! { + $Fixed, $NBits, $Bits; + fn $all; + fn $int, ($int_half, $int_half_cond); + $frac; } - fn $frac(frac: &str, nbits: u32) -> Option<$Bits> { - let half: Option Option<$HalfBits>> = $frac_half; - if let Some(half) = half { - if nbits <= <$Bits as SealedInt>::NBITS / 2 { - return half(frac, nbits).map($Bits::from); - } + fn $frac(frac: &str, radix: i32, nbits: u32) -> Option<$Bits> { + if $frac_half_cond && nbits <= <$Bits as SealedInt>::NBITS / 2 { + return $frac_half(frac, radix, nbits).map($Bits::from); } if frac.is_empty() { return Some(0); } let end = cmp::min(frac.len(), $dec_frac_digits); let rem = $dec_frac_digits - end; - let i = frac[..end].parse::<$DoubleBits>().unwrap() * ($one * 10).pow(rem as u32); + let ten: $DoubleBits = 10; + let i = frac[..end].parse::<$DoubleBits>().unwrap() * ten.pow(rem as u32); $decode_frac(i, <$Bits as SealedInt>::NBITS - nbits) } }; } impl_from_str_signed! { - (FixedI8, U8, i8, bool, u16, 1u16); + FixedI8, U8, i8; fn from_str_i8; - fn get_int_i8, None; + fn get_int_i8, (get_int_i8, false); get_frac8; } -impl_from_str_unsigned! { - (FixedU8, U8, u8, bool, u16, 1u16); +impl_from_str_unsigned_not128! { + FixedU8, U8, u8; fn from_str_u8; - fn get_int_u8, None; - fn get_frac8, None; - dec3_to_bin8, 3; + fn get_int_u8, (get_int_u8, false); + fn get_frac8, (get_frac8, false); + dec3_to_bin8, 3, u16; } impl_from_str_signed! { - (FixedI16, U16, i16, i8, u32, 1u32); + FixedI16, U16, i16; fn from_str_i16; - fn get_int_i16, Some(get_int_i8); + fn get_int_i16, (get_int_i8, true); get_frac16; } -impl_from_str_unsigned! { - (FixedU16, U16, u16, u8, u32, 1u32); +impl_from_str_unsigned_not128! { + FixedU16, U16, u16; fn from_str_u16; - fn get_int_u16, Some(get_int_u8); - fn get_frac16, Some(get_frac8); - dec6_to_bin16, 6; + fn get_int_u16, (get_int_u8, true); + fn get_frac16, (get_frac8, true); + dec6_to_bin16, 6, u32; } impl_from_str_signed! { - (FixedI32, U32, i32, i16, u64, 1u64); + FixedI32, U32, i32; fn from_str_i32; - fn get_int_i32, Some(get_int_i16); + fn get_int_i32, (get_int_i16, true); get_frac32; } -impl_from_str_unsigned! { - (FixedU32, U32, u32, u16, u64, 1u64); +impl_from_str_unsigned_not128! { + FixedU32, U32, u32; fn from_str_u32; - fn get_int_u32, Some(get_int_u16); - fn get_frac32, Some(get_frac16); - dec13_to_bin32, 13; + fn get_int_u32, (get_int_u16, true); + fn get_frac32, (get_frac16, true); + dec13_to_bin32, 13, u64; } impl_from_str_signed! { - (FixedI64, U64, i64, i32, u128, 1u128); + FixedI64, U64, i64; fn from_str_i64; - fn get_int_i64, Some(get_int_i32); + fn get_int_i64, (get_int_i32, true); get_frac64; } -impl_from_str_unsigned! { - (FixedU64, U64, u64, u32, u128, 1u128); +impl_from_str_unsigned_not128! { + FixedU64, U64, u64; fn from_str_u64; - fn get_int_u64, Some(get_int_u32); - fn get_frac64, Some(get_frac32); - dec27_to_bin64, 27; + fn get_int_u64, (get_int_u32, true); + fn get_frac64, (get_frac32, true); + dec27_to_bin64, 27, u128; } -impl_from_str! { FixedI128, U128, from_str_i128 } - -fn from_str_i128(s: &str, int_nbits: u32, frac_nbits: u32) -> Result { - let Parse { neg, int, frac } = parse(s, true)?; - let (frac, whole_frac) = match get_frac128(frac, frac_nbits) { - Some(frac) => (frac, false), - None => (0, true), - }; - let frac = if frac_nbits == ::NBITS { - // special case: no int bits - if neg { - if frac > ::Unsigned::MSB { - err!(Overflow) - } - frac.wrapping_neg() as i128 - } else { - if frac >= ::Unsigned::MSB { - err!(Overflow) - } - frac as i128 - } - } else { - frac as i128 - }; - let int = get_int_i128(neg, int, int_nbits, whole_frac)?; - Ok(int | frac) +impl_from_str_signed! { + FixedI128, U128, i128; + fn from_str_i128; + fn get_int_i128, (get_int_i64, true); + get_frac128; +} +impl_from_str_unsigned! { + FixedU128, U128, u128; + fn from_str_u128; + fn get_int_u128, (get_int_u64, true); + get_frac128; } -fn get_int_i128( - neg: bool, - int: &str, - nbits: u32, - whole_frac: bool, -) -> Result { +fn get_frac128(frac: &str, radix: i32, nbits: u32) -> Option { if nbits <= 64 { - return get_int_i64(neg, int, nbits, whole_frac).map(i128::from); - } - let mut int = int; - while int.starts_with('0') { - int = &int[1..]; - } - if nbits == 0 { - err!(whole_frac || !int.is_empty(), Overflow); - return Ok(0); - } - let max_abs_int = if neg { - 1u128 << (nbits - 1) - } else { - (1u128 << (nbits - 1)) - 1 - }; - let mut acc = match int.parse::() { - Ok(i) => { - err!(i > max_abs_int, Overflow); - i - } - Err(_) => err!(Overflow), - }; - if whole_frac { - acc += 1; - err!(acc > max_abs_int, Overflow); - } - let signed = if neg { - acc.wrapping_neg() as i128 - } else { - acc as i128 - }; - Ok(signed << (::NBITS - nbits)) -} - -impl_from_str! { FixedU128, U128, from_str_u128 } - -fn from_str_u128(s: &str, int_nbits: u32, frac_nbits: u32) -> Result { - let Parse { int, frac, .. } = parse(s, false)?; - let (frac, whole_frac) = match get_frac128(frac, frac_nbits) { - Some(frac) => (frac, false), - None => (0, true), - }; - let int = get_int_u128(int, int_nbits, whole_frac)?; - Ok(int | frac) -} - -fn get_int_u128(int: &str, nbits: u32, whole_frac: bool) -> Result { - if nbits <= 64 { - return get_int_u64(int, nbits, whole_frac).map(u128::from); - } - let mut int = int; - while int.starts_with('0') { - int = &int[1..]; - } - if nbits == 0 { - err!(whole_frac || !int.is_empty(), Overflow); - return Ok(0); - } - let mut acc = match int.parse::() { - Ok(i) => i, - Err(_) => err!(Overflow), - }; - if whole_frac { - acc = match acc.overflowing_add(1) { - (acc, false) => acc, - (_, true) => err!(Overflow), - }; - } - Ok(acc << (::NBITS - nbits)) -} - -fn get_frac128(frac: &str, nbits: u32) -> Option { - if nbits <= 64 { - return get_frac64(frac, nbits).map(u128::from); + return get_frac64(frac, radix, nbits).map(u128::from); } if frac.is_empty() { return Some(0); @@ -744,26 +677,26 @@ mod tests { #[test] fn check_parse_bounds() { - let Parse { neg, int, frac } = parse("-12.34", true).unwrap(); + let Parse { neg, int, frac } = parse("-12.34", true, 10).unwrap(); assert_eq!((neg, int, frac), (true, "12", "34")); - let Parse { neg, int, frac } = parse("12.", true).unwrap(); + let Parse { neg, int, frac } = parse("12.", true, 10).unwrap(); assert_eq!((neg, int, frac), (false, "12", "")); - let Parse { neg, int, frac } = parse("+.34", false).unwrap(); + let Parse { neg, int, frac } = parse("+.34", false, 10).unwrap(); assert_eq!((neg, int, frac), (false, "", "34")); - let Parse { neg, int, frac } = parse("0", false).unwrap(); + let Parse { neg, int, frac } = parse("0", false, 10).unwrap(); assert_eq!((neg, int, frac), (false, "0", "")); - let ParseFixedError { kind } = parse("0 ", true).unwrap_err(); + let ParseFixedError { kind } = parse("0 ", true, 10).unwrap_err(); assert_eq!(kind, ParseErrorKind::InvalidDigit); - let ParseFixedError { kind } = parse("+.", true).unwrap_err(); + let ParseFixedError { kind } = parse("+.", true, 10).unwrap_err(); assert_eq!(kind, ParseErrorKind::NoDigits); - let ParseFixedError { kind } = parse(".1.", true).unwrap_err(); + let ParseFixedError { kind } = parse(".1.", true, 10).unwrap_err(); assert_eq!(kind, ParseErrorKind::TooManyPoints); - let ParseFixedError { kind } = parse("1+2", true).unwrap_err(); + let ParseFixedError { kind } = parse("1+2", true, 10).unwrap_err(); assert_eq!(kind, ParseErrorKind::InvalidDigit); - let ParseFixedError { kind } = parse("1-2", true).unwrap_err(); + let ParseFixedError { kind } = parse("1-2", true, 10).unwrap_err(); assert_eq!(kind, ParseErrorKind::InvalidDigit); - let ParseFixedError { kind } = parse("-12", false).unwrap_err(); + let ParseFixedError { kind } = parse("-12", false, 10).unwrap_err(); assert_eq!(kind, ParseErrorKind::InvalidDigit); }