mirror of
https://github.com/pocoproject/poco.git
synced 2025-01-19 08:46:41 +01:00
- strToInt benchmark and more optimization
- warnmsg macro in CppUnit
This commit is contained in:
parent
bf74a4be8b
commit
8a4eafd05a
@ -1,7 +1,7 @@
|
||||
This is the changelog file for the POCO C++ Libraries.
|
||||
|
||||
|
||||
Release 1.5.0 (2012-09-??)
|
||||
Release 1.5.0 (2012-10-01)
|
||||
==========================
|
||||
|
||||
- added JSON
|
||||
@ -43,6 +43,8 @@ Release 1.5.0 (2012-09-??)
|
||||
- fixed SF#307 Detect the SQL driver type at run time
|
||||
- added VS 2012 Projects/Solutions
|
||||
- enhanced and accelerated numeric parsing for integers and floats
|
||||
- fixed SF#590 Segfault on FreeBSD when stack size not rounded
|
||||
- added warn function and warnmsg macro in CppUnit
|
||||
|
||||
Release 1.4.4p1 (2012-??-??)
|
||||
============================
|
||||
|
@ -162,9 +162,9 @@ protected:
|
||||
long lineNumber = CppUnitException::CPPUNIT_UNKNOWNLINENUMBER,
|
||||
const std::string& fileName = CppUnitException::CPPUNIT_UNKNOWNFILENAME);
|
||||
|
||||
void warn(const std::string& message = "",
|
||||
long lineNumber = CppUnitException::CPPUNIT_UNKNOWNLINENUMBER,
|
||||
const std::string& fileName = CppUnitException::CPPUNIT_UNKNOWNFILENAME);
|
||||
void warn(const std::string& message = "",
|
||||
long lineNumber = CppUnitException::CPPUNIT_UNKNOWNLINENUMBER,
|
||||
const std::string& fileName = CppUnitException::CPPUNIT_UNKNOWNFILENAME);
|
||||
|
||||
|
||||
private:
|
||||
@ -247,6 +247,9 @@ inline std::string TestCase::toString()
|
||||
#define failmsg(msg) \
|
||||
(this->fail(msg, __LINE__, __FILE__))
|
||||
|
||||
#define warnmsg(msg) \
|
||||
(this->fail(msg, __LINE__, __FILE__))
|
||||
|
||||
|
||||
} // namespace CppUnit
|
||||
|
||||
|
@ -57,138 +57,106 @@ namespace Poco {
|
||||
|
||||
|
||||
inline char decimalSeparator()
|
||||
/// Returns decimal separator from global locale or
|
||||
/// default '.' for platforms where locale is unavailable.
|
||||
/// Returns decimal separator from global locale or
|
||||
/// default '.' for platforms where locale is unavailable.
|
||||
{
|
||||
#if !defined(POCO_NO_LOCALE)
|
||||
return std::use_facet<std::numpunct<char> >(std::locale()).decimal_point();
|
||||
return std::use_facet<std::numpunct<char> >(std::locale()).decimal_point();
|
||||
#else
|
||||
return '.';
|
||||
return '.';
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
inline char thousandSeparator()
|
||||
/// Returns thousand separator from global locale or
|
||||
/// default ',' for platforms where locale is unavailable.
|
||||
/// Returns thousand separator from global locale or
|
||||
/// default ',' for platforms where locale is unavailable.
|
||||
{
|
||||
#if !defined(POCO_NO_LOCALE)
|
||||
return std::use_facet<std::numpunct<char> >(std::locale()).thousands_sep();
|
||||
return std::use_facet<std::numpunct<char> >(std::locale()).thousands_sep();
|
||||
#else
|
||||
return ',';
|
||||
return ',';
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
template <typename I>
|
||||
bool strToInt(const char* pStr, I& result, short base = -1)
|
||||
/// Converts zero-terminated array to integer number;
|
||||
/// If base is equal to -1, this functin will try to determine
|
||||
/// the numeric base (using '0' prefix for octal, '0x' for
|
||||
/// hexadecimal and no prefix for decimal).
|
||||
/// Thousand separators are recognized for base10 and the locale
|
||||
/// and silently skipped but not verified for correct positioning.
|
||||
/// Returns true if succesful. If parsing was unsuccesful,
|
||||
/// the return value is false with result value undetermined.
|
||||
bool strToInt(const char* pStr, I& result, short base)
|
||||
/// 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.
|
||||
/// Function returns true if succesful. If parsing was unsuccesful,
|
||||
/// the return value is false with the result value undetermined.
|
||||
{
|
||||
if (!std::numeric_limits<I>::is_integer) return false;
|
||||
|
||||
if (!pStr || (pStr && *pStr == '\0')) return false;
|
||||
while ((*pStr != '\0') && (*pStr == ' ')) ++pStr;
|
||||
if (!pStr) return false;
|
||||
while (*pStr == ' ') ++pStr;
|
||||
if (*pStr == '\0') return false;
|
||||
|
||||
char sign = 1;
|
||||
|
||||
if (*pStr == '-')
|
||||
I sign = 1;
|
||||
if ((base == 10) && (*pStr == '-'))
|
||||
{
|
||||
++pStr;
|
||||
sign = -1;
|
||||
++pStr;
|
||||
}
|
||||
else if (*pStr == '+') ++pStr;
|
||||
if (*pStr == '\0') return false;
|
||||
|
||||
result = 0;
|
||||
|
||||
if (*pStr == '0')
|
||||
{
|
||||
while ((*pStr != '\0') && (*pStr == '0')) ++pStr;
|
||||
if (*pStr == '\0')
|
||||
{
|
||||
result = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((*pStr == 'x') || (*pStr == 'X'))
|
||||
{
|
||||
base = 0x10;
|
||||
++pStr;
|
||||
if (*pStr == '\0') return false;
|
||||
}
|
||||
else if (base == -1) base = 010;
|
||||
}
|
||||
else if (base == -1) base = 10;
|
||||
|
||||
while ((*pStr == '0'))
|
||||
{
|
||||
if (*pStr != '\0')
|
||||
{
|
||||
result = 0;
|
||||
return true;
|
||||
}
|
||||
++pStr;
|
||||
}
|
||||
if (*pStr == '\0') return false;
|
||||
|
||||
// parser states:
|
||||
const char STATE_SIGNIFICANT_DIGITS = 1;
|
||||
char state = 0;
|
||||
const char thSep = thousandSeparator();
|
||||
bool allowDigits = true;
|
||||
|
||||
result = 0;
|
||||
I limitCheck = std::numeric_limits<I>::max() / base;
|
||||
for (; *pStr != '\0'; ++pStr)
|
||||
{
|
||||
switch (*pStr)
|
||||
{
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case 'x': case 'X':
|
||||
if (base != 0x10) return false;
|
||||
|
||||
case '0':
|
||||
if (state < STATE_SIGNIFICANT_DIGITS) break;
|
||||
|
||||
case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7':
|
||||
if (allowDigits)
|
||||
{
|
||||
if (result > (std::numeric_limits<I>::max() / base)) return false;
|
||||
result = result * base + (*pStr - '0');
|
||||
}
|
||||
else return false;
|
||||
if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
|
||||
if (result > limitCheck) return false;
|
||||
result = result * base + (*pStr - '0');
|
||||
|
||||
break;
|
||||
|
||||
case '8': case '9':
|
||||
if (allowDigits && (base == 10 || base == 16))
|
||||
if ((base == 10) || (base == 0x10))
|
||||
{
|
||||
if (result > (std::numeric_limits<I>::max() / base)) return false;
|
||||
if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
|
||||
if (result > limitCheck) return false;
|
||||
result = result * base + (*pStr - '0');
|
||||
}
|
||||
else return false;
|
||||
|
||||
break;
|
||||
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
if (allowDigits && base == 16)
|
||||
{
|
||||
if (result > (std::numeric_limits<I>::max() / base)) return false;
|
||||
result = result * base + (10 + *pStr - 'a');
|
||||
}
|
||||
else return false;
|
||||
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');
|
||||
|
||||
break;
|
||||
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||
if (allowDigits && base == 16)
|
||||
{
|
||||
if (result > (std::numeric_limits<I>::max() / base)) return false;
|
||||
result = result * base + (10 + *pStr - 'A');
|
||||
}
|
||||
else
|
||||
return false;
|
||||
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');
|
||||
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
case 'u':
|
||||
case 'L':
|
||||
case 'l':
|
||||
allowDigits = false;
|
||||
break;
|
||||
goto done;
|
||||
|
||||
case '.':
|
||||
if ((base == 10) && (thSep == '.')) break;
|
||||
@ -199,23 +167,23 @@ bool strToInt(const char* pStr, I& result, short base = -1)
|
||||
else return false;
|
||||
|
||||
case ' ':
|
||||
if (base == 10) break;
|
||||
else return false;
|
||||
if ((base == 10) && (thSep == ' ')) break;
|
||||
goto done;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((base == 10) && (std::numeric_limits<I>::is_signed))
|
||||
result *= sign;
|
||||
done:
|
||||
if ((sign < 0) && (base == 10)) result *= sign;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template <typename I>
|
||||
bool strToInt(const std::string& str, I& result, short base = -1)
|
||||
bool strToInt(const std::string& str, I& result, short base)
|
||||
/// Converts string to integer number;
|
||||
/// This is a wrapper function, for details see see the
|
||||
/// bool strToInt(const char*, I&, short&) implementation.
|
||||
|
@ -564,24 +564,63 @@ void StringTest::testNumericLocale()
|
||||
} catch (std::runtime_error& ex)
|
||||
{
|
||||
std::cout << ex.what() << std::endl;
|
||||
warn ("Locale not found, skipping test");
|
||||
warnmsg ("Locale not found, skipping test");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool parseStream(const std::string& s, double& value)
|
||||
void StringTest::benchmarkStrToInt()
|
||||
{
|
||||
MemoryInputStream istr(s.data(), s.size());
|
||||
#if !defined(POCO_NO_LOCALE)
|
||||
istr.imbue(std::locale::classic());
|
||||
#endif
|
||||
istr >> value;
|
||||
return istr.eof() && !istr.fail();
|
||||
Poco::Stopwatch sw;
|
||||
int number = 123456789;
|
||||
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%c", &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::benchmark()
|
||||
void StringTest::benchmarkStrToFloat()
|
||||
{
|
||||
Poco::Stopwatch sw;
|
||||
double number = 1.23456e-123;
|
||||
@ -599,7 +638,7 @@ void StringTest::benchmark()
|
||||
sw.stop();
|
||||
std::cout << "std::strtod Number: " << res << std::endl;
|
||||
double timeStrtod = sw.elapsed() / 1000.0;
|
||||
|
||||
|
||||
sw.restart();
|
||||
char ou = 0;
|
||||
for (int i = 0; i < 1000000; ++i) strToFloat(num.c_str(), res, ou);
|
||||
@ -662,7 +701,8 @@ CppUnit::Test* StringTest::suite()
|
||||
CppUnit_addTest(pSuite, StringTest, testStringToFloat);
|
||||
CppUnit_addTest(pSuite, StringTest, testStringToFloatError);
|
||||
CppUnit_addTest(pSuite, StringTest, testNumericLocale);
|
||||
CppUnit_addTest(pSuite, StringTest, benchmark);
|
||||
//CppUnit_addTest(pSuite, StringTest, benchmarkStrToFloat);
|
||||
//CppUnit_addTest(pSuite, StringTest, benchmarkStrToInt);
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
@ -66,7 +66,8 @@ public:
|
||||
void testStringToFloat();
|
||||
void testStringToFloatError();
|
||||
void testNumericLocale();
|
||||
void benchmark();
|
||||
void benchmarkStrToFloat();
|
||||
void benchmarkStrToInt();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
@ -80,43 +81,55 @@ private:
|
||||
{
|
||||
T result = 0;
|
||||
if (123 <= std::numeric_limits<T>::max())
|
||||
assert(strToInt("123", result)); assert(result == 123);
|
||||
assert(strToInt("123", result, 10)); assert(result == 123);
|
||||
|
||||
assert(strToInt("0", result)); assert(result == 0);
|
||||
assert(strToInt("000", result)); assert(result == 0);
|
||||
assert(strToInt("0", result, 10)); assert(result == 0);
|
||||
assert(strToInt("000", result, 10)); assert(result == 0);
|
||||
|
||||
if (123 < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt(" 123 ", result)); assert(result == 123); }
|
||||
{ assert(strToInt(" 123 ", result, 10)); assert(result == 123); }
|
||||
if (123 < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt(" 123", result)); assert(result == 123); }
|
||||
{ assert(strToInt(" 123", result, 10)); assert(result == 123); }
|
||||
if (123 < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt("123 ", result)); assert(result == 123); }
|
||||
{ assert(strToInt("123 ", result, 10)); assert(result == 123); }
|
||||
if (std::numeric_limits<T>::is_signed && (-123 > std::numeric_limits<T>::min()))
|
||||
{ assert(strToInt("-123", result)); assert(result == -123); }
|
||||
{ assert(strToInt("-123", result, 10)); assert(result == -123); }
|
||||
if (0x123 < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt("123", result, 0x10)); assert(result == 0x123); }
|
||||
if (0x12ab < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt("12AB", result, 0x10)); assert(result == 0x12ab); }
|
||||
if (0x12ab < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt("0X12AB", result)); assert(result == 0x12ab); }
|
||||
{ assert(strToInt("0X12AB", result, 0x10)); assert(result == 0x12ab); }
|
||||
if (0x12ab < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt("0x12AB", result)); assert(result == 0x12ab); }
|
||||
{ assert(strToInt("0x12AB", result, 0x10)); assert(result == 0x12ab); }
|
||||
if (0x12ab < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt("0x12aB", result)); assert(result == 0x12ab); }
|
||||
{ assert(strToInt("0x12aB", result, 0x10)); assert(result == 0x12ab); }
|
||||
if (0x98fe < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt("0X98Fe", result)); assert(result == 0x98fe); }
|
||||
{ assert(strToInt("0X98Fe", result, 0x10)); assert(result == 0x98fe); }
|
||||
if (123 < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt("0x0", result)); assert(result == 0); }
|
||||
{ assert(strToInt("0x0", result, 0x10)); assert(result == 0); }
|
||||
if (123 < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt("00", result, 0x10)); assert(result == 0); }
|
||||
if (0123 < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt("123", result, 010)); assert(result == 0123); }
|
||||
if (0123 < std::numeric_limits<T>::max())
|
||||
{ assert(strToInt("0123", result)); assert(result == 0123); }
|
||||
{ assert(strToInt("0123", result, 010)); assert(result == 0123); }
|
||||
|
||||
assert(strToInt("0", result, 010)); assert(result == 0);
|
||||
assert(strToInt("000", result)); assert(result == 0);
|
||||
assert(strToInt("000", result, 010)); assert(result == 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool parseStream(const std::string& s, T& value)
|
||||
{
|
||||
MemoryInputStream istr(s.data(), s.size());
|
||||
#if !defined(POCO_NO_LOCALE)
|
||||
istr.imbue(std::locale::classic());
|
||||
#endif
|
||||
istr >> value;
|
||||
return istr.eof() && !istr.fail();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user