From 7695b680e31084c84d9c01906865479cf2f99bc8 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Sun, 11 Aug 2019 00:06:08 +0200 Subject: [PATCH] multiplication-free radix 2, 4, 16 parsing of integer part --- src/from_str.rs | 134 ++++++++++++++++++++++++++++++++++------------ src/sealed_int.rs | 6 +++ 2 files changed, 105 insertions(+), 35 deletions(-) diff --git a/src/from_str.rs b/src/from_str.rs index b85490e..a7abc21 100644 --- a/src/from_str.rs +++ b/src/from_str.rs @@ -27,7 +27,25 @@ use core::{ str::FromStr, }; -fn bin_str_to_bin(a: &str, dump_bits: u32) -> Option +fn bin_str_int_to_bin(a: &str) -> Option +where + I: SealedInt + From, + I: Shl + Shr + Add, +{ + debug_assert!(!a.is_empty()); + let mut bytes = a.as_bytes().iter(); + let first_val = *bytes.next().unwrap() - b'0'; + let mut acc = I::from(first_val); + let mut leading_zeros = acc.leading_zeros(); + for &byte in bytes { + let val = byte - b'0'; + leading_zeros = leading_zeros.checked_sub(1)?; + acc = (acc << 1) + I::from(val); + } + Some(acc) +} + +fn bin_str_frac_to_bin(a: &str, dump_bits: u32) -> Option where I: SealedInt + From, I: Shl + Shr + Add, @@ -52,7 +70,25 @@ where Some(acc << rem_bits) } -fn oct_str_to_bin(a: &str, dump_bits: u32) -> Option +fn oct_str_int_to_bin(a: &str) -> Option +where + I: SealedInt + From, + I: Shl + Shr + Add, +{ + debug_assert!(!a.is_empty()); + let mut bytes = a.as_bytes().iter(); + let first_val = *bytes.next().unwrap() - b'0'; + let mut acc = I::from(first_val); + let mut leading_zeros = acc.leading_zeros(); + for &byte in bytes { + let val = byte - b'0'; + leading_zeros = leading_zeros.checked_sub(3)?; + acc = (acc << 3) + I::from(val); + } + Some(acc) +} + +fn oct_str_frac_to_bin(a: &str, dump_bits: u32) -> Option where I: SealedInt + From, I: Shl + Shr + Add, @@ -78,7 +114,34 @@ where Some(acc << rem_bits) } -fn hex_str_to_bin(a: &str, dump_bits: u32) -> Option +#[inline] +fn unchecked_hex_digit(byte: u8) -> u8 { + // We know that byte is a valid hex: + // * b'0'..=b'9' (0x30..=0x39) => byte & 0x0f + // * b'A'..=b'F' (0x41..=0x46) => byte & 0x0f + 9 + // * b'a'..=b'f' (0x61..=0x66) => byte & 0x0f + 9 + (byte & 0x0f) + if byte >= 0x40 { 9 } else { 0 } +} + +fn hex_str_int_to_bin(a: &str) -> Option +where + I: SealedInt + From, + I: Shl + Add, +{ + debug_assert!(!a.is_empty()); + let mut bytes = a.as_bytes().iter(); + let first_val = unchecked_hex_digit(*bytes.next().unwrap()); + let mut acc = I::from(first_val); + let mut leading_zeros = acc.leading_zeros(); + for &byte in bytes { + let val = unchecked_hex_digit(byte); + leading_zeros = leading_zeros.checked_sub(4)?; + acc = (acc << 4) + I::from(val); + } + Some(acc) +} + +fn hex_str_frac_to_bin(a: &str, dump_bits: u32) -> Option where I: SealedInt + From, I: Shl + Shr + Add, @@ -88,11 +151,7 @@ where 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: - // * b'0'..=b'9' (0x30..=0x39) => val = byte & 0x0f - // * 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 }; + let val = unchecked_hex_digit(byte); if rem_bits < 4 { acc = (acc << rem_bits) + I::from(val >> (4 - rem_bits)); // round @@ -404,7 +463,10 @@ macro_rules! impl_from_str_signed { Some(frac) => (frac, false), None => (0, true), }; - let abs_int = $int(int, radix, int_nbits, whole_frac)?; + let abs_int = match $int(int, radix, int_nbits, whole_frac) { + Some(i) => i, + None => err!(Overflow), + }; let abs = abs_int | abs_frac; let max_abs = if neg { <$Bits as SealedInt>::Unsigned::MSB @@ -442,16 +504,14 @@ macro_rules! impl_from_str_unsigned { Some(frac) => (frac, false), None => (0, true), }; - let int = $int(int, radix, int_nbits, whole_frac)?; + let int = match $int(int, radix, int_nbits, whole_frac) { + Some(i) => i, + None => err!(Overflow), + }; Ok(int | frac) } - fn $int( - int: &str, - radix: u32, - nbits: u32, - whole_frac: bool, - ) -> Result<$Bits, ParseFixedError> { + fn $int(int: &str, radix: u32, nbits: u32, whole_frac: bool) -> Option<$Bits> { const HALF: u32 = <$Bits as SealedInt>::NBITS / 2; if $int_half_cond && nbits <= HALF { return $int_half(int, radix, nbits, whole_frac).map(|x| $Bits::from(x) << HALF); @@ -460,23 +520,27 @@ macro_rules! impl_from_str_unsigned { while int.starts_with('0') { int = &int[1..]; } - if nbits == 0 { - err!(whole_frac || !int.is_empty(), Overflow); - return Ok(0); + if int.is_empty() && !whole_frac { + return Some(0); + } else if int.is_empty() || nbits == 0 { + return None; } - let mut acc = match <$Bits>::from_str_radix(int, radix) { - Ok(i) => i, - Err(_) => err!(Overflow), - }; + let mut parsed_int = match radix { + 2 => bin_str_int_to_bin(int), + 8 => oct_str_int_to_bin(int), + 16 => hex_str_int_to_bin(int), + 10 => int.parse::<$Bits>().ok(), + _ => unreachable!(), + }?; if whole_frac { - acc = match acc.overflowing_add(1) { - (acc, false) => acc, - (_, true) => err!(Overflow), - }; + parsed_int = parsed_int.checked_add(1)?; } let remove_bits = <$Bits as SealedInt>::NBITS - nbits; - err!(remove_bits > 0 && (acc >> nbits) != 0, Overflow); - Ok(acc << remove_bits) + if remove_bits > 0 && (parsed_int >> nbits) != 0 { + None + } else { + Some(parsed_int << remove_bits) + } } }; } @@ -505,9 +569,9 @@ macro_rules! impl_from_str_unsigned_not128 { } let int_nbits = <$Bits as SealedInt>::NBITS - nbits; match radix { - 2 => bin_str_to_bin(frac, int_nbits), - 8 => oct_str_to_bin(frac, int_nbits), - 16 => hex_str_to_bin(frac, int_nbits), + 2 => bin_str_frac_to_bin(frac, int_nbits), + 8 => oct_str_frac_to_bin(frac, int_nbits), + 16 => hex_str_frac_to_bin(frac, int_nbits), 10 => { let end = cmp::min(frac.len(), $dec_frac_digits); let rem = $dec_frac_digits - end; @@ -599,9 +663,9 @@ fn get_frac128(frac: &str, radix: u32, nbits: u32) -> Option { } let int_nbits = ::NBITS - nbits; match radix { - 2 => bin_str_to_bin(frac, int_nbits), - 8 => oct_str_to_bin(frac, int_nbits), - 16 => hex_str_to_bin(frac, int_nbits), + 2 => bin_str_frac_to_bin(frac, int_nbits), + 8 => oct_str_frac_to_bin(frac, int_nbits), + 16 => hex_str_frac_to_bin(frac, int_nbits), 10 => { let (hi, lo) = if frac.len() <= 27 { let rem = 27 - frac.len(); diff --git a/src/sealed_int.rs b/src/sealed_int.rs index c35e2f5..9f844cc 100644 --- a/src/sealed_int.rs +++ b/src/sealed_int.rs @@ -87,6 +87,7 @@ pub trait SealedInt: Copy + Ord + Debug + Display { fn is_zero(self) -> bool; fn is_negative(self) -> bool; fn checked_add(self, val: Self) -> Option; + fn leading_zeros(self) -> u32; fn to_fixed_dir_overflow( self, @@ -162,6 +163,11 @@ macro_rules! sealed_int { <$Int>::checked_add(self, val) } + #[inline] + fn leading_zeros(self) -> u32 { + <$Int>::leading_zeros(self) + } + #[inline] fn to_repr_fixed(self) -> Self::ReprFixed { Self::ReprFixed::from_bits(self.int_repr())