diff --git a/.gitignore b/.gitignore index 6f906e7ff..caf247df3 100644 --- a/.gitignore +++ b/.gitignore @@ -43,10 +43,11 @@ CPackSourceConfig.cmake # Logs and databases # ###################### -*.log +*.log* *.sqlite *.db test*.txt +XML/testsuite/rss.xml # OS generated files # ###################### diff --git a/CHANGELOG b/CHANGELOG index d7cebde54..a3d15c1c4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ This is the changelog file for the POCO C++ Libraries. +Release 1.5.0 (2012-12-17) +========================== +- using double-conversion library for floating-point numeric/string conversions Release 1.5.0 (2012-10-14) ========================== diff --git a/Foundation/include/Poco/Config.h b/Foundation/include/Poco/Config.h index 1973ca20e..0079ee0c3 100644 --- a/Foundation/include/Poco/Config.h +++ b/Foundation/include/Poco/Config.h @@ -139,7 +139,7 @@ // Windows CE has no locale support #if defined(_WIN32_WCE) -#define POCO_NO_LOCALE + #define POCO_NO_LOCALE #endif diff --git a/Foundation/include/Poco/FileChannel.h b/Foundation/include/Poco/FileChannel.h index 37a2e6de4..47953f01a 100644 --- a/Foundation/include/Poco/FileChannel.h +++ b/Foundation/include/Poco/FileChannel.h @@ -161,10 +161,10 @@ class Foundation_API FileChannel: public Channel /// * weeks: the maximum age is weeks. /// * months: the maximum age is months, where a month has 30 days. /// - /// The purgeCount property has an integer value that - /// specifies the maximum number of archived log files. - /// If the number is exceeded, archived log files are - /// deleted, starting with the oldest. + /// The purgeCount property has an integer value that specifies the maximum number + /// of archived log files. If the number is exceeded, archived log files are + /// deleted, starting with the oldest. When "none" or empty string are + /// supplied, they reset purgeCount to none (no purging). /// /// The flush property specifies whether each log message is flushed /// immediately to the log file (which may hurt application performance, diff --git a/Foundation/include/Poco/NumericString.h b/Foundation/include/Poco/NumericString.h index 5103ad4f5..ddc006718 100644 --- a/Foundation/include/Poco/NumericString.h +++ b/Foundation/include/Poco/NumericString.h @@ -55,14 +55,12 @@ #include #endif -// TODO: -// POCO version is fast but simplistic, hence less accurate, undef this for -// double-conversion library use in string => float/double conversions (T.B.D.) -// (double-conversion library is always used for float/double => string) -#define POCO_STR_TO_FLOAT_FAST #define POCO_MAX_INT_STRING_LEN 65 #define POCO_MAX_FLT_STRING_LEN 128 +#define POCO_FLT_INF "inf" +#define POCO_FLT_NAN "nan" +#define POCO_FLT_EXP 'e' namespace Poco { @@ -210,173 +208,6 @@ bool strToInt(const std::string& str, I& result, short base, char thSep = ',') { return strToInt(str.c_str(), result, base, thSep); } - - -namespace Impl { - -static char DUMMY_EXP_UNDERFLOW; // dummy default val - -} - - -template -bool strToFloat (const char* pStr, F& result, char& eu = Impl::DUMMY_EXP_UNDERFLOW, char decSep = '.', char thSep = ',') - /// Converts zero-terminated array to floating-point number; - /// Returns true if succesful. Exponent underflow (i.e. loss of precision due to exponent value) - /// 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. - /// This function will perform fast but significant rounding errors may occur after - /// the platform precision is exceeded. - /// For better precision conversion, use double-conversion wrappers (strToFloatDC and strToDoubleDC). -{ -#ifndef POCO_STR_TO_FLOAT_FAST - // TODO: for high-precision, use double-conversion here -#else - poco_assert (decSep != thSep); - - if (pStr == 0 || *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 - - char numSign = 1, expSign = 1; - char state = STATE_LEADING_SPACES; - F mantissa = 0.0, exponent = 0.0, pow10 = 1.0; - - result = 0.0; - eu = 0; - for (; *pStr != '\0'; ++pStr) - { - switch (*pStr) - { - case '.': - if (decSep == '.') - { - if (state >= STATE_DIGITS_AFTER_DEC_POINT) return false; - state = STATE_DIGITS_AFTER_DEC_POINT; - break; - } - else if ((thSep == '.') && (state == STATE_DIGITS_BEFORE_DEC_POINT)) - break; - else - return false; - - case ',': - if (decSep == ',') - { - if (state >= STATE_DIGITS_AFTER_DEC_POINT) return false; - state = STATE_DIGITS_AFTER_DEC_POINT; - break; - } - else if ((thSep == ',') && (state == STATE_DIGITS_BEFORE_DEC_POINT)) - break; - else - return false; - - case ' ': // space (SPC) - if ((thSep == ' ') && (state == STATE_DIGITS_BEFORE_DEC_POINT)) break; - case '\t': // horizontal tab (TAB) - case '\n': // line feed (LF) - case '\v': // vertical tab (VT) - case '\f': // form feed (FF) - case '\r': // carriage return (CR) - if ((state >= STATE_DIGITS_AFTER_DEC_POINT) || (state >= STATE_EXP_DIGITS)) - break; - else 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 += F(*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); -#endif // POCO_STR_TO_FLOAT_FAST -} - - -template -bool strToFloat (const std::string& s, F& result, char& eu = Impl::DUMMY_EXP_UNDERFLOW, char decSep = '.', char thSep = ',') - /// Converts string to floating-point number; - /// This is a wrapper function, for details see see the - /// bool strToFloat(const char*, F&, char&, char, char) implementation. -{ - return strToFloat(s.c_str(), result, eu, decSep, thSep); -} // @@ -636,12 +467,10 @@ bool uIntToStr (T number, unsigned short base, std::string& result, bool prefix // -// Functions using double-conversion library (http://code.google.com/p/double-conversion/). -// Library is the implementation of the Grisu algorithm as described in Florian Loitsch's paper: -// http://florian.loitsch.com/publications/dtoa-pldi2010.pdf +// Wrappers for double-conversion library (http://code.google.com/p/double-conversion/). // -// For a complete (and simpler) story on floating-point numbers rendering accuracy and performance, see: -// http://www.serpentine.com/blog/2011/06/29/here-be-dragons-advances-in-problems-you-didnt-even-know-you-had/ +// Library is the implementation of the algorithm described in Florian Loitsch's paper: +// http://florian.loitsch.com/publications/dtoa-pldi2010.pdf // Foundation_API void floatToStr(char* buffer, @@ -688,12 +517,31 @@ Foundation_API std::string& doubleToStr(std::string& str, /// precision (total number of digits after the decimal point) and width (total length of formatted string). -Foundation_API float strToFloatDC(const char* str); - /// For testing and performance comparison purposes only. +Foundation_API float strToFloat(const char* str); + /// Converts the string of characters into single-precision floating point number. + /// Function uses double_convesrion::DoubleToStringConverter to do the conversion. -Foundation_API double strToDoubleDC(const char* str); - /// For testing and performance comparison purposes only. +Foundation_API bool strToFloat(const std::string&, float& result, char decSep = '.', char thSep = ','); + /// Converts the string of characters into single-precision floating point number. + /// The conversion result is assigned to the result parameter. + /// If decimal separator and/or thousand separator are different from defaults, they should be + /// supplied to ensure proper conversion. + /// + /// Returns true if succesful, false otherwise. + + +Foundation_API double strToDouble(const char* str); + /// Converts the string of characters into double-precision floating point number. + + +Foundation_API bool strToDouble(const std::string& str, double& result, char decSep = '.', char thSep = ','); + /// Converts the string of characters into double-precision floating point number. + /// The conversion result is assigned to the result parameter. + /// If decimal separator and/or thousand separator are different from defaults, they should be + /// supplied to ensure proper conversion. + /// + /// Returns true if succesful, false otherwise. // // end double-conversion functions declarations diff --git a/Foundation/include/Poco/String.h b/Foundation/include/Poco/String.h index f8e2b41ff..228dc946e 100644 --- a/Foundation/include/Poco/String.h +++ b/Foundation/include/Poco/String.h @@ -482,7 +482,7 @@ S& replaceInPlace(S& str, const typename S::value_type* from, const typename S:: template -S& replaceInPlace(S& str, const typename S::value_type from, const typename S::value_type to, typename S::size_type start = 0) +S& replaceInPlace(S& str, const typename S::value_type from, const typename S::value_type to = 0, typename S::size_type start = 0) { if (from == to) return str; @@ -490,13 +490,24 @@ S& replaceInPlace(S& str, const typename S::value_type from, const typename S::v do { pos = str.find(from, start); - if (pos != S::npos) str[pos] = to; + if (pos != S::npos) + { + if (to) str[pos] = to; + else str.erase(pos, 1); + } } while (pos != S::npos); return str; } +template +S& removeInPlace(S& str, const typename S::value_type ch, typename S::size_type start = 0) +{ + return replaceInPlace(str, ch, 0, start); +} + + template S replace(const S& str, const S& from, const S& to, typename S::size_type start = 0) /// Replace all occurences of from (which must not be the empty string) @@ -517,14 +528,36 @@ S replace(const S& str, const typename S::value_type* from, const typename S::va } +template +S replace(const S& str, const typename S::value_type from, const typename S::value_type to = 0, typename S::size_type start = 0) +{ + S result(str); + replaceInPlace(result, from, to, start); + return result; +} + + +template +S remove(const S& str, const typename S::value_type ch, typename S::size_type start = 0) +{ + S result(str); + replaceInPlace(result, ch, 0, start); + return result; +} + + #else -std::string Foundation_API replace(const std::string& str, const std::string& from, const std::string& to, std::string::size_type start = 0); -std::string Foundation_API replace(const std::string& str, const std::string::value_type* from, const std::string::value_type* to, std::string::size_type start = 0); -std::string& Foundation_API replaceInPlace(std::string& str, const std::string& from, const std::string& to, std::string::size_type start = 0); -std::string& Foundation_API replaceInPlace(std::string& str, const std::string::value_type* from, const std::string::value_type* to, std::string::size_type start = 0); - +Foundation_API std::string replace(const std::string& str, const std::string& from, const std::string& to, std::string::size_type start = 0); +Foundation_API std::string replace(const std::string& str, const std::string::value_type* from, const std::string::value_type* to, std::string::size_type start = 0); +Foundation_API std::string replace(const std::string& str, const std::string::value_type from, const std::string::value_type to = 0, std::string::size_type start = 0); +Foundation_API std::string remove(const std::string& str, const std::string::value_type ch, std::string::size_type start = 0); +Foundation_API std::string& replaceInPlace(std::string& str, const std::string& from, const std::string& to, std::string::size_type start = 0); +Foundation_API std::string& replaceInPlace(std::string& str, const std::string::value_type* from, const std::string::value_type* to, std::string::size_type start = 0); +Foundation_API std::string& replaceInPlace(std::string& str, const std::string::value_type from, const std::string::value_type to = 0, std::string::size_type start = 0); +Foundation_API std::string& removeInPlace(std::string& str, const std::string::value_type ch, std::string::size_type start = 0); + #endif diff --git a/Foundation/src/FileChannel.cpp b/Foundation/src/FileChannel.cpp index b5a37c347..ba28b3ddd 100644 --- a/Foundation/src/FileChannel.cpp +++ b/Foundation/src/FileChannel.cpp @@ -327,12 +327,23 @@ void FileChannel::setCompress(const std::string& compress) void FileChannel::setPurgeAge(const std::string& age) { + delete _pPurgeStrategy; + _pPurgeStrategy = 0; + _purgeAge = "none"; + + if (age.empty() || 0 == icompare(age, "none")) + return; + std::string::const_iterator it = age.begin(); std::string::const_iterator end = age.end(); int n = 0; while (it != end && Ascii::isSpace(*it)) ++it; while (it != end && Ascii::isDigit(*it)) { n *= 10; n += *it++ - '0'; } + if (0 == n) + throw InvalidArgumentException("Zero is not valid purge age."); + while (it != end && Ascii::isSpace(*it)) ++it; + std::string unit; while (it != end && Ascii::isAlpha(*it)) unit += *it++; @@ -349,8 +360,7 @@ void FileChannel::setPurgeAge(const std::string& age) factor = 30*Timespan::DAYS; else if (unit != "seconds") throw InvalidArgumentException("purgeAge", age); - - delete _pPurgeStrategy; + _pPurgeStrategy = new PurgeByAgeStrategy(Timespan(factor*n)); _purgeAge = age; } @@ -358,11 +368,20 @@ void FileChannel::setPurgeAge(const std::string& age) void FileChannel::setPurgeCount(const std::string& count) { + delete _pPurgeStrategy; + _pPurgeStrategy = 0; + _purgeAge = "none"; + + if (count.empty() || 0 == icompare(count, "none")) + return; + std::string::const_iterator it = count.begin(); std::string::const_iterator end = count.end(); int n = 0; while (it != end && Ascii::isSpace(*it)) ++it; while (it != end && Ascii::isDigit(*it)) { n *= 10; n += *it++ - '0'; } + if (0 == n) + throw InvalidArgumentException("Zero is not valid purge count."); while (it != end && Ascii::isSpace(*it)) ++it; delete _pPurgeStrategy; diff --git a/Foundation/src/NumberParser.cpp b/Foundation/src/NumberParser.cpp index c2c713bfd..386f0f965 100644 --- a/Foundation/src/NumberParser.cpp +++ b/Foundation/src/NumberParser.cpp @@ -206,8 +206,7 @@ double NumberParser::parseFloat(const std::string& s, char decSep, char thSep) bool NumberParser::tryParseFloat(const std::string& s, double& value, char decSep, char thSep) { - char eu; - return strToFloat(s.c_str(), value, eu, decSep, thSep); + return strToDouble(s.c_str(), value, decSep, thSep); } diff --git a/Foundation/src/NumericString.cpp b/Foundation/src/NumericString.cpp index f11398ccb..ca72260f8 100644 --- a/Foundation/src/NumericString.cpp +++ b/Foundation/src/NumericString.cpp @@ -33,21 +33,22 @@ // DEALINGS IN THE SOFTWARE. // - +// +++ double conversion +++ +#include "diy-fp.cc" +#include "cached-powers.cc" #include "bignum-dtoa.cc" #include "bignum.cc" -#include "cached-powers.cc" -#include "diy-fp.cc" -#include "double-conversion.cc" #include "fast-dtoa.cc" #include "fixed-dtoa.cc" #include "strtod.cc" - +#include "double-conversion.cc" +// --- double conversion --- #include "Poco/NumericString.h" #include "Poco/String.h" #include + namespace { @@ -78,7 +79,7 @@ void insertThousandSep(std::string& str, char thSep, char decSep = '.') while (it != begin) { --it; - if (*it == 'e') break; + if ((*it == 'e') || (*it == 'E')) break; } } if (decPos != std::string::npos) @@ -115,7 +116,7 @@ void floatToStr(char* buffer, int bufferSize, float value, int lowDec, int highD StringBuilder builder(buffer, bufferSize); int flags = DoubleToStringConverter::UNIQUE_ZERO | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; - DoubleToStringConverter dc(flags, "inf", "nan", 'e', lowDec, highDec, 0, 0); + DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, lowDec, highDec, 0, 0); dc.ToShortestSingle(value, &builder); builder.Finalize(); } @@ -143,7 +144,7 @@ void doubleToStr(char* buffer, int bufferSize, double value, int lowDec, int hig StringBuilder builder(buffer, bufferSize); int flags = DoubleToStringConverter::UNIQUE_ZERO | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; - DoubleToStringConverter dc(flags, "inf", "nan", 'e', lowDec, highDec, 0, 0); + DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, lowDec, highDec, 0, 0); dc.ToShortest(value, &builder); builder.Finalize(); } @@ -166,32 +167,59 @@ std::string& doubleToStr(std::string& str, double value, int precision, int widt } -float strToFloatDC(const char* str) +float strToFloat(const char* str) { using namespace double_conversion; - double empty_string_value = 0.0; int processed; int flags = StringToDoubleConverter::ALLOW_LEADING_SPACES | StringToDoubleConverter::ALLOW_TRAILING_SPACES; - StringToDoubleConverter converter(flags, empty_string_value, Single::NaN(), 0, 0); + StringToDoubleConverter converter(flags, 0.0, Single::NaN(), POCO_FLT_INF, POCO_FLT_NAN); float result = converter.StringToFloat(str, strlen(str), &processed); return result; } -double strToDoubleDC(const char* str) +double strToDouble(const char* str) { using namespace double_conversion; - - double empty_string_value = 0.0; int processed; int flags = StringToDoubleConverter::ALLOW_LEADING_SPACES | StringToDoubleConverter::ALLOW_TRAILING_SPACES; - StringToDoubleConverter converter(flags, empty_string_value, Double::NaN(), 0, 0); + StringToDoubleConverter converter(flags, 0.0, Double::NaN(), POCO_FLT_INF, POCO_FLT_NAN); double result = converter.StringToDouble(str, strlen(str), &processed); return result; } +bool strToFloat(const std::string& str, float& result, char decSep, char thSep) +{ + using namespace double_conversion; + + std::string tmp(str); + removeInPlace(tmp, thSep); + removeInPlace(tmp, 'f'); + replaceInPlace(tmp, decSep, '.'); + result = strToFloat(tmp.c_str()); + return !FPEnvironment::isInfinite(result) && + !FPEnvironment::isNaN(result); +} + + +bool strToDouble(const std::string& str, double& result, char decSep, char thSep) +{ + if (str.empty()) return false; + + using namespace double_conversion; + + std::string tmp(str); + removeInPlace(tmp, thSep); + replaceInPlace(tmp, decSep, '.'); + removeInPlace(tmp, 'f'); + result = strToDouble(tmp.c_str()); + return !FPEnvironment::isInfinite(result) && + !FPEnvironment::isNaN(result); +} + + } // namespace Poco diff --git a/Foundation/src/String.cpp b/Foundation/src/String.cpp index 2f30b4ed1..f46b08fc0 100644 --- a/Foundation/src/String.cpp +++ b/Foundation/src/String.cpp @@ -165,6 +165,22 @@ std::string replace(const std::string& str, const std::string::value_type* from, return result; } + +std::string replace(const std::string& str, const std::string::value_type from, const std::string::value_type to, std::string::size_type start) +{ + std::string result(str); + replaceInPlace(result, from, to, start); + return result; +} + + +std::string remove(const std::string& str, const std::string::value_type ch, std::string::size_type start) +{ + std::string result(str); + replaceInPlace(result, ch, 0, start); + return result; +} + std::string& replaceInPlace(std::string& str, const std::string& from, const std::string& to, std::string::size_type start) { @@ -215,6 +231,31 @@ std::string& replaceInPlace(std::string& str, const std::string::value_type* fro } +std::string& replaceInPlace(std::string& str, const std::string::value_type from, const std::string::value_type to, std::string::size_type start) +{ + if (from == to) return str; + + std::string::size_type pos = 0; + do + { + pos = str.find(from, start); + if (pos != std::string::npos) + { + if (to) str[pos] = to; + else str.erase(pos, 1); + } + } while (pos != std::string::npos); + + return str; +} + + +std::string& removeInPlace(std::string& str, const std::string::value_type ch, std::string::size_type start) +{ + return replaceInPlace(str, ch, 0, start); +} + + #endif diff --git a/Foundation/testsuite/src/StringTest.cpp b/Foundation/testsuite/src/StringTest.cpp index 41b27bc07..3257b3e40 100644 --- a/Foundation/testsuite/src/StringTest.cpp +++ b/Foundation/testsuite/src/StringTest.cpp @@ -58,12 +58,14 @@ using Poco::translate; using Poco::translateInPlace; using Poco::replace; using Poco::replaceInPlace; +using Poco::remove; +using Poco::removeInPlace; using Poco::cat; using Poco::strToInt; using Poco::strToFloat; +using Poco::strToDouble; using Poco::intToStr; using Poco::uIntToStr; -using Poco::strToDoubleDC; using Poco::floatToStr; using Poco::doubleToStr; using Poco::thousandSeparator; @@ -329,6 +331,10 @@ void StringTest::testReplace() assert (replace(s, "aa", "xxx") == "xxxbbxxxbb"); assert (replace(s, "aa", "xx", 2) == "aabbxxbb"); + assert (replace(s, 'a', 'x', 2) == "aabbxxbb"); + assert (remove(s, 'a', 2) == "aabbbb"); + assert (remove(s, 'a') == "bbbb"); + assert (remove(s, 'b', 2) == "aaaa"); } @@ -336,7 +342,18 @@ void StringTest::testReplaceInPlace() { std::string s("aabbccdd"); - assert (replaceInPlace(s, std::string("aa"), std::string("xx")) == "xxbbccdd"); + replaceInPlace(s, std::string("aa"), std::string("xx")); + assert (s == "xxbbccdd"); + + s = "aabbccdd"; + replaceInPlace(s, 'a', 'x'); + assert (s == "xxbbccdd"); + replaceInPlace(s, 'x'); + assert (s == "bbccdd"); + removeInPlace(s, 'b', 1); + assert (s == "bccdd"); + removeInPlace(s, 'd'); + assert (s == "bcc"); } @@ -387,10 +404,7 @@ void StringTest::testStringToInt() void StringTest::testStringToFloat() { -#ifndef POCO_NO_FPENVIRONMENT - - double result; - char eu; + float result; std::string sep(".,"); for (int i = 0; i < 2; ++i) @@ -401,58 +415,148 @@ void StringTest::testStringToFloat() char ts = sep[j]; if (ts == ds) continue; - assert(strToFloat("1", result, eu, ds, ts)); + assert(strToFloat("1", result, ds, ts)); assertEqualDelta(1.0, result, 0.01); - assert(strToFloat(format("%c1", ds), result, eu, ds, ts)); + assert(strToFloat(format("%c1", ds), result, ds, ts)); assertEqualDelta(.1, result, 0.01); - assert(strToFloat(format("1%c", ds), result, eu, ds, ts)); + assert(strToFloat(format("1%c", ds), result, ds, ts)); assertEqualDelta(1., result, 0.01); - assert(strToFloat("0", result, eu, ds, ts)); + assert(strToFloat("0", result, ds, ts)); assertEqualDelta(0.0, result, 0.01); - assert(strToFloat(format("0%c", ds), result, eu, ds, ts)); + assert(strToFloat(format("0%c", ds), result, ds, ts)); assertEqualDelta(0.0, result, 0.01); - assert(strToFloat(format("%c0", ds), result, eu, ds, ts)); + assert(strToFloat(format("%c0", ds), result, ds, ts)); assertEqualDelta(0.0, result, 0.01); - assert(strToFloat(format("0%c0", ds), result, eu, ds, ts)); + assert(strToFloat(format("0%c0", ds), result, ds, ts)); assertEqualDelta(0.0, result, 0.01); - assert(strToFloat(format("0%c0", ds), result, eu, ds, ts)); + assert(strToFloat(format("0%c0", ds), result, ds, ts)); assertEqualDelta(0., result, 0.01); - assert(strToFloat(format("0%c0", ds), result, eu, ds, ts)); + assert(strToFloat(format("0%c0", ds), result, ds, ts)); assertEqualDelta(.0, result, 0.01); - assert(strToFloat(format("12%c34", ds), result, eu, ds, ts)); + assert(strToFloat(format("12%c34", ds), result, ds, ts)); assertEqualDelta(12.34, result, 0.01); - assert(strToFloat(format("12%c34f", ds), result, eu, ds, ts)); + assert(strToFloat(format("12%c34", ds), result, ds, ts)); assertEqualDelta(12.34, result, 0.01); - assert(strToFloat(format("12%c34", ds), result, eu, ds, ts)); - assertEqualDelta(12.34, result, 0.01); - assert(strToFloat(format("-12%c34", ds), result, eu, ds, ts)); + assert(strToFloat(format("-12%c34", ds), result, ds, ts)); assertEqualDelta(-12.34, result, 0.01); - assert(strToFloat(format("%c34", ds), result, eu, ds, ts)); + assert(strToFloat(format("%c34", ds), result, ds, ts)); assertEqualDelta(.34, result, 0.01); - assert(strToFloat(format("-%c34", ds), result, eu, ds, ts)); + assert(strToFloat(format("-%c34", ds), result, ds, ts)); assertEqualDelta(-.34, result, 0.01); - assert(strToFloat(format("12%c", ds), result, eu, ds, ts)); + assert(strToFloat(format("12%c", ds), result, ds, ts)); assertEqualDelta(12., result, 0.01); - assert(strToFloat(format("-12%c", ds), result, eu, ds, ts)); + assert(strToFloat(format("-12%c", ds), result, ds, ts)); assertEqualDelta(-12., result, 0.01); - assert(strToFloat("12", result, eu, ds, ts)); + assert(strToFloat("12", result, ds, ts)); assertEqualDelta(12, result, 0.01); - assert(strToFloat("-12", result, eu, ds, ts)); + assert(strToFloat("-12", result, ds, ts)); assertEqualDelta(-12, result, 0.01); - assert(strToFloat(format("12%c3456789012345678901234567890", ds), result, eu, ds, ts)); + assert(strToFloat(format("12%c34", ds), result, ds, ts)); assertEqualDelta(12.34, result, 0.01); - assert(strToFloat(format("1%c234%c3456789012345678901234567890", ts, ds), result, eu, ds, ts)); + assert(strToFloat(format("1%c234%c34", ts, ds), result, ds, ts)); + assertEqualDelta(1234.34, result, 0.01); + assert(strToFloat(format("12%c345%c34", ts, ds), result, ds, ts)); + assertEqualDelta(12345.34, result, 0.01); + assert(strToFloat(format("123%c456%c34", ts, ds), result, ds, ts)); + assertEqualDelta(123456.34, result, 0.01); + assert(strToFloat(format("1%c234%c567%c34", ts, ts, ds), result, ds, ts)); + + if ((std::numeric_limits::max() / 10) < 1.23456e10) + fail ("test value larger than max value for this platform"); + else + { + float d = 12e34f; + assert(strToFloat(format("12e34", ds), result, ds, ts)); + assertEqualDelta(d, result, 0.01e34); + + d = 1.234e30f; + assert(strToFloat(format("1%c234e30", ds), result, ds, ts)); + assertEqualDelta(d, result, 0.01); + assert(strToFloat(format("1%c234E+30", ds), result, ds, ts)); + assertEqualDelta(d, result, 0.01); + } + + float d = 12.34e-10f; + assert(strToFloat(format("12%c34e-10", ds), result, ds, ts)); + assertEqualDelta(d, result, 0.01); + assert(strToFloat(format("-12%c34", ds), result, ds, ts)); + assertEqualDelta(-12.34, result, 0.01); + + assert(strToFloat(format(" 12%c34", ds), result, ds, ts)); + assertEqualDelta(12.34, result, 0.01); + assert(strToFloat(format("12%c34 ", ds), result, ds, ts)); + assertEqualDelta(12.34, result, 0.01); + assert(strToFloat(format(" 12%c34 ", ds), result, ds, ts)); + assertEqualDelta(12.34, result, 0.01); + } + } +} + + +void StringTest::testStringToDouble() +{ + double result; + std::string sep(".,"); + + for (int i = 0; i < 2; ++i) + { + char ds = sep[i]; + for (int j = 0; j < 2; ++j) + { + char ts = sep[j]; + if (ts == ds) continue; + + assert(strToDouble("1", result, ds, ts)); + assertEqualDelta(1.0, result, 0.01); + assert(strToDouble(format("%c1", ds), result, ds, ts)); + assertEqualDelta(.1, result, 0.01); + assert(strToDouble(format("1%c", ds), result, ds, ts)); + assertEqualDelta(1., result, 0.01); + assert(strToDouble("0", result, ds, ts)); + assertEqualDelta(0.0, result, 0.01); + assert(strToDouble(format("0%c", ds), result, ds, ts)); + assertEqualDelta(0.0, result, 0.01); + assert(strToDouble(format("%c0", ds), result, ds, ts)); + assertEqualDelta(0.0, result, 0.01); + assert(strToDouble(format("0%c0", ds), result, ds, ts)); + assertEqualDelta(0.0, result, 0.01); + assert(strToDouble(format("0%c0", ds), result, ds, ts)); + assertEqualDelta(0., result, 0.01); + assert(strToDouble(format("0%c0", ds), result, ds, ts)); + assertEqualDelta(.0, result, 0.01); + assert(strToDouble(format("12%c34", ds), result, ds, ts)); + assertEqualDelta(12.34, result, 0.01); + assert(strToDouble(format("12%c34", ds), result, ds, ts)); + assertEqualDelta(12.34, result, 0.01); + assert(strToDouble(format("-12%c34", ds), result, ds, ts)); + assertEqualDelta(-12.34, result, 0.01); + assert(strToDouble(format("%c34", ds), result, ds, ts)); + assertEqualDelta(.34, result, 0.01); + assert(strToDouble(format("-%c34", ds), result, ds, ts)); + assertEqualDelta(-.34, result, 0.01); + assert(strToDouble(format("12%c", ds), result, ds, ts)); + assertEqualDelta(12., result, 0.01); + assert(strToDouble(format("-12%c", ds), result, ds, ts)); + assertEqualDelta(-12., result, 0.01); + assert(strToDouble("12", result, ds, ts)); + assertEqualDelta(12, result, 0.01); + assert(strToDouble("-12", result, ds, ts)); + assertEqualDelta(-12, result, 0.01); + assert(strToDouble(format("12%c3456789012345678901234567890", ds), result, ds, ts)); + assertEqualDelta(12.34, result, 0.01); + + assert(strToDouble(format("1%c234%c3456789012345678901234567890", ts, ds), result, ds, ts)); assertEqualDelta(1234.3456789, result, 0.00000001); - assert(strToFloat(format("12%c345%c3456789012345678901234567890", ts, ds), result, eu, ds, ts)); + assert(strToDouble(format("12%c345%c3456789012345678901234567890", ts, ds), result, ds, ts)); assertEqualDelta(12345.3456789, result, 0.00000001); - assert(strToFloat(format("123%c456%c3456789012345678901234567890", ts, ds), result, eu, ds, ts)); + assert(strToDouble(format("123%c456%c3456789012345678901234567890", ts, ds), result, ds, ts)); assertEqualDelta(123456.3456789, result, 0.00000001); - assert(strToFloat(format("1%c234%c567%c3456789012345678901234567890", ts, ts, ds), result, eu, ds, ts)); + assert(strToDouble(format("1%c234%c567%c3456789012345678901234567890", ts, ts, ds), result, ds, ts)); assertEqualDelta(1234567.3456789, result, 0.00000001); - assert(strToFloat(format("12%c345%c678%c3456789012345678901234567890", ts, ts, ds), result, eu, ds, ts)); + assert(strToDouble(format("12%c345%c678%c3456789012345678901234567890", ts, ts, ds), result, ds, ts)); assertEqualDelta(12345678.3456789, result, 0.00000001); - assert(strToFloat(format("123%c456%c789%c3456789012345678901234567890", ts, ts, ds), result, eu, ds, ts)); + assert(strToDouble(format("123%c456%c789%c3456789012345678901234567890", ts, ts, ds), result, ds, ts)); assertEqualDelta(123456789.3456789, result, 0.00000001); if ((std::numeric_limits::max() / 10) < 1.23456e10) @@ -460,96 +564,89 @@ void StringTest::testStringToFloat() else { double d = 12e34; - assert(strToFloat(format("12e34", ds), result, eu, ds, ts)); + assert(strToDouble(format("12e34", ds), result, ds, ts)); assertEqualDelta(d, result, 0.01e34); d = 1.234e100; - assert(strToFloat(format("1%c234e100", ds), result, eu, ds, ts)); + assert(strToDouble(format("1%c234e100", ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); - assert(strToFloat(format("1%c234E+100", ds), result, eu, ds, ts)); + assert(strToDouble(format("1%c234E+100", ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); d = 1.234e-100; - assert(strToFloat(format("1%c234E-100", ds), result, eu, ds, ts)); + assert(strToDouble(format("1%c234E-100", ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); d = -1.234e100; - assert(strToFloat(format("-1%c234e+100", ds), result, eu, ds, ts)); + assert(strToDouble(format("-1%c234e+100", ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); - assert(strToFloat(format("-1%c234E100", ds), result, eu, ds, ts)); + assert(strToDouble(format("-1%c234E100", ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); d = 1.234e-100; - assert(strToFloat(format(" 1%c234e-100 ", ds), result, eu, ds, ts)); + assert(strToDouble(format(" 1%c234e-100 ", ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); - assert(strToFloat(format(" 1%c234e-100 ", ds), result, eu, ds, ts)); + assert(strToDouble(format(" 1%c234e-100 ", ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); - assert(strToFloat(format(" 1%c234e-100 ", ds), result, eu, ds, ts)); + assert(strToDouble(format(" 1%c234e-100 ", ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); d = 1234.234e-100; - assert(strToFloat(format(" 1%c234%c234e-100 ", ts, ds), result, eu, ds, ts)); + assert(strToDouble(format(" 1%c234%c234e-100 ", ts, ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); d = 12345.234e-100; - assert(strToFloat(format(" 12%c345%c234e-100 ", ts, ds), result, eu, ds, ts)); + assert(strToDouble(format(" 12%c345%c234e-100 ", ts, ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); d = 123456.234e-100; - assert(strToFloat(format(" 123%c456%c234e-100 ", ts, ds), result, eu, ds, ts)); + assert(strToDouble(format(" 123%c456%c234e-100 ", ts, ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); d = -1234.234e-100; - assert(strToFloat(format(" -1%c234%c234e-100 ", ts, ds), result, eu, ds, ts)); + assert(strToDouble(format(" -1%c234%c234e-100 ", ts, ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); d = -12345.234e-100; - assert(strToFloat(format(" -12%c345%c234e-100 ", ts, ds), result, eu, ds, ts)); + assert(strToDouble(format(" -12%c345%c234e-100 ", ts, ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); d = -123456.234e-100; char ou = 0; - assert(strToFloat(format(" -123%c456%c234e-100 ", ts, ds), result, eu, ds, ts)); + assert(strToDouble(format(" -123%c456%c234e-100 ", ts, ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); assert (ou == 0); } double d = 12.34e-10; - assert(strToFloat(format("12%c34e-10", ds), result, eu, ds, ts)); + assert(strToDouble(format("12%c34e-10", ds), result, ds, ts)); assertEqualDelta(d, result, 0.01); - assert(strToFloat(format("-12%c34", ds), result, eu, ds, ts)); + assert(strToDouble(format("-12%c34", ds), result, ds, ts)); assertEqualDelta(-12.34, result, 0.01); - assert(strToFloat(format(" 12%c34", ds), result, eu, ds, ts)); + assert(strToDouble(format(" 12%c34", ds), result, ds, ts)); assertEqualDelta(12.34, result, 0.01); - assert(strToFloat(format("12%c34 ", ds), result, eu, ds, ts)); + assert(strToDouble(format("12%c34 ", ds), result, ds, ts)); assertEqualDelta(12.34, result, 0.01); - assert(strToFloat(format(" 12%c34 ", ds), result, eu, ds, ts)); + assert(strToDouble(format(" 12%c34 ", ds), result, ds, ts)); assertEqualDelta(12.34, result, 0.01); } } -#endif // POCO_NO_FPENVIRONMENT } void StringTest::testStringToFloatError() { -#if !defined(POCO_NO_FPENVIRONMENT) && !defined(POCO_NO_LOCALE) 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)); + assert (!strToDouble(format("a12%c3", ds), result)); + assert (!strToDouble(format("1b2%c3", ds), result)); + assert (!strToDouble(format("12c%c3", ds), result)); + assert (!strToDouble(format("12%cx3", ds), result)); 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 + assert(!strToDouble(format("123%c456%c234e1000000", ts, ds), result)); + assert(!strToDouble(format("123%c456%c234e+1000000", ts, ds), result)); + //assert(!strToDouble(0, result, ou)); // strToDouble is resilient to null pointers + assert(!strToDouble("", result)); } @@ -685,10 +782,10 @@ void StringTest::benchmarkStrToFloat() // POCO Way sw.restart(); char ou = 0; - for (int i = 0; i < 1000000; ++i) strToFloat(num.c_str(), res, ou); + for (int i = 0; i < 1000000; ++i) strToDouble(num, res, ou); sw.stop(); - std::cout << "strToFloat Number: " << res << std::endl; - double timeStrToFloat = sw.elapsed() / 1000.0; + std::cout << "strToDouble Number: " << res << std::endl; + double timeStrToDouble = sw.elapsed() / 1000.0; // standard sscanf sw.restart(); @@ -699,7 +796,7 @@ void StringTest::benchmarkStrToFloat() // double-conversion Strtod sw.restart(); - for (int i = 0; i < 1000000; ++i) strToDoubleDC(num.c_str()); + 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; @@ -712,9 +809,9 @@ void StringTest::benchmarkStrToFloat() 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) << "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) << "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' ; @@ -955,13 +1052,14 @@ CppUnit::Test* StringTest::suite() CppUnit_addTest(pSuite, StringTest, testCat); CppUnit_addTest(pSuite, StringTest, testStringToInt); CppUnit_addTest(pSuite, StringTest, testStringToFloat); + CppUnit_addTest(pSuite, StringTest, testStringToDouble); CppUnit_addTest(pSuite, StringTest, testStringToFloatError); CppUnit_addTest(pSuite, StringTest, testNumericLocale); - CppUnit_addTest(pSuite, StringTest, benchmarkStrToFloat); + //CppUnit_addTest(pSuite, StringTest, benchmarkStrToFloat); //CppUnit_addTest(pSuite, StringTest, benchmarkStrToInt); CppUnit_addTest(pSuite, StringTest, testIntToString); CppUnit_addTest(pSuite, StringTest, testFloatToString); - CppUnit_addTest(pSuite, StringTest, benchmarkFloatToStr); + //CppUnit_addTest(pSuite, StringTest, benchmarkFloatToStr); return pSuite; } diff --git a/Foundation/testsuite/src/StringTest.h b/Foundation/testsuite/src/StringTest.h index d3cc7048c..54dadde89 100644 --- a/Foundation/testsuite/src/StringTest.h +++ b/Foundation/testsuite/src/StringTest.h @@ -65,6 +65,7 @@ public: void testStringToInt(); void testStringToFloat(); + void testStringToDouble(); void testStringToFloatError(); void testNumericLocale(); void benchmarkStrToFloat(); diff --git a/Net/src/SMTPChannel.cpp b/Net/src/SMTPChannel.cpp index 1a5b6266a..96df210df 100644 --- a/Net/src/SMTPChannel.cpp +++ b/Net/src/SMTPChannel.cpp @@ -138,11 +138,19 @@ void SMTPChannel::log(const Message& msg) Poco::FileInputStream fis(_attachment, std::ios::in | std::ios::binary | std::ios::ate); if (fis.good()) { - int size = fis.tellg(); - char* pMem = new char [size]; + typedef std::allocator::size_type SST; + + std::streamoff size = fis.tellg(); + poco_assert (std::numeric_limits::max() >= size); + poco_assert (std::numeric_limits::max() >= size); + char* pMem = new char [static_cast(size)]; fis.seekg(std::ios::beg); fis.read(pMem, size); - message.addAttachment(_attachment, new StringPartSource(std::string(pMem, size), _type, _attachment)); + message.addAttachment(_attachment, + new StringPartSource(std::string(pMem, static_cast(size)), + _type, + _attachment)); + delete [] pMem; } }