diff --git a/CHANGELOG b/CHANGELOG index fb9124282..02a070e81 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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-??-??) ============================ diff --git a/CppUnit/include/CppUnit/TestCase.h b/CppUnit/include/CppUnit/TestCase.h index cd843f45b..1811d428d 100644 --- a/CppUnit/include/CppUnit/TestCase.h +++ b/CppUnit/include/CppUnit/TestCase.h @@ -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 diff --git a/Foundation/include/Poco/NumericString.h b/Foundation/include/Poco/NumericString.h index bb340ddd9..0b55b6991 100644 --- a/Foundation/include/Poco/NumericString.h +++ b/Foundation/include/Poco/NumericString.h @@ -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::locale()).decimal_point(); + return std::use_facet >(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::locale()).thousands_sep(); + return std::use_facet >(std::locale()).thousands_sep(); #else - return ','; + return ','; #endif } template -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::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::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::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::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::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::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::is_signed)) - result *= sign; +done: + if ((sign < 0) && (base == 10)) result *= sign; return true; } template -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. diff --git a/Foundation/testsuite/src/StringTest.cpp b/Foundation/testsuite/src/StringTest.cpp index 0bff4ec4a..dccd267d9 100644 --- a/Foundation/testsuite/src/StringTest.cpp +++ b/Foundation/testsuite/src/StringTest.cpp @@ -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; } diff --git a/Foundation/testsuite/src/StringTest.h b/Foundation/testsuite/src/StringTest.h index f05e1d97e..2949a607a 100644 --- a/Foundation/testsuite/src/StringTest.h +++ b/Foundation/testsuite/src/StringTest.h @@ -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::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::max()) - { assert(strToInt(" 123 ", result)); assert(result == 123); } + { assert(strToInt(" 123 ", result, 10)); assert(result == 123); } if (123 < std::numeric_limits::max()) - { assert(strToInt(" 123", result)); assert(result == 123); } + { assert(strToInt(" 123", result, 10)); assert(result == 123); } if (123 < std::numeric_limits::max()) - { assert(strToInt("123 ", result)); assert(result == 123); } + { assert(strToInt("123 ", result, 10)); assert(result == 123); } if (std::numeric_limits::is_signed && (-123 > std::numeric_limits::min())) - { assert(strToInt("-123", result)); assert(result == -123); } + { assert(strToInt("-123", result, 10)); assert(result == -123); } if (0x123 < std::numeric_limits::max()) { assert(strToInt("123", result, 0x10)); assert(result == 0x123); } if (0x12ab < std::numeric_limits::max()) { assert(strToInt("12AB", result, 0x10)); assert(result == 0x12ab); } if (0x12ab < std::numeric_limits::max()) - { assert(strToInt("0X12AB", result)); assert(result == 0x12ab); } + { assert(strToInt("0X12AB", result, 0x10)); assert(result == 0x12ab); } if (0x12ab < std::numeric_limits::max()) - { assert(strToInt("0x12AB", result)); assert(result == 0x12ab); } + { assert(strToInt("0x12AB", result, 0x10)); assert(result == 0x12ab); } if (0x12ab < std::numeric_limits::max()) - { assert(strToInt("0x12aB", result)); assert(result == 0x12ab); } + { assert(strToInt("0x12aB", result, 0x10)); assert(result == 0x12ab); } if (0x98fe < std::numeric_limits::max()) - { assert(strToInt("0X98Fe", result)); assert(result == 0x98fe); } + { assert(strToInt("0X98Fe", result, 0x10)); assert(result == 0x98fe); } if (123 < std::numeric_limits::max()) - { assert(strToInt("0x0", result)); assert(result == 0); } + { assert(strToInt("0x0", result, 0x10)); assert(result == 0); } if (123 < std::numeric_limits::max()) { assert(strToInt("00", result, 0x10)); assert(result == 0); } if (0123 < std::numeric_limits::max()) { assert(strToInt("123", result, 010)); assert(result == 0123); } if (0123 < std::numeric_limits::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 + 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(); + } + };