diff --git a/Cargo.toml b/Cargo.toml index f0ecebd..06a51a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "baa" -version = "0.16.6" +version = "0.16.7" edition = "2021" authors = ["Kevin Laeufer "] description = "BitVector and Array Arithmetic" diff --git a/src/bv/arithmetic.rs b/src/bv/arithmetic.rs index b15b552..8bc7f0e 100644 --- a/src/bv/arithmetic.rs +++ b/src/bv/arithmetic.rs @@ -378,6 +378,43 @@ pub(crate) fn is_neg(src: &[Word], width: WidthInt) -> bool { msb_bit_value == 1 } +#[inline] +pub(crate) fn is_pow2(words: &[Word]) -> Option { + // find most significant bit set + let mut bit_pos = None; + for (word_ii, &word) in words.iter().enumerate() { + if bit_pos.is_none() { + if word != 0 { + // is there only one bit set? + if word.leading_zeros() + word.trailing_zeros() == Word::BITS - 1 { + bit_pos = Some(word.trailing_zeros() + word_ii as WidthInt * Word::BITS); + } else { + // more than one bit set + return None; + } + } + } else if word != 0 { + // more than one bit set + return None; + } + } + bit_pos +} + +#[inline] +pub(crate) fn min_width(words: &[Word]) -> WidthInt { + // find most significant bit set + for (word_ii, &word) in words.iter().enumerate() { + if word != 0 { + // cannot underflow since word.leading_zeros() is always less than Word::BITS + let bit_pos = Word::BITS - word.leading_zeros() - 1; + return word_ii as WidthInt * Word::BITS + bit_pos + 1; + } + } + // all words are zero + 0 +} + #[inline] pub(crate) fn cmp_greater_signed(a: &[Word], b: &[Word], width: WidthInt) -> bool { let (is_neg_a, is_neg_b) = (is_neg(a, width), is_neg(b, width)); diff --git a/src/bv/io/strings.rs b/src/bv/io/strings.rs index d198aeb..de91d96 100644 --- a/src/bv/io/strings.rs +++ b/src/bv/io/strings.rs @@ -258,17 +258,7 @@ pub(crate) fn from_str_radix( } [lsb, msb] => { debug_assert!(width <= 128); - let out = match u128::from_str_radix(value, radix) { - Ok(v) => v, - Err(e) => { - let kind = match e.kind() { - std::num::IntErrorKind::NegOverflow - | std::num::IntErrorKind::PosOverflow => IntErrorKind::ExceedsWidth, - _ => IntErrorKind::InvalidDigit, - }; - return Err(ParseIntError { kind }); - } - }; + let out = parse_u128(value, radix)?; *lsb = out as Word; *msb = (out >> Word::BITS) as Word; } @@ -292,7 +282,7 @@ pub(crate) fn from_str_radix( match radix { 2 => parse_base_2(digits, out, width)?, - 10 => parse_base_10(digits, out, width)?, + 10 => parse_base_10(digits, out)?, 16 => parse_base_16(digits, out)?, _ => todo!("Implement support for base {radix}. Currently the following bases are available: 2, 10, 16"), }; @@ -315,6 +305,21 @@ pub(crate) fn from_str_radix( Ok(()) } +fn parse_u128(value: &str, radix: u32) -> Result { + match u128::from_str_radix(value, radix) { + Ok(v) => Ok(v), + Err(e) => { + let kind = match e.kind() { + std::num::IntErrorKind::NegOverflow | std::num::IntErrorKind::PosOverflow => { + IntErrorKind::ExceedsWidth + } + _ => IntErrorKind::InvalidDigit, + }; + Err(ParseIntError { kind }) + } + } +} + fn parse_base_16(digits: &[u8], out: &mut [Word]) -> Result { let num_digits = digits.len(); let words = (num_digits as u32 * BITS_PER_HEX_DIGIT).div_ceil(Word::BITS); @@ -337,13 +342,22 @@ fn parse_base_16(digits: &[u8], out: &mut [Word]) -> Result Result { - // let other = BitVecValue:: - todo!() +// format!("{}", u128::MAX).len() = 39 +const MAX_U128_DEC_DIGITS: usize = 39 - 1; + +fn parse_base_10(digits: &[u8], out: &mut [Word]) -> Result { + if digits.len() <= MAX_U128_DEC_DIGITS { + let value = parse_u128(std::str::from_utf8(digits).unwrap(), 10)?; + out[0] = value as Word; + out[1] = (value >> Word::BITS) as Word; + for o in out.iter_mut().skip(2) { + *o = 0; + } + } else { + todo!() + } + // calculate the number of bits + Ok(crate::bv::arithmetic::min_width(out)) } fn parse_base_2( diff --git a/src/bv/ops.rs b/src/bv/ops.rs index ea405e8..6d446d5 100644 --- a/src/bv/ops.rs +++ b/src/bv/ops.rs @@ -218,25 +218,13 @@ pub trait BitVecOps { } fn is_pow_2(&self) -> Option { - // find most significant bit set - let mut bit_pos = None; - for (word_ii, &word) in self.words().iter().enumerate() { - if bit_pos.is_none() { - if word != 0 { - // is there only one bit set? - if word.leading_zeros() + word.trailing_zeros() == Word::BITS - 1 { - bit_pos = Some(word.trailing_zeros() + word_ii as WidthInt * Word::BITS); - } else { - // more than one bit set - return None; - } - } - } else if word != 0 { - // more than one bit set - return None; - } - } - bit_pos + crate::bv::arithmetic::is_pow2(self.words()) + } + + /// Computes the minimum number of bits that are necessary to represent the current value. + /// This corresponds to the position of the most significant `1` plus one. + fn min_width(&self) -> WidthInt { + crate::bv::arithmetic::min_width(self.words()) } /// Computes all ranges for which the bits are one. diff --git a/tests/bitvec_to_from_str.rs b/tests/bitvec_to_from_str.rs index aca7a2b..cc00b17 100644 --- a/tests/bitvec_to_from_str.rs +++ b/tests/bitvec_to_from_str.rs @@ -79,6 +79,13 @@ fn test_to_from_dec_str_regression() { do_test_to_from_decimal_str("1000000"); } +#[test] +fn test_to_from_dec_str() { + let dec_str = "34"; + let value = BitVecValue::from_str_radix(dec_str, 10, 512).unwrap(); + assert_eq!(value.to_u64().unwrap(), 34); +} + proptest! { #![proptest_config(ProptestConfig::with_cases(10000))]