Detect overflow in functions to convert strings to numbers
Otherwise these functions can strictly run into undefined behavior which should be prevented.
This commit is contained in:
parent
d5e35e460c
commit
3afed30760
|
@ -505,6 +505,27 @@ template <typename CharType> CharType charToDigit(CharType character, CharType b
|
||||||
throw ConversionException(std::move(errorMsg));
|
throw ConversionException(std::move(errorMsg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \cond
|
||||||
|
namespace Detail {
|
||||||
|
template <typename IntegralType, typename CharType, typename BaseType = IntegralType>
|
||||||
|
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<CharType>(base)), &result)) {
|
||||||
|
throw ConversionException("Number exceeds limit.");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
result *= static_cast<IntegralType>(base);
|
||||||
|
result += static_cast<IntegralType>(charToDigit<CharType>(character, static_cast<CharType>(base)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} // namespace Detail
|
||||||
|
/// \endcond
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Converts the given \a string to an unsigned number assuming \a string uses the specified \a base.
|
* \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.
|
* \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;
|
IntegralType result = 0;
|
||||||
for (const auto &c : string) {
|
for (const auto &c : string) {
|
||||||
if (c == ' ') {
|
Detail::raiseAndAdd(result, base, c);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result *= static_cast<IntegralType>(base);
|
|
||||||
result += static_cast<IntegralType>(charToDigit<typename StringType::value_type>(c, static_cast<typename StringType::value_type>(base)));
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -552,11 +569,7 @@ IntegralType stringToNumber(const StringType &string, BaseType base = 10)
|
||||||
}
|
}
|
||||||
IntegralType result = 0;
|
IntegralType result = 0;
|
||||||
for (; i != end; ++i) {
|
for (; i != end; ++i) {
|
||||||
if (*i == ' ') {
|
Detail::raiseAndAdd(result, base, *i);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result *= static_cast<IntegralType>(base);
|
|
||||||
result += static_cast<IntegralType>(charToDigit<typename StringType::value_type>(*i, static_cast<typename StringType::value_type>(base)));
|
|
||||||
}
|
}
|
||||||
return negative ? -result : result;
|
return negative ? -result : result;
|
||||||
}
|
}
|
||||||
|
@ -601,11 +614,7 @@ IntegralType stringToNumber(const CharType *string, BaseType base = 10)
|
||||||
{
|
{
|
||||||
IntegralType result = 0;
|
IntegralType result = 0;
|
||||||
for (; *string; ++string) {
|
for (; *string; ++string) {
|
||||||
if (*string == ' ') {
|
Detail::raiseAndAdd(result, base, *string);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result *= static_cast<IntegralType>(base);
|
|
||||||
result += static_cast<IntegralType>(charToDigit<CharType>(*string, static_cast<CharType>(base)));
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -649,11 +658,7 @@ IntegralType bufferToNumber(const CharType *string, std::size_t size, BaseType b
|
||||||
{
|
{
|
||||||
IntegralType result = 0;
|
IntegralType result = 0;
|
||||||
for (const CharType *end = string + size; string != end; ++string) {
|
for (const CharType *end = string + size; string != end; ++string) {
|
||||||
if (*string == ' ') {
|
Detail::raiseAndAdd(result, base, *string);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result *= static_cast<IntegralType>(base);
|
|
||||||
result += static_cast<IntegralType>(charToDigit<CharType>(*string, static_cast<CharType>(base)));
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -683,11 +688,7 @@ IntegralType stringToNumber(const CharType *string, IntegralType base = 10)
|
||||||
}
|
}
|
||||||
IntegralType result = 0;
|
IntegralType result = 0;
|
||||||
for (; *string; ++string) {
|
for (; *string; ++string) {
|
||||||
if (*string == ' ') {
|
Detail::raiseAndAdd(result, base, *string);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result *= static_cast<IntegralType>(base);
|
|
||||||
result += static_cast<IntegralType>(charToDigit<CharType>(*string, static_cast<CharType>(base)));
|
|
||||||
}
|
}
|
||||||
return negative ? -result : result;
|
return negative ? -result : result;
|
||||||
}
|
}
|
||||||
|
@ -718,11 +719,7 @@ IntegralType bufferToNumber(const CharType *string, std::size_t size, BaseType b
|
||||||
}
|
}
|
||||||
IntegralType result = 0;
|
IntegralType result = 0;
|
||||||
for (; string != end; ++string) {
|
for (; string != end; ++string) {
|
||||||
if (*string == ' ') {
|
Detail::raiseAndAdd(result, base, *string);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result *= static_cast<IntegralType>(base);
|
|
||||||
result += static_cast<IntegralType>(charToDigit<CharType>(*string, static_cast<CharType>(base)));
|
|
||||||
}
|
}
|
||||||
return negative ? -result : result;
|
return negative ? -result : result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -283,8 +283,14 @@ void ConversionTests::testStringConversions()
|
||||||
CPPUNIT_ASSERT_EQUAL(23u, stringToNumber<std::uint32_t>(" 023"s));
|
CPPUNIT_ASSERT_EQUAL(23u, stringToNumber<std::uint32_t>(" 023"s));
|
||||||
CPPUNIT_ASSERT_EQUAL(23u, bufferToNumber<std::uint32_t>(" 023", 5));
|
CPPUNIT_ASSERT_EQUAL(23u, bufferToNumber<std::uint32_t>(" 023", 5));
|
||||||
CPPUNIT_ASSERT_EQUAL(255u, stringToNumber<std::uint32_t>("fF", 16));
|
CPPUNIT_ASSERT_EQUAL(255u, stringToNumber<std::uint32_t>("fF", 16));
|
||||||
CPPUNIT_ASSERT_THROW(stringToNumber<std::uint32_t>("fF", 15), ConversionException);
|
CPPUNIT_ASSERT_THROW_MESSAGE("character out of range", stringToNumber<std::uint32_t>("fF", 15), ConversionException);
|
||||||
CPPUNIT_ASSERT_THROW(stringToNumber<std::uint32_t>("(", 15), ConversionException);
|
CPPUNIT_ASSERT_THROW_MESSAGE("invalid character", stringToNumber<std::uint32_t>("(", 15), ConversionException);
|
||||||
|
#ifdef __GNUC__ // overflow detection only supported on GCC and Clang
|
||||||
|
CPPUNIT_ASSERT_THROW_MESSAGE("overflow", stringToNumber<std::uint32_t>("100000000", 16), ConversionException);
|
||||||
|
CPPUNIT_ASSERT_THROW_MESSAGE("underflow", stringToNumber<std::int32_t>("-80000001", 16), ConversionException);
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("positive limit", 0xFFFFFFFFu, stringToNumber<std::uint32_t>("FFFFFFFF", 16));
|
||||||
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("negative limit", -2147483647, stringToNumber<std::int32_t>("-2147483647", 10));
|
||||||
|
#endif
|
||||||
|
|
||||||
// interpretIntegerAsString()
|
// interpretIntegerAsString()
|
||||||
CPPUNIT_ASSERT_EQUAL("TEST"s, interpretIntegerAsString<std::uint32_t>(0x54455354));
|
CPPUNIT_ASSERT_EQUAL("TEST"s, interpretIntegerAsString<std::uint32_t>(0x54455354));
|
||||||
|
|
Loading…
Reference in New Issue