From 5efb6f4f85582843c9992dba6168141a0c2bc175 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Sat, 10 Aug 2019 22:06:41 +0200 Subject: [PATCH] fix some bugs and add some tests from from_str stuff --- src/from_str.rs | 348 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 321 insertions(+), 27 deletions(-) diff --git a/src/from_str.rs b/src/from_str.rs index 1f29bb5..b85490e 100644 --- a/src/from_str.rs +++ b/src/from_str.rs @@ -23,55 +23,69 @@ use crate::{ use core::{ cmp::{self, Ordering}, fmt::{Display, Formatter, Result as FmtResult}, - ops::{Add, Shl}, + ops::{Add, Shl, Shr}, str::FromStr, }; fn bin_str_to_bin(a: &str, dump_bits: u32) -> Option where - I: SealedInt + Shl + Add + From, + I: SealedInt + From, + I: Shl + Shr + Add, { debug_assert!(!a.is_empty()); - let mut bits = I::NBITS - dump_bits; + let req_bits = I::NBITS - dump_bits; + let mut rem_bits = req_bits; let mut acc = I::ZERO; for &byte in a.as_bytes() { let val = byte - b'0'; - if bits < 1 { + if rem_bits < 1 { // round - return acc.checked_add(I::from(val)); + acc = acc.checked_add(I::from(val))?; + if dump_bits != 0 && !(acc >> req_bits).is_zero() { + return None; + } + return Some(acc); } acc = (acc << 1) + I::from(val); - bits -= 1; + rem_bits -= 1; } - Some(acc << bits) + Some(acc << rem_bits) } fn oct_str_to_bin(a: &str, dump_bits: u32) -> Option where - I: SealedInt + Shl + Add + From, + I: SealedInt + From, + I: Shl + Shr + Add, { debug_assert!(!a.is_empty()); - let mut bits = I::NBITS - dump_bits; + let req_bits = I::NBITS - dump_bits; + let mut rem_bits = req_bits; let mut acc = I::ZERO; for &byte in a.as_bytes() { let val = byte - b'0'; - if bits < 3 { - acc = (acc << bits) + I::from(val >> (3 - bits)); + if rem_bits < 3 { + acc = (acc << rem_bits) + I::from(val >> (3 - rem_bits)); // round - return acc.checked_add(I::from((val >> (2 - bits)) & 1)); + acc = acc.checked_add(I::from((val >> (2 - rem_bits)) & 1))?; + if dump_bits != 0 && !(acc >> req_bits).is_zero() { + return None; + } + return Some(acc); } acc = (acc << 3) + I::from(val); - bits -= 3; + rem_bits -= 3; } - Some(acc << bits) + Some(acc << rem_bits) } fn hex_str_to_bin(a: &str, dump_bits: u32) -> Option where - I: SealedInt + Shl + Add + From, + I: SealedInt + From, + I: Shl + Shr + Add, { debug_assert!(!a.is_empty()); - let mut bits = I::NBITS - dump_bits; + let req_bits = I::NBITS - dump_bits; + let mut rem_bits = req_bits; let mut acc = I::ZERO; for &byte in a.as_bytes() { // We know that byte is a valid hex: @@ -79,15 +93,19 @@ where // * b'A'..=b'F' (0x41..=0x46) => val = byte & 0x0f + 9 // * b'a'..=b'f' (0x61..=0x66) => val = byte & 0x0f + 9 let val = (byte & 0x0f) + if byte >= 0x40 { 9 } else { 0 }; - if bits < 4 { - acc = (acc << bits) + I::from(val >> (4 - bits)); + if rem_bits < 4 { + acc = (acc << rem_bits) + I::from(val >> (4 - rem_bits)); // round - return acc.checked_add(I::from((val >> (3 - bits)) & 1)); + acc = acc.checked_add(I::from((val >> (3 - rem_bits)) & 1))?; + if dump_bits != 0 && !(acc >> req_bits).is_zero() { + return None; + } + return Some(acc); } acc = (acc << 4) + I::from(val); - bits -= 4; + rem_bits -= 4; } - Some(acc << bits) + Some(acc << rem_bits) } // 5^3 × 2 < 2^8 => (10^3 - 1) × 2^(8-3+1) < 2^16 @@ -485,16 +503,17 @@ macro_rules! impl_from_str_unsigned_not128 { if frac.is_empty() { return Some(0); } + let int_nbits = <$Bits as SealedInt>::NBITS - nbits; match radix { - 2 => bin_str_to_bin(frac, nbits), - 8 => oct_str_to_bin(frac, nbits), - 16 => hex_str_to_bin(frac, nbits), + 2 => bin_str_to_bin(frac, int_nbits), + 8 => oct_str_to_bin(frac, int_nbits), + 16 => hex_str_to_bin(frac, int_nbits), 10 => { let end = cmp::min(frac.len(), $dec_frac_digits); let rem = $dec_frac_digits - end; let ten: $DoubleBits = 10; let i = frac[..end].parse::<$DoubleBits>().unwrap() * ten.pow(rem as u32); - $decode_frac(i, <$Bits as SealedInt>::NBITS - nbits) + $decode_frac(i, int_nbits) } _ => unreachable!(), } @@ -578,8 +597,11 @@ fn get_frac128(frac: &str, radix: u32, nbits: u32) -> Option { if frac.is_empty() { return Some(0); } + let int_nbits = ::NBITS - nbits; match radix { - 2 => bin_str_to_bin(frac, nbits), + 2 => bin_str_to_bin(frac, int_nbits), + 8 => oct_str_to_bin(frac, int_nbits), + 16 => hex_str_to_bin(frac, int_nbits), 10 => { let (hi, lo) = if frac.len() <= 27 { let rem = 27 - frac.len(); @@ -592,7 +614,7 @@ fn get_frac128(frac: &str, radix: u32, nbits: u32) -> Option { let lo = frac[27..lo_end].parse::().unwrap() * 10u128.pow(rem as u32); (hi, lo) }; - dec27_27_to_bin128(hi, lo, ::NBITS - nbits) + dec27_27_to_bin128(hi, lo, int_nbits) } _ => unreachable!(), } @@ -771,6 +793,26 @@ mod tests { } } + fn assert_ok_radix(s: &str, radix: u32, bits: F::Bits) + where + F: Fixed + FromStrRadix, + F::Bits: Eq + Debug, + { + match ::from_str_radix(s, radix) { + Ok(f) => assert_eq!(f.to_bits(), bits), + Err(e) => panic!("could not parse {}: {}", s, e), + } + } + fn assert_err_radix(s: &str, radix: u32, kind: ParseErrorKind) + where + F: Fixed + FromStrRadix, + { + match ::from_str_radix(s, radix) { + Ok(f) => panic!("incorrectly parsed {} as {}", s, f), + Err(ParseFixedError { kind: err }) => assert_eq!(err, kind), + } + } + #[test] fn check_i8_u8_from_str() { use crate::types::*; @@ -888,6 +930,123 @@ mod tests { assert_err::("4294967295.5", ParseErrorKind::Overflow); } + #[test] + fn check_i16_u16_from_str_binary() { + use crate::types::*; + + assert_err_radix::("-1", 2, ParseErrorKind::Overflow); + assert_err_radix::("-0.100000000000000010", 2, ParseErrorKind::Overflow); + assert_ok_radix::("-0.100000000000000001", 2, -0x8000); + assert_ok_radix::("0.011111111111111101", 2, 0x7FFF); + assert_err_radix::("0.011111111111111110", 2, ParseErrorKind::Overflow); + assert_err_radix::("1", 2, ParseErrorKind::Overflow); + + assert_err_radix::("-10000000.0000000010", 2, ParseErrorKind::Overflow); + assert_ok_radix::("-10000000.0000000001", 2, -0x8000); + assert_ok_radix::("1111111.1111111101", 2, 0x7FFF); + assert_err_radix::("1111111.1111111110", 2, ParseErrorKind::Overflow); + + assert_err_radix::("-1000000000000000.10", 2, ParseErrorKind::Overflow); + assert_ok_radix::("-1000000000000000.01", 2, -0x8000); + assert_ok_radix::("111111111111111.01", 2, 0x7FFF); + assert_err_radix::("111111111111111.10", 2, ParseErrorKind::Overflow); + + assert_err_radix::("-0", 2, ParseErrorKind::InvalidDigit); + assert_ok_radix::("0.011111111111111101", 2, 0x7FFF); + assert_ok_radix::("0.011111111111111110", 2, 0x8000); + assert_ok_radix::("0.111111111111111101", 2, 0xFFFF); + assert_err_radix::("0.111111111111111110", 2, ParseErrorKind::Overflow); + assert_err_radix::("1", 2, ParseErrorKind::Overflow); + + assert_ok_radix::("1111111.1111111101", 2, 0x7FFF); + assert_ok_radix::("1111111.1111111110", 2, 0x8000); + assert_ok_radix::("11111111.1111111101", 2, 0xFFFF); + assert_err_radix::("11111111.1111111110", 2, ParseErrorKind::Overflow); + + assert_ok_radix::("111111111111111.01", 2, 0x7FFF); + assert_ok_radix::("111111111111111.10", 2, 0x8000); + assert_ok_radix::("1111111111111111.01", 2, 0xFFFF); + assert_err_radix::("1111111111111111.10", 2, ParseErrorKind::Overflow); + } + + #[test] + fn check_i16_u16_from_str_octal() { + use crate::types::*; + + assert_err_radix::("-1", 8, ParseErrorKind::Overflow); + assert_err_radix::("-0.400002", 8, ParseErrorKind::Overflow); + assert_ok_radix::("-0.400001", 8, -0x8000); + assert_ok_radix::("0.377775", 8, 0x7FFF); + assert_err_radix::("0.377776", 8, ParseErrorKind::Overflow); + assert_err_radix::("1", 8, ParseErrorKind::Overflow); + + assert_err_radix::("-2000.0010", 8, ParseErrorKind::Overflow); + assert_ok_radix::("-200.0007", 8, -0x8000); + assert_ok_radix::("177.7767", 8, 0x7FFF); + assert_err_radix::("177.7770", 8, ParseErrorKind::Overflow); + + assert_err_radix::("-100000.4", 8, ParseErrorKind::Overflow); + assert_ok_radix::("-100000.3", 8, -0x8000); + assert_ok_radix::("77777.3", 8, 0x7FFF); + assert_err_radix::("77777.4", 8, ParseErrorKind::Overflow); + + assert_err_radix::("-0", 8, ParseErrorKind::InvalidDigit); + assert_ok_radix::("0.377775", 8, 0x7FFF); + assert_ok_radix::("0.377776", 8, 0x8000); + assert_ok_radix::("0.777775", 8, 0xFFFF); + assert_err_radix::("0.777776", 8, ParseErrorKind::Overflow); + assert_err_radix::("1", 8, ParseErrorKind::Overflow); + + assert_ok_radix::("177.7767", 8, 0x7FFF); + assert_ok_radix::("177.7770", 8, 0x8000); + assert_ok_radix::("377.7767", 8, 0xFFFF); + assert_err_radix::("377.7770", 8, ParseErrorKind::Overflow); + + assert_ok_radix::("77777.3", 8, 0x7FFF); + assert_ok_radix::("77777.4", 8, 0x8000); + assert_ok_radix::("177777.3", 8, 0xFFFF); + assert_err_radix::("177777.4", 8, ParseErrorKind::Overflow); + } + + #[test] + fn check_i16_u16_from_str_hex() { + use crate::types::*; + + assert_err_radix::("-1", 16, ParseErrorKind::Overflow); + assert_err_radix::("-0.80008", 16, ParseErrorKind::Overflow); + assert_ok_radix::("-0.80007", 16, -0x8000); + assert_ok_radix::("0.7FFF7", 16, 0x7FFF); + assert_err_radix::("0.7FFF8", 16, ParseErrorKind::Overflow); + assert_err_radix::("1", 16, ParseErrorKind::Overflow); + + assert_err_radix::("-80.008", 16, ParseErrorKind::Overflow); + assert_ok_radix::("-80.007", 16, -0x8000); + assert_ok_radix::("7F.FF7", 16, 0x7FFF); + assert_err_radix::("7F.FF8", 16, ParseErrorKind::Overflow); + + assert_err_radix::("-8000.8", 16, ParseErrorKind::Overflow); + assert_ok_radix::("-8000.7", 16, -0x8000); + assert_ok_radix::("7FFF.7", 16, 0x7FFF); + assert_err_radix::("7FFF.8", 16, ParseErrorKind::Overflow); + + assert_err_radix::("-0", 16, ParseErrorKind::InvalidDigit); + assert_ok_radix::("0.7FFF7", 16, 0x7FFF); + assert_ok_radix::("0.7FFF8", 16, 0x8000); + assert_ok_radix::("0.FFFF7", 16, 0xFFFF); + assert_err_radix::("0.FFFF8", 16, ParseErrorKind::Overflow); + assert_err_radix::("1", 16, ParseErrorKind::Overflow); + + assert_ok_radix::("7F.FF7", 16, 0x7FFF); + assert_ok_radix::("7F.FF8", 16, 0x8000); + assert_ok_radix::("FF.FF7", 16, 0xFFFF); + assert_err_radix::("FF.FF8", 16, ParseErrorKind::Overflow); + + assert_ok_radix::("7FFF.7", 16, 0x7FFF); + assert_ok_radix::("7FFF.8", 16, 0x8000); + assert_ok_radix::("FFFF.7", 16, 0xFFFF); + assert_err_radix::("FFFF.8", 16, ParseErrorKind::Overflow); + } + #[test] fn check_i64_u64_from_str() { use crate::types::*; @@ -1049,4 +1208,139 @@ mod tests { ParseErrorKind::Overflow, ); } + + #[test] + fn check_i128_u128_from_str_hex() { + use crate::types::*; + + assert_err_radix::("-1", 16, ParseErrorKind::Overflow); + assert_err_radix::( + "-0.800000000000000000000000000000008", + 16, + ParseErrorKind::Overflow, + ); + assert_ok_radix::( + "-0.800000000000000000000000000000007", + 16, + -0x8000_0000_0000_0000_0000_0000_0000_0000, + ); + assert_ok_radix::( + "0.7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7", + 16, + 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, + ); + assert_err_radix::( + "0.7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8", + 16, + ParseErrorKind::Overflow, + ); + assert_err_radix::("1", 16, ParseErrorKind::Overflow); + + assert_err_radix::( + "-8000000000000000.00000000000000008", + 16, + ParseErrorKind::Overflow, + ); + assert_ok_radix::( + "-8000000000000000.00000000000000007", + 16, + -0x8000_0000_0000_0000_0000_0000_0000_0000, + ); + assert_ok_radix::( + "7FFFFFFFFFFFFFFF.FFFFFFFFFFFFFFFF7", + 16, + 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, + ); + assert_err_radix::( + "7FFFFFFFFFFFFFFF.FFFFFFFFFFFFFFFF8", + 16, + ParseErrorKind::Overflow, + ); + + assert_err_radix::( + "-80000000000000000000000000000000.8", + 16, + ParseErrorKind::Overflow, + ); + assert_ok_radix::( + "-80000000000000000000000000000000.7", + 16, + -0x8000_0000_0000_0000_0000_0000_0000_0000, + ); + assert_ok_radix::( + "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.7", + 16, + 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, + ); + assert_err_radix::( + "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.8", + 16, + ParseErrorKind::Overflow, + ); + + assert_err_radix::("-0", 16, ParseErrorKind::InvalidDigit); + assert_ok_radix::( + "0.7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7", + 16, + 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, + ); + assert_ok_radix::( + "0.7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8", + 16, + 0x8000_0000_0000_0000_0000_0000_0000_0000, + ); + assert_ok_radix::( + "0.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7", + 16, + 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, + ); + assert_err_radix::( + "0.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8", + 16, + ParseErrorKind::Overflow, + ); + assert_err_radix::("1", 16, ParseErrorKind::Overflow); + + assert_ok_radix::( + "7FFFFFFFFFFFFFFF.FFFFFFFFFFFFFFFF7", + 16, + 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, + ); + assert_ok_radix::( + "7FFFFFFFFFFFFFFF.FFFFFFFFFFFFFFFF8", + 16, + 0x8000_0000_0000_0000_0000_0000_0000_0000, + ); + assert_ok_radix::( + "FFFFFFFFFFFFFFFF.FFFFFFFFFFFFFFFF7", + 16, + 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, + ); + assert_err_radix::( + "FFFFFFFFFFFFFFFF.FFFFFFFFFFFFFFFF8", + 16, + ParseErrorKind::Overflow, + ); + + assert_ok_radix::( + "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.7", + 16, + 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, + ); + assert_ok_radix::( + "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.8", + 16, + 0x8000_0000_0000_0000_0000_0000_0000_0000, + ); + assert_ok_radix::( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.7", + 16, + 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, + ); + assert_err_radix::( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.8", + 16, + ParseErrorKind::Overflow, + ); + } }