diff --git a/conversion/stringconversion.h b/conversion/stringconversion.h index 4c491c3..d50feec 100644 --- a/conversion/stringconversion.h +++ b/conversion/stringconversion.h @@ -505,6 +505,27 @@ template CharType charToDigit(CharType character, CharType b throw ConversionException(std::move(errorMsg)); } +/// \cond +namespace Detail { +template +void raiseAndAdd(IntegralType &result, BaseType base, CharType character) +{ + if (character == ' ') { + return; + } +#ifdef __GNUC__ // overflow detection only supported on GCC and Clang + if (__builtin_mul_overflow(result, base, &result) + || __builtin_add_overflow(result, charToDigit(character, static_cast(base)), &result)) { + throw ConversionException("Number exceeds limit."); + } +#else + result *= static_cast(base); + result += static_cast(charToDigit(character, static_cast(base))); +#endif +} +} // namespace Detail +/// \endcond + /*! * \brief Converts the given \a string to an unsigned number assuming \a string uses the specified \a base. * \tparam IntegralType The data type used to store the converted value. @@ -519,11 +540,7 @@ IntegralType stringToNumber(const StringType &string, BaseType base = 10) { IntegralType result = 0; for (const auto &c : string) { - if (c == ' ') { - continue; - } - result *= static_cast(base); - result += static_cast(charToDigit(c, static_cast(base))); + Detail::raiseAndAdd(result, base, c); } return result; } @@ -552,11 +569,7 @@ IntegralType stringToNumber(const StringType &string, BaseType base = 10) } IntegralType result = 0; for (; i != end; ++i) { - if (*i == ' ') { - continue; - } - result *= static_cast(base); - result += static_cast(charToDigit(*i, static_cast(base))); + Detail::raiseAndAdd(result, base, *i); } return negative ? -result : result; } @@ -601,11 +614,7 @@ IntegralType stringToNumber(const CharType *string, BaseType base = 10) { IntegralType result = 0; for (; *string; ++string) { - if (*string == ' ') { - continue; - } - result *= static_cast(base); - result += static_cast(charToDigit(*string, static_cast(base))); + Detail::raiseAndAdd(result, base, *string); } return result; } @@ -649,11 +658,7 @@ IntegralType bufferToNumber(const CharType *string, std::size_t size, BaseType b { IntegralType result = 0; for (const CharType *end = string + size; string != end; ++string) { - if (*string == ' ') { - continue; - } - result *= static_cast(base); - result += static_cast(charToDigit(*string, static_cast(base))); + Detail::raiseAndAdd(result, base, *string); } return result; } @@ -683,11 +688,7 @@ IntegralType stringToNumber(const CharType *string, IntegralType base = 10) } IntegralType result = 0; for (; *string; ++string) { - if (*string == ' ') { - continue; - } - result *= static_cast(base); - result += static_cast(charToDigit(*string, static_cast(base))); + Detail::raiseAndAdd(result, base, *string); } return negative ? -result : result; } @@ -718,11 +719,7 @@ IntegralType bufferToNumber(const CharType *string, std::size_t size, BaseType b } IntegralType result = 0; for (; string != end; ++string) { - if (*string == ' ') { - continue; - } - result *= static_cast(base); - result += static_cast(charToDigit(*string, static_cast(base))); + Detail::raiseAndAdd(result, base, *string); } return negative ? -result : result; } diff --git a/tests/conversiontests.cpp b/tests/conversiontests.cpp index b4d7701..6c7cde2 100644 --- a/tests/conversiontests.cpp +++ b/tests/conversiontests.cpp @@ -283,8 +283,14 @@ void ConversionTests::testStringConversions() CPPUNIT_ASSERT_EQUAL(23u, stringToNumber(" 023"s)); CPPUNIT_ASSERT_EQUAL(23u, bufferToNumber(" 023", 5)); CPPUNIT_ASSERT_EQUAL(255u, stringToNumber("fF", 16)); - CPPUNIT_ASSERT_THROW(stringToNumber("fF", 15), ConversionException); - CPPUNIT_ASSERT_THROW(stringToNumber("(", 15), ConversionException); + CPPUNIT_ASSERT_THROW_MESSAGE("character out of range", stringToNumber("fF", 15), ConversionException); + CPPUNIT_ASSERT_THROW_MESSAGE("invalid character", stringToNumber("(", 15), ConversionException); +#ifdef __GNUC__ // overflow detection only supported on GCC and Clang + CPPUNIT_ASSERT_THROW_MESSAGE("overflow", stringToNumber("100000000", 16), ConversionException); + CPPUNIT_ASSERT_THROW_MESSAGE("underflow", stringToNumber("-80000001", 16), ConversionException); + CPPUNIT_ASSERT_EQUAL_MESSAGE("positive limit", 0xFFFFFFFFu, stringToNumber("FFFFFFFF", 16)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("negative limit", -2147483647, stringToNumber("-2147483647", 10)); +#endif // interpretIntegerAsString() CPPUNIT_ASSERT_EQUAL("TEST"s, interpretIntegerAsString(0x54455354));