mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-26 18:42:41 +01:00
fix(strToInt): overflows #3580
This commit is contained in:
@@ -33,8 +33,6 @@
|
|||||||
#if !defined(POCO_NO_LOCALE)
|
#if !defined(POCO_NO_LOCALE)
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#endif
|
#endif
|
||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
#if defined(POCO_NOINTMAX)
|
#if defined(POCO_NOINTMAX)
|
||||||
typedef Poco::UInt64 uintmax_t;
|
typedef Poco::UInt64 uintmax_t;
|
||||||
typedef Poco::Int64 intmax_t;
|
typedef Poco::Int64 intmax_t;
|
||||||
@@ -113,6 +111,46 @@ inline bool isIntOverflow(From val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename R, typename F, typename S>
|
||||||
|
bool safeMultiply(R& result, F f, S s)
|
||||||
|
{
|
||||||
|
if ((f == 0) || (s==0))
|
||||||
|
{
|
||||||
|
result = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f > 0)
|
||||||
|
{
|
||||||
|
if (s > 0)
|
||||||
|
{
|
||||||
|
if (f > (std::numeric_limits<R>::max() / s))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (s < (std::numeric_limits<R>::min() / f))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (s > 0)
|
||||||
|
{
|
||||||
|
if (f < (std::numeric_limits<R>::min() / s))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (s < (std::numeric_limits<R>::max() / f))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = f * s;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename F, typename T>
|
template <typename F, typename T>
|
||||||
inline T& isSafeIntCast(F from)
|
inline T& isSafeIntCast(F from)
|
||||||
/// Returns true if it is safe to cast
|
/// Returns true if it is safe to cast
|
||||||
@@ -125,7 +163,7 @@ inline T& isSafeIntCast(F from)
|
|||||||
|
|
||||||
template <typename F, typename T>
|
template <typename F, typename T>
|
||||||
inline T& safeIntCast(F from, T& to)
|
inline T& safeIntCast(F from, T& to)
|
||||||
/// Returns csted value if it is safe
|
/// Returns cast value if it is safe
|
||||||
/// to cast integer from F to T,
|
/// to cast integer from F to T,
|
||||||
/// otherwise throws BadCastException.
|
/// otherwise throws BadCastException.
|
||||||
{
|
{
|
||||||
@@ -191,49 +229,34 @@ bool strToInt(const char* pStr, I& outResult, short base, char thSep = ',')
|
|||||||
|
|
||||||
// numbers are parsed as unsigned, for negative numbers the sign is applied after parsing
|
// numbers are parsed as unsigned, for negative numbers the sign is applied after parsing
|
||||||
// overflow is checked in every parse step
|
// overflow is checked in every parse step
|
||||||
uintmax_t limitCheck = negative ? -std::numeric_limits<I>::min() : std::numeric_limits<I>::max();
|
uintmax_t limitCheck = std::numeric_limits<I>::max();
|
||||||
I result = 0;
|
if (negative) ++limitCheck;
|
||||||
|
uintmax_t result = 0;
|
||||||
|
unsigned char add = 0;
|
||||||
for (; *pStr != '\0'; ++pStr)
|
for (; *pStr != '\0'; ++pStr)
|
||||||
{
|
{
|
||||||
if (result > (limitCheck / base)) return false;
|
if (result > (limitCheck / base)) return false;
|
||||||
|
if (!safeMultiply(result, result, base)) return false;
|
||||||
switch (*pStr)
|
switch (*pStr)
|
||||||
{
|
{
|
||||||
case '0': case '1': case '2': case '3':
|
case '0': case '1': case '2': case '3':
|
||||||
case '4': case '5': case '6': case '7':
|
case '4': case '5': case '6': case '7':
|
||||||
{
|
add = (*pStr - '0');
|
||||||
unsigned char add = (*pStr - '0');
|
|
||||||
if ((limitCheck - result) < add) return false;
|
|
||||||
result = result * base + add;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '8': case '9':
|
case '8': case '9':
|
||||||
if ((base == 10) || (base == 0x10))
|
if ((base == 10) || (base == 0x10)) add = (*pStr - '0');
|
||||||
{
|
|
||||||
unsigned char add = (*pStr - '0');
|
|
||||||
if ((limitCheck - result) < add) return false;
|
|
||||||
result = result * base + add;
|
|
||||||
}
|
|
||||||
else return false;
|
else return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||||
{
|
|
||||||
if (base != 0x10) return false;
|
if (base != 0x10) return false;
|
||||||
unsigned char add = (*pStr - 'a');
|
add = (*pStr - 'a') + 10;
|
||||||
if ((limitCheck - result) < add) return false;
|
|
||||||
result = result * base + (10 + add);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||||
{
|
|
||||||
if (base != 0x10) return false;
|
if (base != 0x10) return false;
|
||||||
unsigned char add = (*pStr - 'A');
|
add = (*pStr - 'A') + 10;
|
||||||
if ((limitCheck - result) < add) return false;
|
|
||||||
result = result * base + (10 + add);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '.':
|
case '.':
|
||||||
@@ -250,6 +273,8 @@ bool strToInt(const char* pStr, I& outResult, short base, char thSep = ',')
|
|||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if ((limitCheck - static_cast<uintmax_t>(result)) < add) return false;
|
||||||
|
result += add;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (negative && (base == 10))
|
if (negative && (base == 10))
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ using Poco::MemoryInputStream;
|
|||||||
using Poco::Stopwatch;
|
using Poco::Stopwatch;
|
||||||
using Poco::RangeException;
|
using Poco::RangeException;
|
||||||
using Poco::isIntOverflow;
|
using Poco::isIntOverflow;
|
||||||
|
using Poco::safeMultiply;
|
||||||
using Poco::isSafeIntCast;
|
using Poco::isSafeIntCast;
|
||||||
using Poco::safeIntCast;
|
using Poco::safeIntCast;
|
||||||
using Poco::FPEnvironment;
|
using Poco::FPEnvironment;
|
||||||
@@ -915,121 +916,6 @@ void StringTest::testNumericLocale()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StringTest::benchmarkStrToInt()
|
|
||||||
{
|
|
||||||
Poco::Stopwatch sw;
|
|
||||||
std::string num = "123456789";
|
|
||||||
int res;
|
|
||||||
sw.start();
|
|
||||||
for (int i = 0; i < 1000000; ++i) parseStream(num, res);
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "parseStream Number: " << res << std::endl;
|
|
||||||
double timeStream = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
char* pC = 0;
|
|
||||||
sw.restart();
|
|
||||||
for (int i = 0; i < 1000000; ++i) res = std::strtol(num.c_str(), &pC, 10);
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "std::strtol Number: " << res << std::endl;
|
|
||||||
double timeStrtol = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
sw.restart();
|
|
||||||
for (int i = 0; i < 1000000; ++i) strToInt(num.c_str(), res, 10);
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "strToInt Number: " << res << std::endl;
|
|
||||||
double timeStrToInt = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
sw.restart();
|
|
||||||
for (int i = 0; i < 1000000; ++i) std::sscanf(num.c_str(), "%d", &res);
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "sscanf Number: " << res << std::endl;
|
|
||||||
double timeScanf = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
int graph;
|
|
||||||
std::cout << std::endl << "Timing and speedup relative to I/O stream:" << std::endl << std::endl;
|
|
||||||
std::cout << std::setw(14) << "Stream:\t" << std::setw(10) << std::setfill(' ') << timeStream << "[ms]" << std::endl;
|
|
||||||
|
|
||||||
std::cout << std::setw(14) << "std::strtol:\t" << std::setw(10) << std::setfill(' ') << timeStrtol << "[ms]" <<
|
|
||||||
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStrtol) << '\t' ;
|
|
||||||
graph = (int) (timeStream / timeStrtol); for (int i = 0; i < graph; ++i) std::cout << '|';
|
|
||||||
|
|
||||||
std::cout << std::endl << std::setw(14) << "strToInt:\t" << std::setw(10) << std::setfill(' ') << timeStrToInt << "[ms]" <<
|
|
||||||
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStrToInt) << '\t' ;
|
|
||||||
graph = (int) (timeStream / timeStrToInt); for (int i = 0; i < graph; ++i) std::cout << '|';
|
|
||||||
|
|
||||||
std::cout << std::endl << std::setw(14) << "std::sscanf:\t" << std::setw(10) << std::setfill(' ') << timeScanf << "[ms]" <<
|
|
||||||
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeScanf) << '\t' ;
|
|
||||||
graph = (int) (timeStream / timeScanf); for (int i = 0; i < graph; ++i) std::cout << '|';
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void StringTest::benchmarkStrToFloat()
|
|
||||||
{
|
|
||||||
Poco::Stopwatch sw;
|
|
||||||
std::string num = "1.0372157551632929e-112";
|
|
||||||
std::cout << "The Number: " << num << std::endl;
|
|
||||||
double res;
|
|
||||||
sw.start();
|
|
||||||
for (int i = 0; i < 1000000; ++i) parseStream(num, res);
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "parseStream Number: " << std::setprecision(std::numeric_limits<double>::digits10) << res << std::endl;
|
|
||||||
double timeStream = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
// standard strtod
|
|
||||||
char* pC = 0;
|
|
||||||
sw.restart();
|
|
||||||
for (int i = 0; i < 1000000; ++i) res = std::strtod(num.c_str(), &pC);
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "std::strtod Number: " << res << std::endl;
|
|
||||||
double timeStdStrtod = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
// POCO Way
|
|
||||||
sw.restart();
|
|
||||||
char ou = 0;
|
|
||||||
for (int i = 0; i < 1000000; ++i) strToDouble(num, res, ou);
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "strToDouble Number: " << res << std::endl;
|
|
||||||
double timeStrToDouble = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
// standard sscanf
|
|
||||||
sw.restart();
|
|
||||||
for (int i = 0; i < 1000000; ++i) std::sscanf(num.c_str(), "%lf", &res);
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "sscanf Number: " << res << std::endl;
|
|
||||||
double timeScanf = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
// double-conversion Strtod
|
|
||||||
sw.restart();
|
|
||||||
for (int i = 0; i < 1000000; ++i) strToDouble(num.c_str());
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "Strtod Number: " << res << std::endl;
|
|
||||||
double timeStrtod = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
int graph;
|
|
||||||
std::cout << std::endl << "Timing and speedup relative to I/O stream:" << std::endl << std::endl;
|
|
||||||
std::cout << std::setw(14) << "Stream:\t" << std::setw(10) << std::setfill(' ') << std::setprecision(4) << timeStream << "[ms]" << std::endl;
|
|
||||||
|
|
||||||
std::cout << std::setw(14) << "std::strtod:\t" << std::setw(10) << std::setfill(' ') << timeStdStrtod << "[ms]" <<
|
|
||||||
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStdStrtod) << '\t' ;
|
|
||||||
graph = (int) (timeStream / timeStdStrtod); for (int i = 0; i < graph; ++i) std::cout << '#';
|
|
||||||
|
|
||||||
std::cout << std::endl << std::setw(14) << "strToDouble:\t" << std::setw(10) << std::setfill(' ') << timeStrToDouble << "[ms]" <<
|
|
||||||
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStrToDouble) << '\t' ;
|
|
||||||
graph = (int) (timeStream / timeStrToDouble); for (int i = 0; i < graph; ++i) std::cout << '#';
|
|
||||||
|
|
||||||
std::cout << std::endl << std::setw(14) << "std::sscanf:\t" << std::setw(10) << std::setfill(' ') << timeScanf << "[ms]" <<
|
|
||||||
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeScanf) << '\t' ;
|
|
||||||
graph = (int) (timeStream / timeScanf); for (int i = 0; i < graph; ++i) std::cout << '#';
|
|
||||||
|
|
||||||
std::cout << std::endl << std::setw(14) << "StrtoD:\t" << std::setw(10) << std::setfill(' ') << timeScanf << "[ms]" <<
|
|
||||||
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStrtod) << '\t' ;
|
|
||||||
graph = (int) (timeStream / timeStrtod); for (int i = 0; i < graph; ++i) std::cout << '#';
|
|
||||||
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void StringTest::testIntToString()
|
void StringTest::testIntToString()
|
||||||
{
|
{
|
||||||
//intToStr(T number, unsigned short base, std::string& result, bool prefix = false, int width = -1, char fill = ' ', char thSep = 0)
|
//intToStr(T number, unsigned short base, std::string& result, bool prefix = false, int width = -1, char fill = ' ', char thSep = 0)
|
||||||
@@ -1325,6 +1211,15 @@ void StringTest::testNumericStringLimit()
|
|||||||
numericStringLowerLimit<int16_t, int8_t>();
|
numericStringLowerLimit<int16_t, int8_t>();
|
||||||
numericStringLowerLimit<int32_t, int16_t>();
|
numericStringLowerLimit<int32_t, int16_t>();
|
||||||
numericStringLowerLimit<int64_t, int32_t>();
|
numericStringLowerLimit<int64_t, int32_t>();
|
||||||
|
|
||||||
|
multiplyOverflow<int8_t>();
|
||||||
|
multiplyOverflow<uint8_t>();
|
||||||
|
multiplyOverflow<int16_t>();
|
||||||
|
multiplyOverflow<uint16_t>();
|
||||||
|
multiplyOverflow<int32_t>();
|
||||||
|
multiplyOverflow<uint32_t>();
|
||||||
|
multiplyOverflow<int64_t>();
|
||||||
|
multiplyOverflow<uint64_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1348,63 +1243,6 @@ void formatSprintf(double value, std::string& str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StringTest::benchmarkFloatToStr()
|
|
||||||
{
|
|
||||||
Poco::Stopwatch sw;
|
|
||||||
double val = 1.0372157551632929e-112;
|
|
||||||
std::cout << "The Number: " << std::setprecision(std::numeric_limits<double>::digits10) << val << std::endl;
|
|
||||||
std::string str;
|
|
||||||
sw.start();
|
|
||||||
for (int i = 0; i < 1000000; ++i) formatStream(val, str);
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "formatStream Number: " << str << std::endl;
|
|
||||||
double timeStream = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
// standard sprintf
|
|
||||||
str = "";
|
|
||||||
sw.restart();
|
|
||||||
for (int i = 0; i < 1000000; ++i) formatSprintf(val, str);
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "std::sprintf Number: " << str << std::endl;
|
|
||||||
double timeSprintf = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
// POCO Way (via double-conversion)
|
|
||||||
// no padding
|
|
||||||
sw.restart();
|
|
||||||
char buffer[POCO_MAX_FLT_STRING_LEN];
|
|
||||||
for (int i = 0; i < 1000000; ++i) doubleToStr(buffer, POCO_MAX_FLT_STRING_LEN, val);
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "doubleToStr(char) Number: " << buffer << std::endl;
|
|
||||||
double timeDoubleToStrChar = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
// with padding
|
|
||||||
str = "";
|
|
||||||
sw.restart();
|
|
||||||
for (int i = 0; i < 1000000; ++i) doubleToStr(str, val);
|
|
||||||
sw.stop();
|
|
||||||
std::cout << "doubleToStr(std::string) Number: " << str << std::endl;
|
|
||||||
double timeDoubleToStrString = sw.elapsed() / 1000.0;
|
|
||||||
|
|
||||||
int graph;
|
|
||||||
std::cout << std::endl << "Timing and speedup relative to I/O stream:" << std::endl << std::endl;
|
|
||||||
std::cout << std::setw(14) << "Stream:\t" << std::setw(10) << std::setfill(' ') << std::setprecision(4) << timeStream << "[ms]" << std::endl;
|
|
||||||
|
|
||||||
std::cout << std::setw(14) << "sprintf:\t" << std::setw(10) << std::setfill(' ') << timeSprintf << "[ms]" <<
|
|
||||||
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeSprintf) << '\t' ;
|
|
||||||
graph = (int) (timeStream / timeSprintf); for (int i = 0; i < graph; ++i) std::cout << '#';
|
|
||||||
|
|
||||||
std::cout << std::endl << std::setw(14) << "doubleToChar:\t" << std::setw(10) << std::setfill(' ') << timeDoubleToStrChar << "[ms]" <<
|
|
||||||
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeDoubleToStrChar) << '\t' ;
|
|
||||||
graph = (int) (timeStream / timeDoubleToStrChar); for (int i = 0; i < graph; ++i) std::cout << '#';
|
|
||||||
|
|
||||||
std::cout << std::endl << std::setw(14) << "doubleToString:\t" << std::setw(10) << std::setfill(' ') << timeDoubleToStrString << "[ms]" <<
|
|
||||||
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeDoubleToStrString) << '\t' ;
|
|
||||||
graph = (int) (timeStream / timeDoubleToStrString); for (int i = 0; i < graph; ++i) std::cout << '#';
|
|
||||||
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void StringTest::testJSONString()
|
void StringTest::testJSONString()
|
||||||
{
|
{
|
||||||
assertTrue (toJSON("\\", false) == "\\\\");
|
assertTrue (toJSON("\\", false) == "\\\\");
|
||||||
@@ -1471,6 +1309,201 @@ void StringTest::testJSONString()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void StringTest::conversionBenchmarks()
|
||||||
|
{
|
||||||
|
std::cout << std::endl << "===================" << std::endl;
|
||||||
|
benchmarkFloatToStr();
|
||||||
|
std::cout << "===================" << std::endl << std::endl;
|
||||||
|
std::cout << "===================" << std::endl;
|
||||||
|
benchmarkStrToFloat();
|
||||||
|
std::cout << "===================" << std::endl << std::endl;
|
||||||
|
std::cout << "===================" << std::endl;
|
||||||
|
benchmarkStrToInt();
|
||||||
|
std::cout << "===================" << std::endl << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void StringTest::benchmarkFloatToStr()
|
||||||
|
{
|
||||||
|
Poco::Stopwatch sw;
|
||||||
|
double val = 1.0372157551632929e-112;
|
||||||
|
std::cout << "The Number: " << std::setprecision(std::numeric_limits<double>::digits10) << val << std::endl;
|
||||||
|
std::string str;
|
||||||
|
sw.start();
|
||||||
|
for (int i = 0; i < 1000000; ++i) formatStream(val, str);
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "formatStream Number: " << str << std::endl;
|
||||||
|
double timeStream = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
// standard sprintf
|
||||||
|
str = "";
|
||||||
|
sw.restart();
|
||||||
|
for (int i = 0; i < 1000000; ++i) formatSprintf(val, str);
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "std::sprintf Number: " << str << std::endl;
|
||||||
|
double timeSprintf = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
// POCO Way (via double-conversion)
|
||||||
|
// no padding
|
||||||
|
sw.restart();
|
||||||
|
char buffer[POCO_MAX_FLT_STRING_LEN];
|
||||||
|
for (int i = 0; i < 1000000; ++i) doubleToStr(buffer, POCO_MAX_FLT_STRING_LEN, val);
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "doubleToStr(char) Number: " << buffer << std::endl;
|
||||||
|
double timeDoubleToStrChar = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
// with padding
|
||||||
|
str = "";
|
||||||
|
sw.restart();
|
||||||
|
for (int i = 0; i < 1000000; ++i) doubleToStr(str, val);
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "doubleToStr(std::string) Number: " << str << std::endl;
|
||||||
|
double timeDoubleToStrString = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
int graph;
|
||||||
|
std::cout << std::endl << "Timing and speedup relative to I/O stream:" << std::endl << std::endl;
|
||||||
|
std::cout << std::setw(14) << "Stream:\t" << std::setw(10) << std::setfill(' ') << std::setprecision(4) << timeStream << "[ms]" << std::endl;
|
||||||
|
|
||||||
|
std::cout << std::setw(14) << "sprintf:\t" << std::setw(10) << std::setfill(' ') << timeSprintf << "[ms]" <<
|
||||||
|
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeSprintf) << '\t' ;
|
||||||
|
graph = (int) (timeStream / timeSprintf); for (int i = 0; i < graph; ++i) std::cout << '#';
|
||||||
|
|
||||||
|
std::cout << std::endl << std::setw(14) << "doubleToChar:\t" << std::setw(10) << std::setfill(' ') << timeDoubleToStrChar << "[ms]" <<
|
||||||
|
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeDoubleToStrChar) << '\t' ;
|
||||||
|
graph = (int) (timeStream / timeDoubleToStrChar); for (int i = 0; i < graph; ++i) std::cout << '#';
|
||||||
|
|
||||||
|
std::cout << std::endl << std::setw(14) << "doubleToString:\t" << std::setw(10) << std::setfill(' ') << timeDoubleToStrString << "[ms]" <<
|
||||||
|
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeDoubleToStrString) << '\t' ;
|
||||||
|
graph = (int) (timeStream / timeDoubleToStrString); for (int i = 0; i < graph; ++i) std::cout << '#';
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void StringTest::benchmarkStrToInt()
|
||||||
|
{
|
||||||
|
Poco::Stopwatch sw;
|
||||||
|
std::string num = "123456789";
|
||||||
|
int res[4] = {};
|
||||||
|
sw.start();
|
||||||
|
for (int i = 0; i < 1000000; ++i) parseStream(num, res[0]);
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "parseStream Number: " << res[0] << std::endl;
|
||||||
|
double timeStream = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
char* pC = 0;
|
||||||
|
sw.restart();
|
||||||
|
for (int i = 0; i < 1000000; ++i) res[1] = std::strtol(num.c_str(), &pC, 10);
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "std::strtol Number: " << res[1] << std::endl;
|
||||||
|
double timeStrtol = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
sw.restart();
|
||||||
|
for (int i = 0; i < 1000000; ++i) strToInt(num.c_str(), res[2], 10);
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "strToInt Number: " << res[2] << std::endl;
|
||||||
|
double timeStrToInt = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
sw.restart();
|
||||||
|
for (int i = 0; i < 1000000; ++i) std::sscanf(num.c_str(), "%d", &res[3]);
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "sscanf Number: " << res[3] << std::endl;
|
||||||
|
double timeScanf = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
assertEqual (res[0], res[1]);
|
||||||
|
assertEqual (res[1], res[2]);
|
||||||
|
assertEqual (res[2], res[3]);
|
||||||
|
|
||||||
|
int graph;
|
||||||
|
std::cout << std::endl << "Timing and speedup relative to I/O stream:" << std::endl << std::endl;
|
||||||
|
std::cout << std::setw(14) << "Stream:\t" << std::setw(10) << std::setfill(' ') << timeStream << "[ms]" << std::endl;
|
||||||
|
|
||||||
|
std::cout << std::setw(14) << "std::strtol:\t" << std::setw(10) << std::setfill(' ') << timeStrtol << "[ms]" <<
|
||||||
|
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStrtol) << '\t' ;
|
||||||
|
graph = (int) (timeStream / timeStrtol); for (int i = 0; i < graph; ++i) std::cout << '|';
|
||||||
|
|
||||||
|
std::cout << std::endl << std::setw(14) << "strToInt:\t" << std::setw(10) << std::setfill(' ') << timeStrToInt << "[ms]" <<
|
||||||
|
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStrToInt) << '\t' ;
|
||||||
|
graph = (int) (timeStream / timeStrToInt); for (int i = 0; i < graph; ++i) std::cout << '|';
|
||||||
|
|
||||||
|
std::cout << std::endl << std::setw(14) << "std::sscanf:\t" << std::setw(10) << std::setfill(' ') << timeScanf << "[ms]" <<
|
||||||
|
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeScanf) << '\t' ;
|
||||||
|
graph = (int) (timeStream / timeScanf); for (int i = 0; i < graph; ++i) std::cout << '|';
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void StringTest::benchmarkStrToFloat()
|
||||||
|
{
|
||||||
|
double res[5] = {};
|
||||||
|
Poco::Stopwatch sw;
|
||||||
|
std::string num = "1.0372157551632929e-112";
|
||||||
|
std::cout << "The Number: " << num << std::endl;
|
||||||
|
sw.start();
|
||||||
|
for (int i = 0; i < 1000000; ++i) parseStream(num, res[0]);
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "parseStream Number: " << std::setprecision(std::numeric_limits<double>::digits10) << res[0] << std::endl;
|
||||||
|
double timeStream = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
// standard strtod
|
||||||
|
char* pC = 0;
|
||||||
|
sw.restart();
|
||||||
|
for (int i = 0; i < 1000000; ++i) res[1] = std::strtod(num.c_str(), &pC);
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "std::strtod Number: " << res[1] << std::endl;
|
||||||
|
double timeStdStrtod = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
// POCO Way
|
||||||
|
sw.restart();
|
||||||
|
char ou = 0;
|
||||||
|
for (int i = 0; i < 1000000; ++i) strToDouble(num, res[2], ou);
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "Poco::strToDouble(const string&, double&) Number: " << res[2] << std::endl;
|
||||||
|
double timeStrToDouble = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
sw.restart();
|
||||||
|
for (int i = 0; i < 1000000; ++i) res[3] = strToDouble(num.c_str());
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "Poco::strToDouble(const char*) Number: " << res[3] << std::endl;
|
||||||
|
double timeStrtoD = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
// standard sscanf
|
||||||
|
sw.restart();
|
||||||
|
for (int i = 0; i < 1000000; ++i) std::sscanf(num.c_str(), "%lf", &res[4]);
|
||||||
|
sw.stop();
|
||||||
|
std::cout << "sscanf Number: " << res[4] << std::endl;
|
||||||
|
double timeScanf = sw.elapsed() / 1000.0;
|
||||||
|
|
||||||
|
assertEqual (res[0], res[1]);
|
||||||
|
assertEqual (res[1], res[2]);
|
||||||
|
assertEqual (res[2], res[3]);
|
||||||
|
assertEqual (res[3], res[4]);
|
||||||
|
|
||||||
|
int graph;
|
||||||
|
std::cout << std::endl << "Timing and speedup relative to I/O stream:" << std::endl << std::endl;
|
||||||
|
std::cout << std::setw(14) << "Stream:\t" << std::setw(10) << std::setfill(' ') << std::setprecision(4) << timeStream << "[ms]" << std::endl;
|
||||||
|
|
||||||
|
std::cout << std::setw(14) << "std::strtod:\t" << std::setw(10) << std::setfill(' ') << timeStdStrtod << "[ms]" <<
|
||||||
|
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStdStrtod) << '\t' ;
|
||||||
|
graph = (int) (timeStream / timeStdStrtod); for (int i = 0; i < graph; ++i) std::cout << '#';
|
||||||
|
|
||||||
|
std::cout << std::endl << std::setw(14) << "strToDouble:\t" << std::setw(10) << std::setfill(' ') << timeStrToDouble << "[ms]" <<
|
||||||
|
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStrToDouble) << '\t' ;
|
||||||
|
graph = (int) (timeStream / timeStrToDouble); for (int i = 0; i < graph; ++i) std::cout << '#';
|
||||||
|
|
||||||
|
std::cout << std::endl << std::setw(14) << "strToDouble:\t" << std::setw(10) << std::setfill(' ') << timeScanf << "[ms]" <<
|
||||||
|
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStrtoD) << '\t' ;
|
||||||
|
graph = (int) (timeStream / timeStrtoD); for (int i = 0; i < graph; ++i) std::cout << '#';
|
||||||
|
|
||||||
|
std::cout << std::endl << std::setw(14) << "std::sscanf:\t" << std::setw(10) << std::setfill(' ') << timeScanf << "[ms]" <<
|
||||||
|
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeScanf) << '\t' ;
|
||||||
|
graph = (int) (timeStream / timeScanf); for (int i = 0; i < graph; ++i) std::cout << '#';
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void StringTest::setUp()
|
void StringTest::setUp()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -1510,12 +1543,10 @@ CppUnit::Test* StringTest::suite()
|
|||||||
CppUnit_addTest(pSuite, StringTest, testNumericStringLimit);
|
CppUnit_addTest(pSuite, StringTest, testNumericStringLimit);
|
||||||
CppUnit_addTest(pSuite, StringTest, testStringToFloatError);
|
CppUnit_addTest(pSuite, StringTest, testStringToFloatError);
|
||||||
CppUnit_addTest(pSuite, StringTest, testNumericLocale);
|
CppUnit_addTest(pSuite, StringTest, testNumericLocale);
|
||||||
//CppUnit_addTest(pSuite, StringTest, benchmarkStrToFloat);
|
|
||||||
//CppUnit_addTest(pSuite, StringTest, benchmarkStrToInt);
|
|
||||||
CppUnit_addTest(pSuite, StringTest, testIntToString);
|
CppUnit_addTest(pSuite, StringTest, testIntToString);
|
||||||
CppUnit_addTest(pSuite, StringTest, testFloatToString);
|
CppUnit_addTest(pSuite, StringTest, testFloatToString);
|
||||||
//CppUnit_addTest(pSuite, StringTest, benchmarkFloatToStr);
|
|
||||||
CppUnit_addTest(pSuite, StringTest, testJSONString);
|
CppUnit_addTest(pSuite, StringTest, testJSONString);
|
||||||
|
CppUnit_addTest(pSuite, StringTest, conversionBenchmarks);
|
||||||
|
|
||||||
return pSuite;
|
return pSuite;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "Poco/NumericString.h"
|
#include "Poco/NumericString.h"
|
||||||
#include "Poco/MemoryStream.h"
|
#include "Poco/MemoryStream.h"
|
||||||
#include "Poco/NumberFormatter.h"
|
#include "Poco/NumberFormatter.h"
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
|
||||||
class StringTest: public CppUnit::TestCase
|
class StringTest: public CppUnit::TestCase
|
||||||
@@ -53,12 +54,14 @@ public:
|
|||||||
void testNumericStringLimit();
|
void testNumericStringLimit();
|
||||||
void testStringToFloatError();
|
void testStringToFloatError();
|
||||||
void testNumericLocale();
|
void testNumericLocale();
|
||||||
void benchmarkStrToFloat();
|
|
||||||
void benchmarkStrToInt();
|
|
||||||
|
|
||||||
void testIntToString();
|
void testIntToString();
|
||||||
void testFloatToString();
|
void testFloatToString();
|
||||||
|
|
||||||
|
void conversionBenchmarks();
|
||||||
void benchmarkFloatToStr();
|
void benchmarkFloatToStr();
|
||||||
|
void benchmarkStrToFloat();
|
||||||
|
void benchmarkStrToInt();
|
||||||
|
|
||||||
void testJSONString();
|
void testJSONString();
|
||||||
|
|
||||||
@@ -128,7 +131,6 @@ private:
|
|||||||
assertFalse(s == std::numeric_limits<Smaller>::min());
|
assertFalse(s == std::numeric_limits<Smaller>::min());
|
||||||
assertTrue(Poco::strToInt<Smaller>(val, s, 10));
|
assertTrue(Poco::strToInt<Smaller>(val, s, 10));
|
||||||
assertTrue (s == std::numeric_limits<Smaller>::min());
|
assertTrue (s == std::numeric_limits<Smaller>::min());
|
||||||
assertTrue(s == std::numeric_limits<Smaller>::min());
|
|
||||||
--l; val = Poco::NumberFormatter::format(l);
|
--l; val = Poco::NumberFormatter::format(l);
|
||||||
assertFalse(Poco::strToInt<Smaller>(val, s, 10));
|
assertFalse(Poco::strToInt<Smaller>(val, s, 10));
|
||||||
--l; val = Poco::NumberFormatter::format(l);
|
--l; val = Poco::NumberFormatter::format(l);
|
||||||
@@ -143,6 +145,21 @@ private:
|
|||||||
assertFalse(Poco::strToInt<Smaller>(val, s, 10));
|
assertFalse(Poco::strToInt<Smaller>(val, s, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void multiplyOverflow()
|
||||||
|
{
|
||||||
|
T m = static_cast<T>(10);
|
||||||
|
T t = 0;
|
||||||
|
T f = std::numeric_limits<T>::max()/m;
|
||||||
|
assertTrue (Poco::safeMultiply(t, f, m));
|
||||||
|
f += 1;
|
||||||
|
assertFalse (Poco::safeMultiply(t, f, m));
|
||||||
|
f = std::numeric_limits<T>::min()/m;
|
||||||
|
assertTrue (Poco::safeMultiply(t, f, m));
|
||||||
|
f -= 1;
|
||||||
|
assertFalse (Poco::safeMultiply(t, f, m));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool parseStream(const std::string& s, T& value)
|
bool parseStream(const std::string& s, T& value)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user