backport NumericString from develop (fixes #2250)

This commit is contained in:
Alex Fabijanic
2018-06-03 13:52:25 -05:00
parent 5fb10f6746
commit b30683fd6d
6 changed files with 638 additions and 107 deletions

View File

@@ -34,6 +34,20 @@
#include <locale>
#endif
#ifndef uintmax_t
typedef Poco::UInt64 uintmax_t;
#endif
#ifndef intmax_t
typedef Poco::Int64 intmax_t;
#endif
#if !defined (INTMAX_MAX)
#define INTMAX_MAX std::numeric_limits<intmax_t>::max()
#endif
#ifdef POCO_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable : 4146)
#endif // POCO_COMPILER_MSVC
// binary numbers are supported, thus 64 (bits) + 1 (string terminating zero)
#define POCO_MAX_INT_STRING_LEN 65
@@ -48,6 +62,82 @@
namespace Poco {
namespace Impl {
template<bool SIGNED, typename T>
class IsNegativeImpl;
template<typename T>
class IsNegativeImpl<true, T>
{
public:
bool operator()(T x) { return x < 0; }
};
template<typename T>
class IsNegativeImpl<false, T>
{
public:
bool operator()(T) { return false; }
};
}
template<typename T>
inline bool isNegative(T x)
{
using namespace Impl;
return IsNegativeImpl<std::numeric_limits<T>::is_signed, T>()(x);
}
template<typename To, typename From>
inline bool isIntOverflow(From val)
{
poco_assert_dbg (std::numeric_limits<From>::is_integer);
poco_assert_dbg (std::numeric_limits<To>::is_integer);
bool ret;
if (std::numeric_limits<To>::is_signed)
{
ret = (!std::numeric_limits<From>::is_signed &&
(::uintmax_t)val > (::uintmax_t)INTMAX_MAX) ||
(::intmax_t)val < (::intmax_t)std::numeric_limits<To>::min() ||
(::intmax_t)val > (::intmax_t)std::numeric_limits<To>::max();
}
else
{
ret = isNegative(val) ||
(::uintmax_t)val > (::uintmax_t)std::numeric_limits<To>::max();
}
return ret;
}
template <typename F, typename T>
inline T& isSafeIntCast(F from)
/// Returns true if it is safe to cast
/// integer from F to T.
{
if (!isIntOverflow<T, F>(from)) return true;
return false;
}
template <typename F, typename T>
inline T& safeIntCast(F from, T& to)
/// Returns csted value if it is safe
/// to cast integer from F to T,
/// otherwise throws BadCastException.
{
if (!isIntOverflow<T, F>(from))
{
to = static_cast<T>(from);
return to;
}
throw BadCastException("safeIntCast: Integer overflow");
}
inline char decimalSeparator()
/// Returns decimal separator from global locale or
/// default '.' for platforms where locale is unavailable.
@@ -77,72 +167,90 @@ inline char thousandSeparator()
//
template <typename I>
bool strToInt(const char* pStr, I& result, short base, char thSep = ',')
bool strToInt(const char* pStr, I& outResult, short base, char thSep = ',')
/// Converts zero-terminated character array to integer number;
/// Thousand separators are recognized for base10 and current locale;
/// it is silently skipped but not verified for correct positioning.
/// they are silently skipped and not verified for correct positioning.
/// It is not allowed to convert a negative number to unsigned integer.
///
/// Function returns true if successful. If parsing was unsuccessful,
/// the return value is false with the result value undetermined.
{
poco_assert_dbg (base == 2 || base == 8 || base == 10 || base == 16);
if (!pStr) return false;
while (std::isspace(*pStr)) ++pStr;
if (*pStr == '\0') return false;
short sign = 1;
bool negative = false;
if ((base == 10) && (*pStr == '-'))
{
// Unsigned types can't be negative so abort parsing
if (std::numeric_limits<I>::min() >= 0) return false;
sign = -1;
if (!std::numeric_limits<I>::is_signed) return false;
negative = true;
++pStr;
}
else if (*pStr == '+') ++pStr;
// parser states:
const char STATE_SIGNIFICANT_DIGITS = 1;
char state = 0;
// all numbers are parsed as positive; the sign
// for negative numbers is adjusted after parsing
::uintmax_t limitCheck = std::numeric_limits<I>::max();
if (negative)
{
poco_assert_dbg(std::numeric_limits<I>::is_signed);
// to cover the entire range, (-min > max) has to be
// taken into account;
// to avoid overflow for the largest int size,
// we resort to FPEnvironment::copySign() (ie. floating-point)
if (sizeof(I) == sizeof(::intmax_t))
limitCheck = static_cast<::uintmax_t>(FPEnvironment::copySign(static_cast<double>(std::numeric_limits<I>::min()), 1));
else
{
::intmax_t i = std::numeric_limits<I>::min();
limitCheck = -i;
}
}
result = 0;
I limitCheck = std::numeric_limits<I>::max() / base;
::uintmax_t result = 0;
for (; *pStr != '\0'; ++pStr)
{
if (result > (limitCheck / base)) return false;
switch (*pStr)
{
case '0':
if (state < STATE_SIGNIFICANT_DIGITS) break;
case '1': case '2': case '3': case '4':
case '5': case '6': case '7':
if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
if (result > limitCheck) return false;
result = result * base + (*pStr - '0');
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
{
char add = (*pStr - '0');
if ((limitCheck - result) < add) return false;
result = result * base + add;
}
break;
case '8': case '9':
if ((base == 10) || (base == 0x10))
{
if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
if (result > limitCheck) return false;
result = result * base + (*pStr - '0');
char add = (*pStr - '0');
if ((limitCheck - result) < add) return false;
result = result * base + add;
}
else return false;
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
{
if (base != 0x10) return false;
if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
if (result > limitCheck) return false;
result = result * base + (10 + *pStr - 'a');
char add = (*pStr - 'a');
if ((limitCheck - result) < add) return false;
result = result * base + (10 + add);
}
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
{
if (base != 0x10) return false;
if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
if (result > limitCheck) return false;
result = result * base + (10 + *pStr - 'A');
char add = (*pStr - 'A');
if ((limitCheck - result) < add) return false;
result = result * base + (10 + add);
}
break;
case '.':
@@ -155,14 +263,28 @@ bool strToInt(const char* pStr, I& result, short base, char thSep = ',')
case ' ':
if ((base == 10) && (thSep == ' ')) break;
// fallthrough
default:
return false;
}
}
if ((sign < 0) && (base == 10)) result *= sign;
if (negative && (base == 10))
{
poco_assert_dbg(std::numeric_limits<I>::is_signed);
::intmax_t i;
if (sizeof(I) == sizeof(::intmax_t))
i = static_cast<::intmax_t>(FPEnvironment::copySign(static_cast<double>(result), -1));
else
i = static_cast<::intmax_t>(-result);
if (isIntOverflow<I>(i)) return false;
outResult = static_cast<I>(i);
}
else
{
if (isIntOverflow<I>(result)) return false;
outResult = static_cast<I>(result);
}
return true;
}
@@ -317,7 +439,7 @@ bool intToStr(T value,
size = ptr - result;
poco_assert_dbg (size <= ptr.span());
poco_assert_dbg ((-1 == width) || (size >= std::size_t(width)));
poco_assert_dbg ((-1 == width) || (size >= size_t(width)));
*ptr-- = '\0';
char* ptrr = result;
@@ -392,7 +514,7 @@ bool uIntToStr(T value,
size = ptr - result;
poco_assert_dbg (size <= ptr.span());
poco_assert_dbg ((-1 == width) || (size >= std::size_t(width)));
poco_assert_dbg ((-1 == width) || (size >= size_t(width)));
*ptr-- = '\0';
char* ptrr = result;
@@ -527,12 +649,15 @@ Foundation_API std::string& doubleToFixedStr(std::string& str,
/// precision (total number of digits after the decimal point) and width (total length of formatted string).
Foundation_API float strToFloat(const char* str);
Foundation_API float strToFloat(const char* str,
const char* inf = POCO_FLT_INF, const char* nan = POCO_FLT_NAN);
/// Converts the string of characters into single-precision floating point number.
/// Function uses double_convesrion::DoubleToStringConverter to do the conversion.
/// Function uses double_conversion::DoubleToStringConverter to do the conversion.
Foundation_API bool strToFloat(const std::string&, float& result, char decSep = '.', char thSep = ',');
Foundation_API bool strToFloat(const std::string&, float& result,
char decSep = '.', char thSep = ',',
const char* inf = POCO_FLT_INF, const char* nan = POCO_FLT_NAN);
/// Converts the string of characters into single-precision floating point number.
/// The conversion result is assigned to the result parameter.
/// If decimal separator and/or thousand separator are different from defaults, they should be
@@ -541,11 +666,14 @@ Foundation_API bool strToFloat(const std::string&, float& result, char decSep =
/// Returns true if successful, false otherwise.
Foundation_API double strToDouble(const char* str);
Foundation_API double strToDouble(const char* str,
const char* inf = POCO_FLT_INF, const char* nan = POCO_FLT_NAN);
/// Converts the string of characters into double-precision floating point number.
Foundation_API bool strToDouble(const std::string& str, double& result, char decSep = '.', char thSep = ',');
Foundation_API bool strToDouble(const std::string& str, double& result,
char decSep = '.', char thSep = ',',
const char* inf = POCO_FLT_INF, const char* nan = POCO_FLT_NAN);
/// Converts the string of characters into double-precision floating point number.
/// The conversion result is assigned to the result parameter.
/// If decimal separator and/or thousand separator are different from defaults, they should be
@@ -557,4 +685,9 @@ Foundation_API bool strToDouble(const std::string& str, double& result, char dec
} // namespace Poco
#ifdef POCO_COMPILER_MSVC
#pragma warning(pop)
#endif // POCO_COMPILER_MSVC
#endif // Foundation_NumericString_INCLUDED

View File

@@ -627,6 +627,22 @@ S cat(const S& delim, const It& begin, const It& end)
}
template <class S>
bool startsWith(const S& str, const S& prefix)
/// Tests whether the string starts with the given prefix.
{
return str.size() >= prefix.size() && equal(prefix.begin(), prefix.end(), str.begin());
}
template <class S>
bool endsWith(const S& str, const S& suffix)
/// Tests whether the string ends with the given suffix.
{
return str.size() >= suffix.size() && equal(suffix.rbegin(), suffix.rend(), str.rbegin());
}
//
// case-insensitive string equality
//

View File

@@ -16,6 +16,7 @@
// +++ double conversion +++
#define double_conversion poco_double_conversion // don't collide with standalone double_conversion library
#define UNREACHABLE poco_bugcheck
#define UNIMPLEMENTED poco_bugcheck
#include "diy-fp.cc"
@@ -57,11 +58,7 @@ void pad(std::string& str, int precision, int width, char prefix = ' ', char dec
std::string::size_type frac = str.length() - decSepPos - 1;
std::string::size_type ePos = str.find_first_of("eE");
#ifndef POCO_ENABLE_CPP11
std::auto_ptr<std::string> eStr;
#else
std::unique_ptr<std::string> eStr;
#endif // POCO_ENABLE_CPP11
if (ePos != std::string::npos)
{
eStr.reset(new std::string(str.substr(ePos, std::string::npos)));
@@ -72,10 +69,47 @@ void pad(std::string& str, int precision, int width, char prefix = ' ', char dec
if (frac != precision)
{
if (frac < precision)
{
str.append(precision - frac, '0');
}
else if ((frac > precision) && (decSepPos != std::string::npos))
{
int pos = static_cast<int>(decSepPos) + 1 + precision;
if (str[pos] >= '5') // we must round up
{
char carry = 0;
if(str[--pos] == '9')
{
str[pos] = '0';
carry = 1;
}
else
{
++str[pos];
carry = 0;
}
while (--pos >= 0)
{
if(str[pos] == decSep) continue;
if(carry)
{
if((str[pos] + carry) <= '9')
{
++str[pos];
carry = 0;
}
else
{
str[pos] = '0';
carry = 1;
}
}
}
if (carry) str.insert(str.begin(), 1, '1');
}
str = str.substr(0, decSepPos + 1 + precision);
}
}
if (eStr.get()) str += *eStr;
@@ -211,7 +245,8 @@ void doubleToFixedStr(char* buffer, int bufferSize, double value, int precision)
StringBuilder builder(buffer, bufferSize);
int flags = DoubleToStringConverter::UNIQUE_ZERO |
DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN;
DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, -std::numeric_limits<double>::digits10, std::numeric_limits<double>::digits10, 0, 0);
DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP,
-std::numeric_limits<double>::digits10, std::numeric_limits<double>::digits10, 0, 0);
dc.ToFixed(value, precision, &builder);
builder.Finalize();
}
@@ -255,32 +290,32 @@ std::string& doubleToFixedStr(std::string& str, double value, int precision, int
}
float strToFloat(const char* str)
float strToFloat(const char* str, const char* inf, const char* nan)
{
using namespace double_conversion;
int processed;
int flags = StringToDoubleConverter::ALLOW_LEADING_SPACES |
StringToDoubleConverter::ALLOW_TRAILING_SPACES;
StringToDoubleConverter converter(flags, 0.0, Single::NaN(), POCO_FLT_INF, POCO_FLT_NAN);
StringToDoubleConverter converter(flags, 0.0, Single::NaN(), inf, nan);
float result = converter.StringToFloat(str, static_cast<int>(strlen(str)), &processed);
return result;
}
double strToDouble(const char* str)
double strToDouble(const char* str, const char* inf, const char* nan)
{
using namespace double_conversion;
int processed;
int flags = StringToDoubleConverter::ALLOW_LEADING_SPACES |
StringToDoubleConverter::ALLOW_TRAILING_SPACES;
StringToDoubleConverter converter(flags, 0.0, Double::NaN(), POCO_FLT_INF, POCO_FLT_NAN);
StringToDoubleConverter converter(flags, 0.0, Double::NaN(), inf, nan);
double result = converter.StringToDouble(str, static_cast<int>(strlen(str)), &processed);
return result;
}
bool strToFloat(const std::string& str, float& result, char decSep, char thSep)
bool strToFloat(const std::string& str, float& result, char decSep, char thSep, const char* inf, const char* nan)
{
using namespace double_conversion;
@@ -289,13 +324,13 @@ bool strToFloat(const std::string& str, float& result, char decSep, char thSep)
removeInPlace(tmp, thSep);
removeInPlace(tmp, 'f');
replaceInPlace(tmp, decSep, '.');
result = strToFloat(tmp.c_str());
result = strToFloat(tmp.c_str(), inf, nan);
return !FPEnvironment::isInfinite(result) &&
!FPEnvironment::isNaN(result);
}
bool strToDouble(const std::string& str, double& result, char decSep, char thSep)
bool strToDouble(const std::string& str, double& result, char decSep, char thSep, const char* inf, const char* nan)
{
if (str.empty()) return false;
@@ -306,7 +341,7 @@ bool strToDouble(const std::string& str, double& result, char decSep, char thSep
removeInPlace(tmp, thSep);
replaceInPlace(tmp, decSep, '.');
removeInPlace(tmp, 'f');
result = strToDouble(tmp.c_str());
result = strToDouble(tmp.c_str(), inf, nan);
return !FPEnvironment::isInfinite(result) &&
!FPEnvironment::isNaN(result);
}

View File

@@ -9,20 +9,22 @@
#include "StringTest.h"
#include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h"
#include "Poco/CppUnit/TestCaller.h"
#include "Poco/CppUnit/TestSuite.h"
#include "Poco/String.h"
#include "Poco/JSONString.h"
#include "Poco/Format.h"
#include "Poco/MemoryStream.h"
#include "Poco/Stopwatch.h"
#include "Poco/FPEnvironment.h"
#include "Poco/Exception.h"
#include "Poco/JSONString.h"
#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <climits>
#include <map>
#include <set>
#include <sstream>
using Poco::trimLeft;
@@ -45,6 +47,8 @@ using Poco::replaceInPlace;
using Poco::remove;
using Poco::removeInPlace;
using Poco::cat;
using Poco::startsWith;
using Poco::endsWith;
using Poco::strToInt;
using Poco::strToFloat;
using Poco::strToDouble;
@@ -55,14 +59,18 @@ using Poco::doubleToStr;
using Poco::thousandSeparator;
using Poco::decimalSeparator;
using Poco::format;
using Poco::toJSON;
using Poco::CILess;
using Poco::MemoryInputStream;
using Poco::Stopwatch;
using Poco::RangeException;
using Poco::toJSON;
using Poco::isIntOverflow;
using Poco::isSafeIntCast;
using Poco::safeIntCast;
using Poco::FPEnvironment;
StringTest::StringTest(const std::string& name): CppUnit::TestCase(name)
StringTest::StringTest(const std::string& rName): CppUnit::TestCase(rName)
{
}
@@ -433,6 +441,55 @@ void StringTest::testCat()
}
void StringTest::testStartsWith()
{
std::string s1("o");
assertTrue (startsWith(s1, std::string("o")));
assertTrue (startsWith(s1, std::string("")));
assertTrue (!startsWith(s1, std::string("O")));
assertTrue (!startsWith(s1, std::string("1")));
std::string s2("");
assertTrue (startsWith(s2, std::string("")));
assertTrue (!startsWith(s2, std::string("o")));
std::string s3("oO");
assertTrue (startsWith(s3, std::string("o")));
assertTrue (!startsWith(s3, std::string(" o")));
}
void StringTest::testEndsWith()
{
std::string s1("o");
assertTrue (endsWith(s1, std::string("o")));
assertTrue (endsWith(s1, std::string("")));
assertTrue (!endsWith(s1, std::string("O")));
assertTrue (!endsWith(s1, std::string("1")));
std::string s2("");
assertTrue (endsWith(s2, std::string("")));
assertTrue (!endsWith(s2, std::string("o")));
std::string s3("Oo");
assertTrue (endsWith(s3, std::string("o")));
assertTrue (!endsWith(s3, std::string("o ")));
}
void StringTest::testStringToInt()
{
//gcc on Mac emits warnings that cannot be suppressed
@@ -540,6 +597,23 @@ void StringTest::testStringToFloat()
assertEqualDelta(12.34, result, 0.01);
}
}
assertTrue (FPEnvironment::isNaN(strToFloat("nan")));
assertTrue (FPEnvironment::isNaN(strToFloat("xNaNy")));
assertTrue (!FPEnvironment::isNaN(strToFloat("inf")));
assertTrue (!FPEnvironment::isNaN(strToFloat("-inf")));
assertTrue (FPEnvironment::isNaN(strToFloat("infinity")));
assertTrue (!FPEnvironment::isNaN(strToFloat("infinity", "infinity")));
assertTrue (!FPEnvironment::isNaN(strToFloat("-infinity", "infinity")));
assertTrue (FPEnvironment::isNaN(strToFloat("Inf")));
assertTrue (!FPEnvironment::isNaN(strToFloat("Inf", "Inf")));
assertTrue (FPEnvironment::isInfinite(strToFloat("inf")));
assertTrue (FPEnvironment::isInfinite(strToFloat("-inf")));
assertTrue (FPEnvironment::isInfinite(strToFloat("infinity", "infinity")));
assertTrue (FPEnvironment::isInfinite(strToFloat("-infinity", "infinity")));
assertTrue (FPEnvironment::isInfinite(strToFloat("Inf")));
assertTrue (FPEnvironment::isInfinite(strToFloat("Inf", "Inf")));
}
@@ -681,9 +755,50 @@ void StringTest::testStringToDouble()
assertEqualDelta(12.34, result, 0.01);
}
}
assertTrue (FPEnvironment::isNaN(strToDouble("nan")));
assertTrue (FPEnvironment::isNaN(strToDouble("xNaNy")));
assertTrue (!FPEnvironment::isNaN(strToDouble("inf")));
assertTrue (!FPEnvironment::isNaN(strToDouble("-inf")));
assertTrue (FPEnvironment::isNaN(strToDouble("infinity")));
assertTrue (!FPEnvironment::isNaN(strToDouble("infinity", "infinity")));
assertTrue (!FPEnvironment::isNaN(strToDouble("-infinity", "infinity")));
assertTrue (FPEnvironment::isNaN(strToDouble("Inf")));
assertTrue (!FPEnvironment::isNaN(strToDouble("Inf", "Inf")));
assertTrue (FPEnvironment::isInfinite(strToDouble("inf")));
assertTrue (FPEnvironment::isInfinite(strToDouble("-inf")));
assertTrue (FPEnvironment::isInfinite(strToDouble("infinity", "infinity")));
assertTrue (FPEnvironment::isInfinite(strToDouble("-infinity", "infinity")));
assertTrue (FPEnvironment::isInfinite(strToDouble("Inf")));
assertTrue (FPEnvironment::isInfinite(strToDouble("Inf", "Inf")));
}
void StringTest::testNumericStringPadding()
{
std::string str;
assertTrue (floatToStr(str, 0.999f, 2, 4) == "1.00");
assertTrue (floatToStr(str, 0.945f, 2, 4) == "0.95");
assertTrue (floatToStr(str, 0.944f, 2, 4) == "0.94");
assertTrue (floatToStr(str, 12.45f, 2, 5) == "12.45");
assertTrue (floatToStr(str, 12.45f, 1, 4) == "12.5");
assertTrue (floatToStr(str, 12.45f, 2, 6) == " 12.45");
assertTrue (floatToStr(str, 12.455f, 3, 7) == " 12.455");
assertTrue (floatToStr(str, 12.455f, 2, 6) == " 12.46");
assertTrue (floatToStr(str, 1.23556E-16f, 2, 6) == "1.24e-16");
assertTrue (doubleToStr(str, 0.999, 2, 4) == "1.00");
assertTrue (doubleToStr(str, 0.945, 2, 4) == "0.95");
assertTrue (doubleToStr(str, 0.944, 2, 4) == "0.94");
assertTrue (doubleToStr(str, 12.45, 2, 5) == "12.45");
assertTrue (doubleToStr(str, 12.45, 1, 4) == "12.5");
assertTrue (doubleToStr(str, 12.45, 2, 6) == " 12.45");
assertTrue (doubleToStr(str, 12.455, 3, 7) == " 12.455");
assertTrue (doubleToStr(str, 12.455, 2, 6) == " 12.46");
assertTrue (doubleToStr(str, 1.23556E-16, 2, 6) == "1.24e-16");
}
void StringTest::testStringToFloatError()
{
char ds = decimalSeparator();
@@ -1004,6 +1119,182 @@ void StringTest::testFloatToString()
}
void StringTest::testNumericStringLimit()
{
char c = 0, t = -1;
assertTrue(!isIntOverflow<char>(c));
assertTrue(safeIntCast<char>(c, t) == c);
assertTrue(t == c);
short s = SHRT_MAX;
assertTrue(isIntOverflow<char>(s));
try
{
safeIntCast(s, t);
fail("cast must fail");
}
catch(Poco::BadCastException&){}
s = SHRT_MIN;
assertTrue(isIntOverflow<char>(s));
try
{
safeIntCast(s, t);
fail("short => char cast must fail");
}
catch(Poco::BadCastException&){}
signed char sc = 0, st = -1;
assertTrue(!isIntOverflow<signed char>(sc));
assertTrue(safeIntCast<char>(sc, st) == sc);
assertTrue(st == sc);
short ss = SHRT_MAX;
assertTrue(isIntOverflow<signed char>(ss));
assertTrue(isIntOverflow<char>(ss));
try
{
safeIntCast(ss, st);
fail("short => signed char cast must fail");
}
catch(Poco::BadCastException&){}
ss = SHRT_MIN;
assertTrue(isIntOverflow<signed char>(ss));
assertTrue(isIntOverflow<char>(ss));
try
{
safeIntCast(ss, st);
fail("short => signed char cast must fail");
}
catch(Poco::BadCastException&){}
assertTrue(safeIntCast<signed char>(sc, st) == c);
assertTrue(st == sc);
unsigned char uc = 0, ut = -1;
assertTrue(!isIntOverflow<unsigned char>(uc));
assertTrue(safeIntCast<char>(uc, ut) == uc);
assertTrue(ut == uc);
ss = SHRT_MAX;
assertTrue(isIntOverflow<unsigned char>(ss));
try
{
safeIntCast(ss, st);
fail("cast must fail");
}
catch(Poco::BadCastException&){}
ss = -1;
assertTrue(isIntOverflow<unsigned char>(ss));
try
{
safeIntCast(ss, uc);
fail("unsigned short => unsigned char cast must fail");
}
catch(Poco::BadCastException&){}
int i = 0;
assertTrue(!isIntOverflow<int>(i));
assertTrue(!isIntOverflow<unsigned>(i));
i = -1;
unsigned int ti = -1;
assertTrue(isIntOverflow<unsigned>(i));
try
{
safeIntCast(i, ti);
fail("unsigned int => int cast must fail");
}
catch(Poco::BadCastException&){}
if (sizeof(long) > sizeof(int))
{
long l = LONG_MAX;
assertTrue(isIntOverflow<int>(l));
l = -1L;
assertTrue(isIntOverflow<unsigned>(l));
i = -1;
assertTrue(!isIntOverflow<long>(i));
long tl = 0;
assertTrue(safeIntCast(i, tl) == i);
unsigned long ul = ULONG_MAX, tul = 0;
assertTrue(isIntOverflow<long>(ul));
try
{
safeIntCast(ul, tl);
fail("unsigned long => long cast must fail");
}
catch(Poco::BadCastException&){}
assertTrue(!isIntOverflow<unsigned long>(ul));
tl = 0;
assertTrue(safeIntCast(ul, tul) == ul);
l = LONG_MIN;
assertTrue(isIntOverflow<unsigned long>(l));
try
{
safeIntCast(l, ul);
fail("unsigned long => long cast must fail");
}
catch(Poco::BadCastException&){}
ul = LONG_MAX;
assertTrue(!isIntOverflow<long>(ul));
assertTrue(safeIntCast(ul, l) == ul);
}
numericStringLimitSameSign<unsigned short, unsigned char>();
numericStringLimitSameSign<short, char>();
numericStringLimitSameSign<unsigned int, unsigned short>();
numericStringLimitSameSign<int, short>();
if (sizeof(long) > sizeof(int))
{
numericStringLimitSameSign<unsigned long, unsigned int>();
numericStringLimitSameSign<long, int>();
}
numericStringLowerLimit<short, char>();
numericStringLowerLimit<int, short>();
if (sizeof(long) > sizeof(int))
{
numericStringLowerLimit<Poco::Int64, Poco::Int32>();
}
#ifdef POCO_ENABLE_CPP11
assertTrue(!isIntOverflow<int8_t>(0));
assertTrue(isIntOverflow<int8_t>(std::numeric_limits<int16_t>::max()));
assertTrue(isIntOverflow<int8_t>(std::numeric_limits<int16_t>::min()));
assertTrue(!isIntOverflow<uint8_t>(0));
assertTrue(isIntOverflow<uint8_t>(std::numeric_limits<int16_t>::max()));
assertTrue(isIntOverflow<uint8_t>(-1));
assertTrue(!isIntOverflow<int32_t>(0));
assertTrue(isIntOverflow<int32_t>(std::numeric_limits<int64_t>::max()));
assertTrue(!isIntOverflow<uint32_t>(0));
assertTrue(isIntOverflow<uint32_t>(-1));
assertTrue(isIntOverflow<uint32_t>(-1L));
assertTrue(isIntOverflow<uint32_t>(-1LL));
assertTrue(!isIntOverflow<int64_t>(-1));
assertTrue(isIntOverflow<int64_t>(std::numeric_limits<uint64_t>::max()));
assertTrue(!isIntOverflow<uint64_t>(std::numeric_limits<uint64_t>::max()));
assertTrue(isIntOverflow<uint64_t>(std::numeric_limits<int64_t>::min()));
assertTrue(!isIntOverflow<uint64_t>(std::numeric_limits<uint64_t>::min()));
assertTrue(!isIntOverflow<int64_t>(std::numeric_limits<int64_t>::max()));
numericStringLimitSameSign<uint16_t, uint8_t>();
numericStringLimitSameSign<int16_t, int8_t>();
numericStringLimitSameSign<uint32_t, uint16_t>();
numericStringLimitSameSign<int32_t, int16_t>();
numericStringLimitSameSign<uint64_t, uint32_t>();
numericStringLimitSameSign<int64_t, int32_t>();
numericStringLowerLimit<int16_t, int8_t>();
numericStringLowerLimit<int32_t, int16_t>();
numericStringLowerLimit<int64_t, int32_t>();
#endif
}
void formatStream(double value, std::string& str)
{
char buffer[128];
@@ -1178,9 +1469,13 @@ CppUnit::Test* StringTest::suite()
CppUnit_addTest(pSuite, StringTest, testReplace);
CppUnit_addTest(pSuite, StringTest, testReplaceInPlace);
CppUnit_addTest(pSuite, StringTest, testCat);
CppUnit_addTest(pSuite, StringTest, testStartsWith);
CppUnit_addTest(pSuite, StringTest, testEndsWith);
CppUnit_addTest(pSuite, StringTest, testStringToInt);
CppUnit_addTest(pSuite, StringTest, testStringToFloat);
CppUnit_addTest(pSuite, StringTest, testStringToDouble);
CppUnit_addTest(pSuite, StringTest, testNumericStringPadding);
CppUnit_addTest(pSuite, StringTest, testNumericStringLimit);
CppUnit_addTest(pSuite, StringTest, testStringToFloatError);
CppUnit_addTest(pSuite, StringTest, testNumericLocale);
//CppUnit_addTest(pSuite, StringTest, benchmarkStrToFloat);

View File

@@ -15,9 +15,10 @@
#include "Poco/Foundation.h"
#include "CppUnit/TestCase.h"
#include "Poco/CppUnit/TestCase.h"
#include "Poco/NumericString.h"
#include "Poco/MemoryStream.h"
#include "Poco/NumberFormatter.h"
class StringTest: public CppUnit::TestCase
@@ -42,10 +43,14 @@ public:
void testReplace();
void testReplaceInPlace();
void testCat();
void testStartsWith();
void testEndsWith();
void testStringToInt();
void testStringToFloat();
void testStringToDouble();
void testNumericStringPadding();
void testNumericStringLimit();
void testStringToFloatError();
void testNumericLocale();
void benchmarkStrToFloat();
@@ -91,6 +96,53 @@ private:
assertTrue (Poco::strToInt("000", result, 010)); assertTrue (result == 0);
}
template <typename Larger, typename Smaller>
void numericStringLimitSameSign()
{
Larger l = std::numeric_limits<Smaller>::max();
std::string str = Poco::NumberFormatter::format(l);
Smaller s;
assertTrue(Poco::strToInt<Smaller>(str, s, 10));
assertTrue(s == std::numeric_limits<Smaller>::max());
++l; str = Poco::NumberFormatter::format(l);
assertFalse(Poco::strToInt<Smaller>(str, s, 10));
++l; str = Poco::NumberFormatter::format(l);
assertFalse(Poco::strToInt<Smaller>(str, s, 10));
++l; str = Poco::NumberFormatter::format(l);
assertFalse(Poco::strToInt<Smaller>(str, s, 10));
++l; str = Poco::NumberFormatter::format(l);
assertFalse(Poco::strToInt<Smaller>(str, s, 10));
++l; str = Poco::NumberFormatter::format(l);
assertFalse(Poco::strToInt<Smaller>(str, s, 10));
++l; str = Poco::NumberFormatter::format(l);
assertFalse(Poco::strToInt<Smaller>(str, s, 10));
}
template <typename Larger, typename Smaller>
void numericStringLowerLimit()
{
Larger l = std::numeric_limits<Smaller>::min();
std::string val = Poco::NumberFormatter::format(l);
Smaller s = -1;
assertFalse(s == std::numeric_limits<Smaller>::min());
assertTrue(Poco::strToInt<Smaller>(val, s, 10));
assertTrue (s == std::numeric_limits<Smaller>::min());
assertTrue(s == std::numeric_limits<Smaller>::min());
--l; val = Poco::NumberFormatter::format(l);
assertFalse(Poco::strToInt<Smaller>(val, s, 10));
--l; val = Poco::NumberFormatter::format(l);
assertFalse(Poco::strToInt<Smaller>(val, s, 10));
--l; val = Poco::NumberFormatter::format(l);
assertFalse(Poco::strToInt<Smaller>(val, s, 10));
--l; val = Poco::NumberFormatter::format(l);
assertFalse(Poco::strToInt<Smaller>(val, s, 10));
--l; val = Poco::NumberFormatter::format(l);
assertFalse(Poco::strToInt<Smaller>(val, s, 10));
--l; val = Poco::NumberFormatter::format(l);
assertFalse(Poco::strToInt<Smaller>(val, s, 10));
}
template <typename T>
bool parseStream(const std::string& s, T& value)
{