From f70ac1ca078afac6fb1719e20e42a2290564890d Mon Sep 17 00:00:00 2001 From: Aleksandar Fabijanic Date: Sun, 30 Sep 2012 05:17:56 +0000 Subject: [PATCH] - added NumericString.h - NumberParser improvements (internal Poco locale-awareness and parsing) --- CHANGELOG | 1 + CppUnit/src/TestCase.cpp | 8 +- Foundation/Foundation_CE_vs90.vcproj | 4 + Foundation/Foundation_vs100.vcxproj | 2 + Foundation/Foundation_vs100.vcxproj.filters | 6 + Foundation/Foundation_vs110.vcxproj | 1 + Foundation/Foundation_vs110.vcxproj.filters | 3 + Foundation/Foundation_vs71.vcproj | 3 + Foundation/Foundation_vs80.vcproj | 4 + Foundation/Foundation_vs90.vcproj | 4 + Foundation/Foundation_x64_vs100.vcxproj | 1 + .../Foundation_x64_vs100.vcxproj.filters | 3 + Foundation/Foundation_x64_vs110.vcxproj | 1 + .../Foundation_x64_vs110.vcxproj.filters | 3 + Foundation/Foundation_x64_vs90.vcproj | 4 + Foundation/include/Poco/FPEnvironment.h | 12 +- Foundation/include/Poco/NumberParser.h | 141 +------ Foundation/include/Poco/NumericString.h | 376 ++++++++++++++++++ Foundation/src/FileChannel.cpp | 3 - Foundation/src/NumberParser.cpp | 32 +- Foundation/src/String.cpp | 44 +- .../testsuite/TestSuite_vs100.vcxproj.filters | 6 +- Foundation/testsuite/src/NumberParserTest.cpp | 104 ++++- Foundation/testsuite/src/NumberParserTest.h | 1 + Foundation/testsuite/src/StringTest.cpp | 284 ++++++++++++- Foundation/testsuite/src/StringTest.h | 50 +++ JSON/testsuite/src/JSONTest.cpp | 2 +- 27 files changed, 904 insertions(+), 199 deletions(-) create mode 100644 Foundation/include/Poco/NumericString.h diff --git a/CHANGELOG b/CHANGELOG index cc5662144..fb9124282 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -42,6 +42,7 @@ Release 1.5.0 (2012-09-??) - fixed SF#321 Binding DatTime or Timestamp - 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 Release 1.4.4p1 (2012-??-??) ============================ diff --git a/CppUnit/src/TestCase.cpp b/CppUnit/src/TestCase.cpp index b583cc9a3..95e9c34e5 100644 --- a/CppUnit/src/TestCase.cpp +++ b/CppUnit/src/TestCase.cpp @@ -36,15 +36,15 @@ void TestCase::assertImplementation(bool condition, const std::string& condition void TestCase::loop1assertImplementation(bool condition, const std::string& conditionExpression, long lineNumber, long data1lineNumber, const std::string& fileName) { - if (!condition) - throw CppUnitException(conditionExpression, lineNumber, data1lineNumber, fileName); + if (!condition) + throw CppUnitException(conditionExpression, lineNumber, data1lineNumber, fileName); } void TestCase::loop2assertImplementation(bool condition, const std::string& conditionExpression, long lineNumber, long data1lineNumber, long data2lineNumber, const std::string& fileName) { - if (!condition) - throw CppUnitException(conditionExpression, lineNumber, data1lineNumber, data2lineNumber, fileName); + if (!condition) + throw CppUnitException(conditionExpression, lineNumber, data1lineNumber, data2lineNumber, fileName); } diff --git a/Foundation/Foundation_CE_vs90.vcproj b/Foundation/Foundation_CE_vs90.vcproj index 1e7d23bcd..0bc16ecde 100644 --- a/Foundation/Foundation_CE_vs90.vcproj +++ b/Foundation/Foundation_CE_vs90.vcproj @@ -6633,6 +6633,10 @@ RelativePath=".\include\Poco\NumberParser.h" > + + diff --git a/Foundation/Foundation_vs100.vcxproj b/Foundation/Foundation_vs100.vcxproj index 61c562053..5be73d8d2 100644 --- a/Foundation/Foundation_vs100.vcxproj +++ b/Foundation/Foundation_vs100.vcxproj @@ -1007,6 +1007,7 @@ + @@ -1014,6 +1015,7 @@ + diff --git a/Foundation/Foundation_vs100.vcxproj.filters b/Foundation/Foundation_vs100.vcxproj.filters index 93eb87a13..32d629673 100644 --- a/Foundation/Foundation_vs100.vcxproj.filters +++ b/Foundation/Foundation_vs100.vcxproj.filters @@ -1826,6 +1826,12 @@ Core\Header Files + + Text\Header Files + + + Core\Header Files + diff --git a/Foundation/Foundation_vs110.vcxproj b/Foundation/Foundation_vs110.vcxproj index f741d2761..8ce9c728b 100644 --- a/Foundation/Foundation_vs110.vcxproj +++ b/Foundation/Foundation_vs110.vcxproj @@ -1020,6 +1020,7 @@ + diff --git a/Foundation/Foundation_vs110.vcxproj.filters b/Foundation/Foundation_vs110.vcxproj.filters index 37ae3b829..c120249d5 100644 --- a/Foundation/Foundation_vs110.vcxproj.filters +++ b/Foundation/Foundation_vs110.vcxproj.filters @@ -995,6 +995,9 @@ Core\Header Files + + Core\Header Files + Core\Header Files diff --git a/Foundation/Foundation_vs71.vcproj b/Foundation/Foundation_vs71.vcproj index b19e2e1d5..de1d691f6 100644 --- a/Foundation/Foundation_vs71.vcproj +++ b/Foundation/Foundation_vs71.vcproj @@ -892,6 +892,9 @@ + + diff --git a/Foundation/Foundation_vs80.vcproj b/Foundation/Foundation_vs80.vcproj index 9d494740e..3be5ddbd8 100644 --- a/Foundation/Foundation_vs80.vcproj +++ b/Foundation/Foundation_vs80.vcproj @@ -1191,6 +1191,10 @@ RelativePath=".\include\Poco\NumberParser.h" > + + diff --git a/Foundation/Foundation_vs90.vcproj b/Foundation/Foundation_vs90.vcproj index 427146a0f..a5188311c 100644 --- a/Foundation/Foundation_vs90.vcproj +++ b/Foundation/Foundation_vs90.vcproj @@ -1188,6 +1188,10 @@ RelativePath=".\include\Poco\NumberParser.h" > + + diff --git a/Foundation/Foundation_x64_vs100.vcxproj b/Foundation/Foundation_x64_vs100.vcxproj index f920df35f..a68ba19f0 100644 --- a/Foundation/Foundation_x64_vs100.vcxproj +++ b/Foundation/Foundation_x64_vs100.vcxproj @@ -1019,6 +1019,7 @@ + diff --git a/Foundation/Foundation_x64_vs100.vcxproj.filters b/Foundation/Foundation_x64_vs100.vcxproj.filters index 4e9a1e05c..d91d36795 100644 --- a/Foundation/Foundation_x64_vs100.vcxproj.filters +++ b/Foundation/Foundation_x64_vs100.vcxproj.filters @@ -995,6 +995,9 @@ Core\Header Files + + Core\Header Files + Core\Header Files diff --git a/Foundation/Foundation_x64_vs110.vcxproj b/Foundation/Foundation_x64_vs110.vcxproj index f649a3303..c179e1ad3 100644 --- a/Foundation/Foundation_x64_vs110.vcxproj +++ b/Foundation/Foundation_x64_vs110.vcxproj @@ -1025,6 +1025,7 @@ + diff --git a/Foundation/Foundation_x64_vs110.vcxproj.filters b/Foundation/Foundation_x64_vs110.vcxproj.filters index bd06a6d70..1b4a48f69 100644 --- a/Foundation/Foundation_x64_vs110.vcxproj.filters +++ b/Foundation/Foundation_x64_vs110.vcxproj.filters @@ -995,6 +995,9 @@ Core\Header Files + + Core\Header Files + Core\Header Files diff --git a/Foundation/Foundation_x64_vs90.vcproj b/Foundation/Foundation_x64_vs90.vcproj index 81d2902cc..d745db9be 100644 --- a/Foundation/Foundation_x64_vs90.vcproj +++ b/Foundation/Foundation_x64_vs90.vcproj @@ -1190,6 +1190,10 @@ RelativePath=".\include\Poco\NumberParser.h" > + + diff --git a/Foundation/include/Poco/FPEnvironment.h b/Foundation/include/Poco/FPEnvironment.h index 3620f2c6f..e9660b73a 100644 --- a/Foundation/include/Poco/FPEnvironment.h +++ b/Foundation/include/Poco/FPEnvironment.h @@ -95,10 +95,10 @@ public: FPEnvironment(RoundingMode mode); /// Remembers the current environment and /// sets the given rounding mode. - + FPEnvironment(const FPEnvironment& env); /// Copy constructor. - + ~FPEnvironment(); /// Restores the previous environment (unless /// keepCurrent() has been called previously) @@ -121,18 +121,18 @@ public: static RoundingMode getRoundingMode(); /// Returns the current rounding mode. - - static bool isInfinite(float value); + + static bool isInfinite(float value); static bool isInfinite(double value); static bool isInfinite(long double value); /// Returns true iff the given number is infinite. - static bool isNaN(float value); + static bool isNaN(float value); static bool isNaN(double value); static bool isNaN(long double value); /// Returns true iff the given number is NaN. - static float copySign(float target, float source); + static float copySign(float target, float source); static double copySign(double target, double source); static long double copySign(long double target, long double source); /// Copies the sign from source to target. diff --git a/Foundation/include/Poco/NumberParser.h b/Foundation/include/Poco/NumberParser.h index 8b37c390e..d4aafa65c 100644 --- a/Foundation/include/Poco/NumberParser.h +++ b/Foundation/include/Poco/NumberParser.h @@ -55,6 +55,10 @@ class Foundation_API NumberParser /// for parsing numbers out of strings. { public: + static const unsigned short NUM_BASE_OCT = 010; + static const unsigned short NUM_BASE_DEC = 10; + static const unsigned short NUM_BASE_HEX = 0x10; + static int parse(const std::string& s); /// Parses an integer value in decimal notation from the given string. /// Throws a SyntaxException if the string does not hold a number in decimal notation. @@ -127,7 +131,7 @@ public: static double parseFloat(const std::string& s); /// Parses a double value in decimal floating point notation - /// from the given string. + /// from the given string. /// Throws a SyntaxException if the string does not hold a floating-point /// number in decimal notation. @@ -151,141 +155,6 @@ public: /// String forms are NOT case sensitive. /// Returns true if a valid bool number has been found, /// false otherwise. - -private: - - template - static bool strToIntOct(const std::string &s, T& result) - { - if (s.empty()) return false; - if (std::numeric_limits::is_signed) return false; - std::string::const_iterator it = s.begin(); - std::string::const_iterator end = s.end(); - while (it != end && std::isspace(*it)) ++it; - if (it == end) return false; - while (it != end && *it == '0') ++it; - if (it == end) - { - result = 0; - return true; - } - - unsigned base = 010; - T n = 0; - for (; it != end; ++it) - { - if (*it >= '0' && *it <= '7') - { - if (n > (std::numeric_limits::max() / base)) - return false; - n = n * base + *it - '0'; - } - else break; - } - - while (it != end && std::isspace(*it)) ++it; - if (it != end) return false; - - result = n; - return true; - } - - template - static bool strToIntDec(const std::string &s, T& result) - { - if (s.empty()) return false; - int sign = 1; - std::string::const_iterator it = s.begin(); - std::string::const_iterator end = s.end(); - while (it != end && std::isspace(*it)) ++it; - if (it == end) return false; - if (std::numeric_limits::is_signed) - { - if (*it == '-') - { - sign = -1; - ++it; - } - else if (*it == '+') ++it; - if (it == end) return false; - } - - unsigned base = 10; - T n = 0; - for (; it != end; ++it) - { - if (*it >= '0' && *it <= '9') - { - if (n > (std::numeric_limits::max() / base)) - return false; - n = n * base + *it - '0'; - } - else break; - } - - while (it != end && std::isspace(*it)) ++it; - if (it != end) return false; - - result = sign * n; - return true; - } - - template - static bool strToIntHex(const std::string &s, T& result) - { - if (s.empty()) return false; - if (std::numeric_limits::is_signed) return false; - std::string::const_iterator it = s.begin(); - std::string::const_iterator end = s.end(); - while (it != end && std::isspace(*it)) ++it; - if (it == end) return false; - - bool beginWith0x = false; - if (*it == '0') - { - beginWith0x = true; - while (it != end && *it == '0') - { - ++it; - beginWith0x = false; - } - if (it == end) - { - result = 0; - return true; - } - } - if (beginWith0x && (*it != 'x') && (*it != 'X')) return false; - else if ((*it == 'x') || (*it == 'X')) ++it; - if (it == end) return false; - - unsigned base = 0x10; - T n = 0; - for (; it != end; ++it) - { - if ((*it >= '0' && *it <= '9') || (*it >= 'A' && *it <= 'F') || (*it >= 'a' && *it <= 'f')) - { - if (n > (std::numeric_limits::max() / base)) - return false; - - if (*it >= '0' && *it <= '9') - n = n * base + *it - '0'; - else if (*it >= 'A' && *it <= 'F') - n = n * base + *it - 'A' + 10; - else if (*it >= 'a' && *it <= 'f') - n = n * base + *it - 'a' + 10; - } - else break; - } - if (it != end) - { - while (std::isspace(*it)) ++it; - if (it != end) return false; - } - - result = n; - return true; - } }; diff --git a/Foundation/include/Poco/NumericString.h b/Foundation/include/Poco/NumericString.h new file mode 100644 index 000000000..30267c2ae --- /dev/null +++ b/Foundation/include/Poco/NumericString.h @@ -0,0 +1,376 @@ +// +// NumericString.h +// +// $Id: //poco/1.4/Foundation/include/Poco/NumericString.h#1 $ +// +// Library: Foundation +// Package: Core +// Module: NumericString +// +// Numeric string utility functions. +// +// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + +#ifndef Foundation_NumericString_INCLUDED +#define Foundation_NumericString_INCLUDED + + +#include "Poco/Foundation.h" +#include "Poco/FPEnvironment.h" +#ifdef min + #undef min +#endif +#ifdef max + #undef max +#endif +#include +#include + + +namespace Poco { + + +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. +{ + if (!std::numeric_limits::is_integer) return false; + + if (!pStr || (pStr && *pStr == '\0')) return false; + while ((*pStr != '\0') && (*pStr == ' ')) ++pStr; + if (*pStr == '\0') return false; + + char sign = 1; + + if (*pStr == '-') + { + ++pStr; + sign = -1; + } + 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; + + const char thSep = thousandSeparator(); + bool allowDigits = true; + for (; *pStr != '\0'; ++pStr) + { + switch (*pStr) + { + case '0': 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; + break; + + case '8': case '9': + if (allowDigits && (base == 10 || base == 16)) + { + if (result > (std::numeric_limits::max() / base)) 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; + 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; + break; + + case 'U': + case 'u': + case 'L': + case 'l': + allowDigits = false; + break; + + case '.': + if ((base == 10) && (thSep == '.')) break; + else return false; + + case ',': + if ((base == 10) && (thSep == ',')) break; + else return false; + + case ' ': + if (base == 10) break; + else return false; + + default: + return false; + } + } + + if ((base == 10) && (std::numeric_limits::is_signed)) + result *= sign; + + return true; +} + + +template +bool strToInt(const std::string& str, I& result, short base = -1) + /// Converts string to integer number; + /// This is a wrapper function, for details see see the + /// bool strToInt(const char*, I&, short&) implementation. +{ + return strToInt(str.c_str(), result, base); +} + + +inline char decimalSeparator() + /// 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(); +#else + return '.'; +#endif +} + + +inline char thousandSeparator() + /// 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(); +#else + return ','; +#endif +} + + +#ifndef POCO_NO_FPENVIRONMENT + +namespace { + +static char DUMMY_EXP_UNDERFLOW = 0; // dummy default val + +} + +template +bool strToFloat (const char* pStr, F& result, char& eu = DUMMY_EXP_UNDERFLOW) + /// Converts zero-terminated array to floating-point number; + /// Returns true if succesful. Exponent underflow (i.e. loss of precision) + /// is signalled in eu. Thousand separators are recognized for the locale + /// and silently skipped but not verified for correct positioning. + /// + /// If parsing was unsuccesful, the return value is false with + /// result and eu values undetermined. +{ + if (!pStr || (pStr && *pStr == '\0')) return false; + + // parser states: + const char STATE_LEADING_SPACES = 0; + const char STATE_DIGITS_BEFORE_DEC_POINT = 1; + const char STATE_DIGITS_AFTER_DEC_POINT = 2; + const char STATE_EXP_CHAR = 3; + const char STATE_EXP_DIGITS = 4; + const char STATE_SUFFIX = 5; // 'f' suffix + + const char decSep = decimalSeparator(); + char numSign = 1, expSign = 1; + char state = STATE_LEADING_SPACES; + F mantissa = 0.0, exponent = 0.0; + F pow10 = 1.; + result = 0.0; + eu = 0; + for (; *pStr != '\0'; ++pStr) + { + switch (*pStr) + { + case '.': + if (decSep == '.') + { + if (state > STATE_DIGITS_BEFORE_DEC_POINT) return false; + state = STATE_DIGITS_AFTER_DEC_POINT; + } + break; + + case ',': + if (decSep == ',') + { + if (state > STATE_DIGITS_BEFORE_DEC_POINT) return false; + state = STATE_DIGITS_AFTER_DEC_POINT; + } + break; + + case ' ': + if ((state > STATE_LEADING_SPACES) && (state < STATE_DIGITS_AFTER_DEC_POINT)) + return false; + break; + + case '-': + if (state == STATE_LEADING_SPACES) + numSign = -1; + else if (state == STATE_EXP_CHAR) // exponential char + expSign = -1; + else return false; + case '+': + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (state >= STATE_SUFFIX) return false; // constant suffix + if (state <= STATE_DIGITS_BEFORE_DEC_POINT) // integral part digits + { + result = result * 10 + (*pStr - '0'); + state = STATE_DIGITS_BEFORE_DEC_POINT; + } + else if (state <= STATE_DIGITS_AFTER_DEC_POINT) // fractional part digits + { + mantissa += (*pStr - '0') / (pow10 *= 10.); + state = STATE_DIGITS_AFTER_DEC_POINT; + } + else if (state <= STATE_EXP_DIGITS) // exponent digits + { + exponent = exponent * 10 + (*pStr - '0'); + state = STATE_EXP_DIGITS; + } + else return false; + break; + + case 'E': + case 'e': + if (state > STATE_DIGITS_AFTER_DEC_POINT) return false; + state = STATE_EXP_CHAR; + break; + + case 'F': + case 'f': + state = STATE_SUFFIX; + break; + + default: + return false; + } + } + + if (exponent > std::numeric_limits::max_exponent10) + { + eu = expSign; + exponent = std::numeric_limits::max_exponent10; + } + + result += mantissa; + if (numSign != 1) result *= numSign; + if (exponent > 1.0) + { + F scale = std::pow(10., exponent); + result = (expSign < 0) ? (result / scale) : (result * scale); + } + + return (state != STATE_LEADING_SPACES) && // empty/zero-length string + !FPEnvironment::isInfinite(result) && + !FPEnvironment::isNaN(result); +} + + +template +bool strToFloat (const std::string& s, F& result, char& eu = DUMMY_EXP_UNDERFLOW) + /// Converts string to floating-point number; + /// This is a wrapper function, for details see see the + /// bool strToFloat(const char*, F&, char&) implementation. +{ + return strToFloat(s.c_str(), result, eu); +} + + +#endif // POCO_NO_FPENVIRONMENT + + +} // namespace Poco + + +#endif // Foundation_NumericString_INCLUDED diff --git a/Foundation/src/FileChannel.cpp b/Foundation/src/FileChannel.cpp index 2bc60b100..de921551b 100644 --- a/Foundation/src/FileChannel.cpp +++ b/Foundation/src/FileChannel.cpp @@ -39,7 +39,6 @@ #include "Poco/RotateStrategy.h" #include "Poco/PurgeStrategy.h" #include "Poco/Message.h" -#include "Poco/NumberParser.h" #include "Poco/DateTimeFormatter.h" #include "Poco/DateTime.h" #include "Poco/LocalDateTime.h" @@ -48,7 +47,6 @@ #include "Poco/Exception.h" #include "Poco/Ascii.h" - namespace Poco { @@ -232,7 +230,6 @@ void FileChannel::setRotation(const std::string& rotation) while (it != end && Ascii::isSpace(*it)) ++it; std::string unit; while (it != end && Ascii::isAlpha(*it)) unit += *it++; - RotateStrategy* pStrategy = 0; if ((rotation.find(',') != std::string::npos) || (rotation.find(':') != std::string::npos)) { diff --git a/Foundation/src/NumberParser.cpp b/Foundation/src/NumberParser.cpp index 9a76c3e1c..9134bfc5e 100644 --- a/Foundation/src/NumberParser.cpp +++ b/Foundation/src/NumberParser.cpp @@ -36,13 +36,14 @@ #include "Poco/NumberParser.h" #include "Poco/Exception.h" -#include "Poco/MemoryStream.h" #include "Poco/String.h" -#if !defined(POCO_NO_LOCALE) -#include -#endif +#include "Poco/NumericString.h" #include #include +#include +#if !defined(POCO_NO_LOCALE) + #include +#endif #if defined(POCO_LONG_IS_64_BIT) @@ -71,7 +72,7 @@ int NumberParser::parse(const std::string& s) bool NumberParser::tryParse(const std::string& s, int& value) { - return strToIntDec(s, value); + return strToInt(s.c_str(), value, NUM_BASE_DEC); } @@ -87,7 +88,7 @@ unsigned NumberParser::parseUnsigned(const std::string& s) bool NumberParser::tryParseUnsigned(const std::string& s, unsigned& value) { - return strToIntDec(s, value); + return strToInt(s.c_str(), value, NUM_BASE_DEC); } @@ -103,7 +104,7 @@ unsigned NumberParser::parseHex(const std::string& s) bool NumberParser::tryParseHex(const std::string& s, unsigned& value) { - return strToIntHex(s, value); + return strToInt(s.c_str(), value, NUM_BASE_HEX); } @@ -119,7 +120,7 @@ unsigned NumberParser::parseOct(const std::string& s) bool NumberParser::tryParseOct(const std::string& s, unsigned& value) { - return strToIntOct(s, value); + return strToInt(s.c_str(), value, NUM_BASE_OCT); } @@ -138,7 +139,7 @@ Int64 NumberParser::parse64(const std::string& s) bool NumberParser::tryParse64(const std::string& s, Int64& value) { - return strToIntDec(s, value); + return strToInt(s.c_str(), value, NUM_BASE_DEC); } @@ -154,7 +155,7 @@ UInt64 NumberParser::parseUnsigned64(const std::string& s) bool NumberParser::tryParseUnsigned64(const std::string& s, UInt64& value) { - return strToIntDec(s, value); + return strToInt(s.c_str(), value, NUM_BASE_DEC); } @@ -170,7 +171,7 @@ UInt64 NumberParser::parseHex64(const std::string& s) bool NumberParser::tryParseHex64(const std::string& s, UInt64& value) { - return strToIntHex(s, value); + return strToInt(s.c_str(), value, NUM_BASE_HEX); } @@ -186,7 +187,7 @@ UInt64 NumberParser::parseOct64(const std::string& s) bool NumberParser::tryParseOct64(const std::string& s, UInt64& value) { - return strToIntOct(s, value); + return strToInt(s.c_str(), value, NUM_BASE_OCT); } @@ -205,12 +206,7 @@ double NumberParser::parseFloat(const std::string& s) bool NumberParser::tryParseFloat(const std::string& s, double& value) { - Poco::MemoryInputStream istr(s.data(), s.size()); -#if !defined(POCO_NO_LOCALE) - istr.imbue(std::locale::classic()); -#endif - istr >> value; - return istr.eof() && !istr.fail(); + return strToFloat(s.c_str(), value); } diff --git a/Foundation/src/String.cpp b/Foundation/src/String.cpp index b89278062..2f30b4ed1 100644 --- a/Foundation/src/String.cpp +++ b/Foundation/src/String.cpp @@ -52,19 +52,19 @@ int icompare(const std::string& str, std::string::size_type pos, std::string::si std::string::const_iterator end1 = str.begin() + pos + n; while (it1 != end1 && it2 != end2) { - std::string::value_type c1 = Ascii::toLower(*it1); - std::string::value_type c2 = Ascii::toLower(*it2); - if (c1 < c2) - return -1; - else if (c1 > c2) - return 1; - ++it1; ++it2; + std::string::value_type c1 = Ascii::toLower(*it1); + std::string::value_type c2 = Ascii::toLower(*it2); + if (c1 < c2) + return -1; + else if (c1 > c2) + return 1; + ++it1; ++it2; } - - if (it1 == end1) + + if (it1 == end1) return it2 == end2 ? 0 : -1; - else - return 1; + else + return 1; } @@ -122,19 +122,19 @@ int icompare(const std::string& str, std::string::size_type pos, std::string::si std::string::const_iterator end = str.begin() + pos + n; while (it != end && *ptr) { - std::string::value_type c1 = Ascii::toLower(*it); - std::string::value_type c2 = Ascii::toLower(*ptr); - if (c1 < c2) - return -1; - else if (c1 > c2) - return 1; - ++it; ++ptr; + std::string::value_type c1 = Ascii::toLower(*it); + std::string::value_type c2 = Ascii::toLower(*ptr); + if (c1 < c2) + return -1; + else if (c1 > c2) + return 1; + ++it; ++ptr; } - - if (it == end) + + if (it == end) return *ptr == 0 ? 0 : -1; - else - return 1; + else + return 1; } diff --git a/Foundation/testsuite/TestSuite_vs100.vcxproj.filters b/Foundation/testsuite/TestSuite_vs100.vcxproj.filters index f39e374ab..55ebe9310 100644 --- a/Foundation/testsuite/TestSuite_vs100.vcxproj.filters +++ b/Foundation/testsuite/TestSuite_vs100.vcxproj.filters @@ -959,11 +959,11 @@ Dynamic\Header Files - - Core\Source Files - Streams\Header Files + + Core\Header Files + \ No newline at end of file diff --git a/Foundation/testsuite/src/NumberParserTest.cpp b/Foundation/testsuite/src/NumberParserTest.cpp index 835f98090..58b3f171d 100644 --- a/Foundation/testsuite/src/NumberParserTest.cpp +++ b/Foundation/testsuite/src/NumberParserTest.cpp @@ -35,6 +35,12 @@ #include "CppUnit/TestSuite.h" #include "Poco/Exception.h" #include "Poco/Types.h" +#include "Poco/Format.h" +#include "Poco/NumericString.h" +#include "Poco/MemoryStream.h" +#include "Poco/Stopwatch.h" +#include +#include using Poco::NumberParser; @@ -50,6 +56,9 @@ using Poco::UInt32; using Poco::Int64; using Poco::UInt64; #endif +using Poco::format; +using Poco::decimalSeparator; +using Poco::thousandSeparator; NumberParserTest::NumberParserTest(const std::string& name): CppUnit::TestCase(name) @@ -64,12 +73,21 @@ NumberParserTest::~NumberParserTest() void NumberParserTest::testParse() { + const char ts = thousandSeparator(); + assert(NumberParser::parse("123") == 123); + assert(NumberParser::parse(format("123%c456", ts)) == 123456); + assert(NumberParser::parse(format("1%c234%c567", ts, ts)) == 1234567); + assert(NumberParser::parse("+123") == 123); + assert(NumberParser::parse("-123") == -123); assert(NumberParser::parse("0") == 0); assert(NumberParser::parse("000") == 0); assert(NumberParser::parse(" 123 ") == 123); assert(NumberParser::parse(" 123") == 123); assert(NumberParser::parse("123 ") == 123); + assert(NumberParser::parse("0123") == 123); + assert(NumberParser::parse("+0123") == 123); + assert(NumberParser::parse("-0123") == -123); assert(NumberParser::parse("-123") == -123); assert(NumberParser::parseUnsigned("123") == 123); assert(NumberParser::parseHex("12AB") == 0x12ab); @@ -94,6 +112,8 @@ void NumberParserTest::testParse() #if defined(POCO_HAVE_INT64) assert(NumberParser::parse64("123") == 123); assert(NumberParser::parse64("-123") == -123); + assert(NumberParser::parse64("0123") == 123); + assert(NumberParser::parse64("-0123") == -123); assert(NumberParser::parseUnsigned64("123") == 123); assert(NumberParser::parseHex64("12AB") == 0x12ab); assert(NumberParser::parseHex64("0x12AB") == 0x12ab); @@ -101,7 +121,75 @@ void NumberParserTest::testParse() assert(NumberParser::parseOct64("0123") == 0123); #endif - assertEqualDelta(12.34, NumberParser::parseFloat("12.34"), 0.01); +#ifndef POCO_NO_FPENVIRONMENT + const char dp = decimalSeparator(); + + assertEqualDelta(1.0, NumberParser::parseFloat(format("1", dp)), 0.01); + assertEqualDelta(0.0, NumberParser::parseFloat(format("0%c0", dp)), 0.01); + assertEqualDelta(0., NumberParser::parseFloat(format("0%c0", dp)), 0.01); + assertEqualDelta(.0, NumberParser::parseFloat(format("0%c0", dp)), 0.01); + assertEqualDelta(12.34, NumberParser::parseFloat(format("12%c34", dp)), 0.01); + assertEqualDelta(12.34, NumberParser::parseFloat(format("12%c34f", dp)), 0.01); + assertEqualDelta(12.34, NumberParser::parseFloat(format("12%c34", dp)), 0.01); + assertEqualDelta(-12.34, NumberParser::parseFloat(format("-12%c34", dp)), 0.01); + assertEqualDelta(.34, NumberParser::parseFloat(format("%c34", dp)), 0.01); + assertEqualDelta(-.34, NumberParser::parseFloat(format("-%c34", dp)), 0.01); + assertEqualDelta(12., NumberParser::parseFloat(format("12%c", dp)), 0.01); + assertEqualDelta(-12., NumberParser::parseFloat(format("-12%c", dp)), 0.01); + assertEqualDelta(12, NumberParser::parseFloat("12"), 0.01); + assertEqualDelta(-12, NumberParser::parseFloat("-12"), 0.01); + assertEqualDelta(12.34, NumberParser::parseFloat(format("12%c3456789012345678901234567890", dp)), 0.01); + + assertEqualDelta(1234.3456789, NumberParser::parseFloat(format("1%c234%c3456789012345678901234567890", ts, dp)), 0.00000001); + assertEqualDelta(12345.3456789, NumberParser::parseFloat(format("12%c345%c3456789012345678901234567890", ts, dp)), 0.00000001); + assertEqualDelta(123456.3456789, NumberParser::parseFloat(format("123%c456%c3456789012345678901234567890", ts, dp)), 0.00000001); + assertEqualDelta(1234567.3456789, NumberParser::parseFloat(format("1%c234%c567%c3456789012345678901234567890", ts, ts, dp)), 0.00000001); + assertEqualDelta(12345678.3456789, NumberParser::parseFloat(format("12%c345%c678%c3456789012345678901234567890", ts, ts, dp)), 0.00000001); + assertEqualDelta(123456789.3456789, NumberParser::parseFloat(format("123%c456%c789%c3456789012345678901234567890", ts, ts, dp)), 0.00000001); + + if ((std::numeric_limits::max() / 10) < 1.23456e10) + fail ("test value larger than max value for this platform"); + else + { + double d = 1.234e100; + assertEqualDelta(d, NumberParser::parseFloat(format("1%c234e100", dp)), 0.01); + assertEqualDelta(d, NumberParser::parseFloat(format("1%c234E+100", dp)), 0.01); + + d = 1.234e-100; + assertEqualDelta(d, NumberParser::parseFloat(format("1%c234E-100", dp)), 0.01); + + d = -1.234e100; + assertEqualDelta(d, NumberParser::parseFloat(format("-1%c234e+100", dp)), 0.01); + assertEqualDelta(d, NumberParser::parseFloat(format("-1%c234E100", dp)), 0.01); + + d = 1.234e-100; + assertEqualDelta(d, NumberParser::parseFloat(format(" 1%c234e-100 ", dp)), 0.01); + assertEqualDelta(d, NumberParser::parseFloat(format(" 1%c234e-100 ", dp)), 0.01); + assertEqualDelta(d, NumberParser::parseFloat(format(" 1%c234e-100 ", dp)), 0.01); + + d = 1234.234e-100; + assertEqualDelta(d, NumberParser::parseFloat(format(" 1%c234%c234e-100 ", ts, dp)), 0.01); + d = 12345.234e-100; + assertEqualDelta(d, NumberParser::parseFloat(format(" 12%c345%c234e-100 ", ts, dp)), 0.01); + d = 123456.234e-100; + assertEqualDelta(d, NumberParser::parseFloat(format(" 123%c456%c234e-100 ", ts, dp)), 0.01); + + d = -1234.234e-100; + assertEqualDelta(d, NumberParser::parseFloat(format(" -1%c234%c234e-100 ", ts, dp)), 0.01); + d = -12345.234e-100; + assertEqualDelta(d, NumberParser::parseFloat(format(" -12%c345%c234e-100 ", ts, dp)), 0.01); + d = -123456.234e-100; + assertEqualDelta(d, NumberParser::parseFloat(format(" -123%c456%c234e-100 ", ts, dp)), 0.01); + } + + double d = 12.34e-10; + assertEqualDelta(d, NumberParser::parseFloat(format("12%c34e-10", dp)), 0.01); + assertEqualDelta(-12.34, NumberParser::parseFloat(format("-12%c34", dp)), 0.01); + + assertEqualDelta(12.34, NumberParser::parseFloat(format(" 12%c34", dp)), 0.01); + assertEqualDelta(12.34, NumberParser::parseFloat(format("12%c34 ", dp)), 0.01); + assertEqualDelta(12.34, NumberParser::parseFloat(format(" 12%c34 ", dp)), 0.01); +#endif // POCO_NO_FPENVIRONMENT } @@ -126,6 +214,9 @@ void NumberParserTest::testLimits() void NumberParserTest::testParseError() { + const char dp = decimalSeparator(); + const char ts = thousandSeparator(); + try { NumberParser::parse(""); @@ -198,19 +289,22 @@ void NumberParserTest::testParseError() failmsg("must throw SyntaxException"); } catch (SyntaxException&) { } -#endif - try { - NumberParser::parseFloat("a12.3"); + NumberParser::parseHex64(format("123%c45", ts)); failmsg("must throw SyntaxException"); } catch (SyntaxException&) { } +#endif // POCO_HAVE_INT64 + +#ifndef POCO_NO_FPENVIRONMENT try { - NumberParser::parseFloat("12.3aa"); + NumberParser::parseFloat(format("a12%c3", dp)); failmsg("must throw SyntaxException"); } catch (SyntaxException&) { } + +#endif // POCO_NO_FPENVIRONMENT } diff --git a/Foundation/testsuite/src/NumberParserTest.h b/Foundation/testsuite/src/NumberParserTest.h index 08b8093d4..4c2f75716 100644 --- a/Foundation/testsuite/src/NumberParserTest.h +++ b/Foundation/testsuite/src/NumberParserTest.h @@ -60,6 +60,7 @@ public: static CppUnit::Test* suite(); private: + template bool testUpperLimit() { T n = std::numeric_limits::max(); diff --git a/Foundation/testsuite/src/StringTest.cpp b/Foundation/testsuite/src/StringTest.cpp index cfcb1c3c4..5fcdc012a 100644 --- a/Foundation/testsuite/src/StringTest.cpp +++ b/Foundation/testsuite/src/StringTest.cpp @@ -34,6 +34,11 @@ #include "CppUnit/TestCaller.h" #include "CppUnit/TestSuite.h" #include "Poco/String.h" +#include "Poco/Format.h" +#include "Poco/MemoryStream.h" +#include "Poco/Stopwatch.h" +#include +#include using Poco::trimLeft; @@ -52,6 +57,13 @@ using Poco::translateInPlace; using Poco::replace; using Poco::replaceInPlace; using Poco::cat; +using Poco::strToInt; +using Poco::strToFloat; +using Poco::thousandSeparator; +using Poco::decimalSeparator; +using Poco::format; +using Poco::MemoryInputStream; +using Poco::Stopwatch; StringTest::StringTest(const std::string& name): CppUnit::TestCase(name) @@ -308,7 +320,7 @@ void StringTest::testReplace() assert (replace(s, "a", "xx") == "xxxxbbxxxxbb"); assert (replace(s, "aa", "xxx") == "xxxbbxxxbb"); - assert (replace(s, "aa", "xx", 2) == "aabbxxbb"); + assert (replace(s, "aa", "xx", 2) == "aabbxxbb"); } @@ -347,6 +359,271 @@ void StringTest::testCat() } +void StringTest::testStringToInt() +{ + stringToInt(); + stringToInt(); + stringToInt(); + stringToInt(); + stringToInt(); + stringToInt(); +#if defined(POCO_HAVE_INT64) + stringToInt(); + stringToInt(); +#endif +} + + +void StringTest::testStringToFloat() +{ +#ifndef POCO_NO_FPENVIRONMENT + + const char ds = decimalSeparator(); + const char ts = thousandSeparator(); + double result; + assert(strToFloat(format("1", ds), result)); + assertEqualDelta(1.0, result, 0.01); + assert(strToFloat(format("0", ds), result)); + assertEqualDelta(0.0, result, 0.01); + assert(strToFloat(format("0%c0", ds), result)); + assertEqualDelta(0.0, result, 0.01); + assert(strToFloat(format("0%c0", ds), result)); + assertEqualDelta(0., result, 0.01); + assert(strToFloat(format("0%c0", ds), result)); + assertEqualDelta(.0, result, 0.01); + assert(strToFloat(format("12%c34", ds), result)); + assertEqualDelta(12.34, result, 0.01); + assert(strToFloat(format("12%c34f", ds), result)); + assertEqualDelta(12.34, result, 0.01); + assert(strToFloat(format("12%c34", ds), result)); + assertEqualDelta(12.34, result, 0.01); + assert(strToFloat(format("-12%c34", ds), result)); + assertEqualDelta(-12.34, result, 0.01); + assert(strToFloat(format("%c34", ds), result)); + assertEqualDelta(.34, result, 0.01); + assert(strToFloat(format("-%c34", ds), result)); + assertEqualDelta(-.34, result, 0.01); + assert(strToFloat(format("12%c", ds), result)); + assertEqualDelta(12., result, 0.01); + assert(strToFloat(format("-12%c", ds), result)); + assertEqualDelta(-12., result, 0.01); + assert(strToFloat("12", result)); + assertEqualDelta(12, result, 0.01); + assert(strToFloat("-12", result)); + assertEqualDelta(-12, result, 0.01); + assert(strToFloat(format("12%c3456789012345678901234567890", ds), result)); + assertEqualDelta(12.34, result, 0.01); + + assert(strToFloat(format("1%c234%c3456789012345678901234567890", ts, ds), result)); + assertEqualDelta(1234.3456789, result, 0.00000001); + assert(strToFloat(format("12%c345%c3456789012345678901234567890", ts, ds), result)); + assertEqualDelta(12345.3456789, result, 0.00000001); + assert(strToFloat(format("123%c456%c3456789012345678901234567890", ts, ds), result)); + assertEqualDelta(123456.3456789, result, 0.00000001); + assert(strToFloat(format("1%c234%c567%c3456789012345678901234567890", ts, ts, ds), result)); + assertEqualDelta(1234567.3456789, result, 0.00000001); + assert(strToFloat(format("12%c345%c678%c3456789012345678901234567890", ts, ts, ds), result)); + assertEqualDelta(12345678.3456789, result, 0.00000001); + assert(strToFloat(format("123%c456%c789%c3456789012345678901234567890", ts, ts, ds), result)); + assertEqualDelta(123456789.3456789, result, 0.00000001); + + if ((std::numeric_limits::max() / 10) < 1.23456e10) + fail ("test value larger than max value for this platform"); + else + { + double d = 12e34; + assert(strToFloat(format("12e34", ds), result)); + assertEqualDelta(d, result, 0.01e34); + + d = 1.234e100; + assert(strToFloat(format("1%c234e100", ds), result)); + assertEqualDelta(d, result, 0.01); + assert(strToFloat(format("1%c234E+100", ds), result)); + assertEqualDelta(d, result, 0.01); + + d = 1.234e-100; + assert(strToFloat(format("1%c234E-100", ds), result)); + assertEqualDelta(d, result, 0.01); + + d = -1.234e100; + assert(strToFloat(format("-1%c234e+100", ds), result)); + assertEqualDelta(d, result, 0.01); + assert(strToFloat(format("-1%c234E100", ds), result)); + assertEqualDelta(d, result, 0.01); + + d = 1.234e-100; + assert(strToFloat(format(" 1%c234e-100 ", ds), result)); + assertEqualDelta(d, result, 0.01); + assert(strToFloat(format(" 1%c234e-100 ", ds), result)); + assertEqualDelta(d, result, 0.01); + assert(strToFloat(format(" 1%c234e-100 ", ds), result)); + assertEqualDelta(d, result, 0.01); + + d = 1234.234e-100; + assert(strToFloat(format(" 1%c234%c234e-100 ", ts, ds), result)); + assertEqualDelta(d, result, 0.01); + d = 12345.234e-100; + assert(strToFloat(format(" 12%c345%c234e-100 ", ts, ds), result)); + assertEqualDelta(d, result, 0.01); + d = 123456.234e-100; + assert(strToFloat(format(" 123%c456%c234e-100 ", ts, ds), result)); + assertEqualDelta(d, result, 0.01); + + d = -1234.234e-100; + assert(strToFloat(format(" -1%c234%c234e-100 ", ts, ds), result)); + assertEqualDelta(d, result, 0.01); + d = -12345.234e-100; + assert(strToFloat(format(" -12%c345%c234e-100 ", ts, ds), result)); + assertEqualDelta(d, result, 0.01); + d = -123456.234e-100; + char ou = 0; + assert(strToFloat(format(" -123%c456%c234e-100 ", ts, ds), result)); + assertEqualDelta(d, result, 0.01); + assert (ou == 0); + } + + double d = 12.34e-10; + assert(strToFloat(format("12%c34e-10", ds), result)); + assertEqualDelta(d, result, 0.01); + assert(strToFloat(format("-12%c34", ds), result)); + assertEqualDelta(-12.34, result, 0.01); + + assert(strToFloat(format(" 12%c34", ds), result)); + assertEqualDelta(12.34, result, 0.01); + assert(strToFloat(format("12%c34 ", ds), result)); + assertEqualDelta(12.34, result, 0.01); + assert(strToFloat(format(" 12%c34 ", ds), result)); + assertEqualDelta(12.34, result, 0.01); + +#endif // POCO_NO_FPENVIRONMENT +} + + +void StringTest::testStringToFloatError() +{ +#ifndef POCO_NO_FPENVIRONMENT + const char ds = decimalSeparator(); + const char ts = thousandSeparator(); + + double result = 0.0; + char ou; + assert (!strToFloat(format("a12%c3", ds), result, ou)); + assert (!strToFloat(format("1b2%c3", ds), result, ou)); + assert (!strToFloat(format("12c%c3", ds), result, ou)); + assert (!strToFloat(format("12%cx3", ds), result, ou)); + + double d = -123456.234e-100; + assert(strToFloat(format("123%c456%c234e-1000000", ts, ds), result, ou)); + assert (ou < 0); // loss of precision + assertEqualDelta(d, result, 0.01); // value still good + assert(!strToFloat(format("123%c456%c234e1000000", ts, ds), result, ou)); + assert(!strToFloat(format("123%c456%c234e+1000000", ts, ds), result, ou)); + assert(!strToFloat(0, result, ou)); // strToFloat is resilient to null pointers + assert(!strToFloat("", result, ou)); +#endif +} + + +void StringTest::testNumericLocale() +{ +#if !defined(POCO_NO_LOCALE) + char dp = decimalSeparator(); + char ts = thousandSeparator(); + std::locale loc; + std::cout << "Original locale: '" << loc.c_str() << '\'' << std::endl; + std::cout << "Decimal point: '" << decimalSeparator() << '\'' << std::endl; + std::cout << "Thousand separator: '" << ts << '\'' << std::endl; + + std::locale::global(std::locale("German")); + std::locale locGerman; + assert (',' == decimalSeparator()); + assert ('.' == thousandSeparator()); + std::cout << "New locale: '" << locGerman.c_str() << '\'' << std::endl; + std::cout << "Decimal point: '" << decimalSeparator() << '\'' << std::endl; + std::cout << "Thousand separator: '" << thousandSeparator() << '\'' << std::endl; + + std::locale::global(std::locale("US")); + std::locale locUS; + assert ('.' == decimalSeparator()); + assert (',' == thousandSeparator()); + std::cout << "New locale: '" << locUS.c_str() << '\'' << std::endl; + std::cout << "Decimal point: '" << decimalSeparator() << '\'' << std::endl; + std::cout << "Thousand separator: '" << thousandSeparator() << '\'' << std::endl; + + std::locale::global(loc); + dp = decimalSeparator(); + ts = thousandSeparator(); + std::cout << "Final locale: '" << loc.c_str() << '\'' << std::endl; + std::cout << "Decimal point: '" << decimalSeparator() << '\'' << std::endl; + std::cout << "Thousand separator: '" << thousandSeparator() << '\'' << std::endl; + assert (dp == decimalSeparator()); + assert (ts == thousandSeparator()); +#else + std::cout << "No locale available, skipping." << std::endl; +#endif +} + + +bool parseStream(const std::string& s, double& 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(); +} + + +void StringTest::benchmark() +{ + Poco::Stopwatch sw; + double number = 1.23456e-123; + std::string num = "1.23456e-123"; + double 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::strtod(num.c_str(), &pC); + 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); + sw.stop(); + std::cout << "strToFloat Number: " << res << std::endl; + double timeStrToFloat = sw.elapsed() / 1000.0; + + sw.restart(); + for (int i = 0; i < 1000000; ++i) std::sscanf(num.c_str(), "%f%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::strtod:\t" << std::setw(10) << std::setfill(' ') << timeStrtod << "[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) << "strToFloat:\t" << std::setw(10) << std::setfill(' ') << timeStrToFloat << "[ms]" << + std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStrToFloat) << '\t' ; + graph = (int) (timeStream / timeStrToFloat); 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() { } @@ -375,6 +652,11 @@ CppUnit::Test* StringTest::suite() CppUnit_addTest(pSuite, StringTest, testReplace); CppUnit_addTest(pSuite, StringTest, testReplaceInPlace); CppUnit_addTest(pSuite, StringTest, testCat); + CppUnit_addTest(pSuite, StringTest, testStringToInt); + CppUnit_addTest(pSuite, StringTest, testStringToFloat); + CppUnit_addTest(pSuite, StringTest, testStringToFloatError); + CppUnit_addTest(pSuite, StringTest, testNumericLocale); + CppUnit_addTest(pSuite, StringTest, benchmark); return pSuite; } diff --git a/Foundation/testsuite/src/StringTest.h b/Foundation/testsuite/src/StringTest.h index 83bbc992e..f05e1d97e 100644 --- a/Foundation/testsuite/src/StringTest.h +++ b/Foundation/testsuite/src/StringTest.h @@ -38,6 +38,7 @@ #include "Poco/Foundation.h" #include "CppUnit/TestCase.h" +#include "Poco/NumericString.h" class StringTest: public CppUnit::TestCase @@ -61,12 +62,61 @@ public: void testReplaceInPlace(); void testCat(); + void testStringToInt(); + void testStringToFloat(); + void testStringToFloatError(); + void testNumericLocale(); + void benchmark(); + void setUp(); void tearDown(); static CppUnit::Test* suite(); private: + + template + void stringToInt() + { + T result = 0; + if (123 <= std::numeric_limits::max()) + assert(strToInt("123", result)); assert(result == 123); + + assert(strToInt("0", result)); assert(result == 0); + assert(strToInt("000", result)); assert(result == 0); + + if (123 < std::numeric_limits::max()) + { assert(strToInt(" 123 ", result)); assert(result == 123); } + if (123 < std::numeric_limits::max()) + { assert(strToInt(" 123", result)); assert(result == 123); } + if (123 < std::numeric_limits::max()) + { assert(strToInt("123 ", result)); assert(result == 123); } + if (std::numeric_limits::is_signed && (-123 > std::numeric_limits::min())) + { assert(strToInt("-123", result)); 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); } + if (0x12ab < std::numeric_limits::max()) + { assert(strToInt("0x12AB", result)); assert(result == 0x12ab); } + if (0x12ab < std::numeric_limits::max()) + { assert(strToInt("0x12aB", result)); assert(result == 0x12ab); } + if (0x98fe < std::numeric_limits::max()) + { assert(strToInt("0X98Fe", result)); assert(result == 0x98fe); } + if (123 < std::numeric_limits::max()) + { assert(strToInt("0x0", result)); 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("0", result, 010)); assert(result == 0); + assert(strToInt("000", result)); assert(result == 0); + } }; diff --git a/JSON/testsuite/src/JSONTest.cpp b/JSON/testsuite/src/JSONTest.cpp index 7c2cca031..a8c088e17 100644 --- a/JSON/testsuite/src/JSONTest.cpp +++ b/JSON/testsuite/src/JSONTest.cpp @@ -299,7 +299,7 @@ void JSONTest::testDouble2Property() Var test = object->get("test"); assert(test.isNumeric()); double value = test; - assert(value == 12e34); + assert(value >= 1.99e34 && value <= 12.01e34); }