Re: [Quickfix-developers] FieldConvertor.h optimizations
Brought to you by:
orenmnero
From: Oren M. <or...@qu...> - 2004-08-13 16:28:47
|
Very silly of me, I was looking at the wrong numbers. My results look similar to yours. I've checked in the changes. --oren On Aug 13, 2004, at 10:51 AM, Oren Miller wrote: > Thanks Kristopher, > > I ran these through and was able to see nice performance improvements > on serializing to strings, however for some reasons the performance > for serializing from strings remained unchanged on the systems I > tested under. Also converting from doubles to strings showed no > improvement. I tried this under a i686 Xeon and a powerpc chipsets. > > --oren > > On Aug 12, 2004, at 2:13 PM, Peterson, Kristofer wrote: > >> QuickFIX Documentation: >> http://www.quickfixengine.org/quickfix/doc/html/index.html >> QuickFIX FAQ: >> http://www.quickfixengine.org/wikifix/index.php?QuickFixFAQ >> QuickFIX Support: http://www.quickfixengine.org/services.html >> >> I have optimized some of the field convertors in FieldConvertors.h >> and have achieved substantial improvements in performance (generally >> 50% - 200% faster). I include a patch against the FieldConvertors.h >> file from quickfix-1.7.1. >> >> The code is admittedly 'C-ish', however no interface has been changed >> and I believe the results speak for themselves. >> >> Replacing strtod() in the double to string conversion would probably >> be a significant win, although I don't know of any efficient >> algorithms for this conversion. >> >> Finally, time stamp conversions should probably be added to pt as >> well. >> >> Cheers, >> >> - Kris >> >> The times were obtained from pt running on an AthlonXP 2500 running >> Linux kernel 2.6.7 compiled with g++ 3.3.4. 'normal' builds used the >> existing FieldConvertors.h, 'modified' builds used the modified >> FieldConvertors.h. >> >> Optimization flags for the builds were as follows: >> >> quickfix-debug-modified/Makefile:CFLAGS = -g -Wall >> quickfix-debug-normal/Makefile:CFLAGS = -g -Wall >> quickfix-optimized-modified/Makefile:CFLAGS = -g -O3 -ffast-math >> -mcpu=athlon-xp -Wall >> quickfix-optimized-normal/Makefile:CFLAGS = -g -O3 -ffast-math >> -mcpu=athlon-xp -Wall >> >> times-debug-normal.txt : peterson@karl src $ ./pt -c 100000 >> times-debug-modified.txt : peterson@karl src $ ./pt -c 100000 >> times-optimized-normal.txt : peterson@karl src $ ./pt -c 100000 >> times-optimized-modified.txt: peterson@karl src $ ./pt -c 100000 >> >> : Converting integers to strings: >> times-debug-normal.txt : num: 100000, seconds: 0.259, >> num_per_second: 386100 >> times-debug-modified.txt : num: 100000, seconds: 0.032, >> num_per_second: 3.125e+06 >> times-optimized-normal.txt : num: 100000, seconds: 0.183, >> num_per_second: 546448 >> times-optimized-modified.txt: num: 100000, seconds: 0.026, >> num_per_second: 3.84615e+06 >> : Converting strings to integers: >> times-debug-normal.txt : num: 100000, seconds: 0.024, >> num_per_second: 4.16667e+06 >> times-debug-modified.txt : num: 100000, seconds: 0.008, >> num_per_second: 1.25e+07 >> times-optimized-normal.txt : num: 100000, seconds: 0.016, >> num_per_second: 6.25e+06 >> times-optimized-modified.txt: num: 100000, seconds: 0.004, >> num_per_second: 2.5e+07 >> : Converting doubles to strings: >> times-debug-normal.txt : num: 100000, seconds: 0.711, >> num_per_second: 140647 >> times-debug-modified.txt : num: 100000, seconds: 0.243, >> num_per_second: 411523 >> times-optimized-normal.txt : num: 100000, seconds: 0.531, >> num_per_second: 188324 >> times-optimized-modified.txt: num: 100000, seconds: 0.236, >> num_per_second: 423729 >> : Converting strings to doubles: >> times-debug-normal.txt : num: 100000, seconds: 0.987, >> num_per_second: 101317 >> times-debug-modified.txt : num: 100000, seconds: 0.058, >> num_per_second: 1.72414e+06 >> times-optimized-normal.txt : num: 100000, seconds: 0.725, >> num_per_second: 137931 >> times-optimized-modified.txt: num: 100000, seconds: 0.062, >> num_per_second: 1.6129e+06 >> : Creating Heartbeat messages: >> times-debug-normal.txt : num: 100000, seconds: 1.823, >> num_per_second: 54854.6 >> times-debug-modified.txt : num: 100000, seconds: 1.166, >> num_per_second: 85763.3 >> times-optimized-normal.txt : num: 100000, seconds: 1.242, >> num_per_second: 80515.3 >> times-optimized-modified.txt: num: 100000, seconds: 0.825, >> num_per_second: 121212 >> : Serializing Heartbeat messages to >> strings: >> times-debug-normal.txt : num: 100000, seconds: 2.35, >> num_per_second: 42553.2 >> times-debug-modified.txt : num: 100000, seconds: 1.198, >> num_per_second: 83472.5 >> times-optimized-normal.txt : num: 100000, seconds: 1.425, >> num_per_second: 70175.4 >> times-optimized-modified.txt: num: 100000, seconds: 0.707, >> num_per_second: 141443 >> : Serializing Heartbeat messages from >> strings: >> times-debug-normal.txt : num: 100000, seconds: 4.749, >> num_per_second: 21057.1 >> times-debug-modified.txt : num: 100000, seconds: 2.671, >> num_per_second: 37439.2 >> times-optimized-normal.txt : num: 100000, seconds: 3.053, >> num_per_second: 32754.7 >> times-optimized-modified.txt: num: 100000, seconds: 1.782, >> num_per_second: 56116.7 >> : Creating NewOrderSingle messages: >> times-debug-normal.txt : num: 100000, seconds: 6.422, >> num_per_second: 15571.5 >> times-debug-modified.txt : num: 100000, seconds: 3.937, >> num_per_second: 25400.1 >> times-optimized-normal.txt : num: 100000, seconds: 4.778, >> num_per_second: 20929.3 >> times-optimized-modified.txt: num: 100000, seconds: 3.026, >> num_per_second: 33046.9 >> : Serializing NewOrderSingle messages to >> strings: >> times-debug-normal.txt : num: 100000, seconds: 6.429, >> num_per_second: 15554.5 >> times-debug-modified.txt : num: 100000, seconds: 3.84, >> num_per_second: 26041.7 >> times-optimized-normal.txt : num: 100000, seconds: 4.874, >> num_per_second: 20517 >> times-optimized-modified.txt: num: 100000, seconds: 3.029, >> num_per_second: 33014.2 >> : Serializing NewOrderSingle messages >> from strings: >> times-debug-normal.txt : num: 100000, seconds: 9.608, >> num_per_second: 10408 >> times-debug-modified.txt : num: 100000, seconds: 5.385, >> num_per_second: 18570.1 >> times-optimized-normal.txt : num: 100000, seconds: 7.202, >> num_per_second: 13885 >> times-optimized-modified.txt: num: 100000, seconds: 3.779, >> num_per_second: 26462 >> : Creating QuoteRequest messages: >> times-debug-normal.txt : num: 100000, seconds: 93.295, >> num_per_second: 1071.87 >> times-debug-modified.txt : num: 100000, seconds: 46.975, >> num_per_second: 2128.79 >> times-optimized-normal.txt : num: 100000, seconds: 61.148, >> num_per_second: 1635.38 >> times-optimized-modified.txt: num: 100000, seconds: 33.904, >> num_per_second: 2949.5 >> : Serializing QuoteRequest messages to >> strings: >> times-debug-normal.txt : num: 100000, seconds: 11.052, >> num_per_second: 9048.14 >> times-debug-modified.txt : num: 100000, seconds: 10.691, >> num_per_second: 9353.66 >> times-optimized-normal.txt : num: 100000, seconds: 5.667, >> num_per_second: 17646 >> times-optimized-modified.txt: num: 100000, seconds: 4.808, >> num_per_second: 20798.7 >> : Serializing QuoteRequest messages from >> strings: >> times-debug-normal.txt : num: 100000, seconds: 74.91, >> num_per_second: 1334.94 >> times-debug-modified.txt : num: 100000, seconds: 48.556, >> num_per_second: 2059.48 >> times-optimized-normal.txt : num: 100000, seconds: 47.944, >> num_per_second: 2085.77 >> times-optimized-modified.txt: num: 100000, seconds: 28.627, >> num_per_second: 3493.21 >> : Reading fields from QuoteRequest >> message: >> times-debug-normal.txt : num: 100000, seconds: 34.526, >> num_per_second: 2896.37 >> times-debug-modified.txt : num: 100000, seconds: 12.988, >> num_per_second: 7699.41 >> times-optimized-normal.txt : num: 100000, seconds: 21.891, >> num_per_second: 4568.09 >> times-optimized-modified.txt: num: 100000, seconds: 6.875, >> num_per_second: 14545.5 >> : Storing NewOrderSingle messages: >> times-debug-normal.txt : num: 100000, seconds: 0.765, >> num_per_second: 130719 >> times-debug-modified.txt : num: 100000, seconds: 0.71, >> num_per_second: 140845 >> times-optimized-normal.txt : num: 100000, seconds: 0.575, >> num_per_second: 173913 >> times-optimized-modified.txt: num: 100000, seconds: 0.521, >> num_per_second: 191939 >> : Validating NewOrderSingle messages with >> no data dictionary: >> times-debug-normal.txt : num: 100000, seconds: 1.489, >> num_per_second: 67159.2 >> times-debug-modified.txt : num: 100000, seconds: 0.859, >> num_per_second: 116414 >> times-optimized-normal.txt : num: 100000, seconds: 0.843, >> num_per_second: 118624 >> times-optimized-modified.txt: num: 100000, seconds: 0.426, >> num_per_second: 234742 >> : Validating NewOrderSingle messages with >> data dictionary: >> times-debug-normal.txt : num: 100000, seconds: 7.732, >> num_per_second: 12933.3 >> times-debug-modified.txt : num: 100000, seconds: 6.105, >> num_per_second: 16380 >> times-optimized-normal.txt : num: 100000, seconds: 2.485, >> num_per_second: 40241.4 >> times-optimized-modified.txt: num: 100000, seconds: 1.717, >> num_per_second: 58241.1 >> >> >> --- quickfix/src/C++/FieldConvertors.h 2004-04-29 20:58:20.000000000 >> +0100 >> +++ quickfix-debug-modified/src/C++/FieldConvertors.h 2004-08-12 >> 19:13:22.036829060 +0100 >> @@ -1,3 +1,4 @@ >> + >> /* -*- C++ -*- */ >> >> >> / >> ********************************************************************** >> ****** >> @@ -32,6 +33,76 @@ >> >> namespace FIX >> { >> +template<class T> >> +static inline char * integer_to_string (char * buf, const size_t >> len, T t) >> +{ >> + const bool is_neg = t < 0; >> + char * p = buf + len; >> + >> + *--p = '\0'; >> + >> + if (is_neg) { >> + if (t == std::numeric_limits<T>::min()) { >> + *--p = '0' + (10-t%10)%10; >> + t/=10; >> + } >> + t=-t; >> + do { >> + *--p = '0' + t % 10; >> + t /= 10; >> + } while (t > 0); >> + *--p = '-'; >> + } else { >> + do { >> + *--p = '0' + t % 10; >> + t /= 10; >> + } while (t > 0); >> + } >> + return p; >> +} >> + >> + >> + >> +template<class T> >> +static inline char * integer_to_string_padded (char * buf, >> + const size_t len, >> + T t, >> + const size_t width = 0, >> + const char padding_char = '0') >> +{ >> + if (!width) return integer_to_string (buf, len, t); >> + >> + const bool is_neg = t < 0; >> + char * p = buf + len; >> + >> + *--p = '\0'; >> + >> + if (is_neg) { >> + if (t == std::numeric_limits<T>::min()) { >> + *--p = '0' + (10-t%10)%10; >> + t/=10; >> + } >> + t=-t; >> + do { >> + *--p = '0' + t % 10; >> + t /= 10; >> + } while (t > 0); >> + if (p > buf) >> + *--p = '-'; >> + } else { >> + do { >> + *--p = '0' + t % 10; >> + t /= 10; >> + } while (t > 0); >> + } >> + const char * stop_p = buf + len - width - 1; >> + if (stop_p < buf) stop_p = buf; >> + while (p > stop_p) >> + *--p = padding_char; >> + return p; >> +} >> + >> + >> /// Empty convertor is a no-op. >> struct EmptyConvertor >> { >> @@ -46,21 +117,32 @@ >> { >> static std::string convert( long value ) >> { >> - char temp[ 12 ]; >> - memset( temp, 0, 12 * sizeof( char ) ); >> - sprintf( temp, "%d", ( int ) value ); >> - return temp; >> + // buffer is big enough for significant digits and extra digit, >> minus and null >> + char buffer [std::numeric_limits<long>::digits10 + 3]; >> + const char * const start = integer_to_string (buffer, sizeof >> (buffer), value); >> + return std::string (start, buffer + sizeof (buffer) - start - 1); >> } >> >> static bool convert( const std::string& value, long& result ) >> { >> const char * str = value.c_str(); >> - if ( *str == '-' ) ++str; >> - for ( const char * p = str; *p != 0; ++p ) >> - { >> - if ( !isdigit( *p ) ) return false; >> + bool is_neg = false; >> + long x = 0; >> + >> + if (*str == '-') { >> + is_neg = true; >> + ++str; >> } >> - result = atol( value.c_str() ); >> + >> + do { >> + const int c = *str - '0'; >> + if (c < 0 || 9 < c) return false; >> + x = 10 * x + c; >> + } while (*++str); >> + >> + if (is_neg) x = -x; >> + >> + result = x; >> return true; >> } >> >> @@ -81,9 +163,10 @@ >> throw( FieldConvertError& ) >> { >> if ( value > 255 || value < 0 ) throw FieldConvertError(); >> - std::stringstream stream; >> - stream << std::setw( 3 ) << std::setfill( '0' ) << value; >> - return stream.str(); >> + char result [4]; >> + if (integer_to_string_padded (result, sizeof(result), value, 3, >> '0') != result) >> + throw FieldConvertError(); >> + return std::string (result, 3); >> } >> >> static bool convert( const std::string& value, long& result ) >> @@ -104,53 +187,32 @@ >> static std::string convert( double value ) >> { >> char result[ 32 ]; >> - memset( result, 0, 32 * sizeof( char ) ); >> - sprintf( result, "%.15g", value ); >> - return result; >> + return std::string (result, std::sprintf( result, "%.15g", value >> )); >> } >> >> static bool convert( const std::string& value, double& result ) >> { >> - if ( !value.size() ) return false; >> - if ( value == "." ) return false; >> - >> - result = atof( const_cast < char* > ( value.c_str() ) ); >> - std::string stripped = value; >> - >> - // add leading zero if none exists >> - if ( *stripped.begin() == '.' ) >> - stripped = '0' + stripped; >> - >> - // remove extra leading zeros >> - while ( stripped.size() > 1 >> - && *(stripped.begin()) == '0' >> - && isdigit(*(stripped.begin()+1)) ) >> - { >> - stripped.erase( stripped.begin() ); >> + const char * i = value.c_str(); >> + >> + if (!*i) return false; // Catch null >> strings >> + if (*i == '-' && !*++i) return false; // Eat >> leading '-' and recheck for null string >> + >> + bool have_digit = false; >> + >> + if (std::isdigit (*i)) { >> + have_digit = true; >> + while (std::isdigit (*++i)); >> } >> >> - if ( stripped.find( '.' ) != std::string::npos ) >> - { >> - std::string::reverse_iterator r = stripped.rbegin(); >> - while ( r != stripped.rend() && ( *r == '0' || *r == '.' ) ) >> - { >> - if ( *r == '.' ) { stripped.resize( stripped.size() - 1 ); >> break; } >> - stripped.resize( stripped.size() - 1 ); r++; >> - } >> - } >> - if ( *stripped.begin() == '0' ) >> - { >> - std::string::size_type i = stripped.find_first_not_of( '0' ); >> - std::string::size_type dot = stripped.find( '.' ); >> - if ( i != dot ) >> - stripped = stripped.substr( i ); >> + if (*i == '.' && std::isdigit (*++i)) { >> + have_digit = true; >> + while (std::isdigit (*++i)); >> } >> >> - std::string converted = convert( result ); >> - if ( stripped.size() != converted.size() ) return false; >> + if (*i || !have_digit) return false; >> >> - // strcmp is being used because == operator is funky under linux >> - return ( strcmp( stripped.c_str(), converted.c_str() ) == 0 ); >> + result = std::strtod (value.c_str(), 0); >> + return true; >> } >> >> static double convert( const std::string& value ) >> @@ -169,16 +231,13 @@ >> { >> static std::string convert( char value ) >> { >> - char temp[ 2 ]; >> - temp[ 0 ] = value; >> - temp[ 1 ] = '\0'; >> - return temp; >> + return std::string (1, value); >> } >> >> static bool convert( const std::string& value, char& result ) >> { >> - if ( value.size() == 0 ) return false; >> - result = *value.c_str(); >> + if ( value.size() != 1 ) return false; >> + result = value[0]; >> return true; >> } >> >> @@ -197,15 +256,19 @@ >> struct BoolConvertor >> { >> static std::string convert( bool value ) >> - { return value ? "Y" : "N"; } >> + { >> + const char ch = value ? 'Y' : 'N'; >> + return std::string (1, ch); >> + } >> >> static bool convert( const std::string& value, bool& result ) >> { >> - if ( value == "Y" ) result = true; >> - else >> - if ( value == "N" ) result = false; >> - else >> - return false; >> + if (value.size() != 1) return false; >> + switch (value[0]) { >> + case 'Y': result = true; break; >> + case 'N': result = false; break; >> + default: return false; >> + } >> return true; >> } >> >> @@ -227,13 +290,19 @@ >> throw( FieldConvertError& ) >> { >> char result[ 18+4 ]; >> - int len = strftime( result, 18, "%Y%m%d-%H:%M:%S", value ); >> - if ( len != 17 ) throw FieldConvertError(); >> + >> + integer_to_string_padded (result , 5, static_cast<const >> tm*>(value)->tm_year + 1900, 4, '0'); >> + integer_to_string_padded (result + 4, 3, static_cast<const >> tm*>(value)->tm_mon + 1, 2, '0'); >> + integer_to_string_padded (result + 6, 3, static_cast<const >> tm*>(value)->tm_mday, 2, '0'); result[8] = '-'; >> + integer_to_string_padded (result + 9, 3, static_cast<const >> tm*>(value)->tm_hour, 2, '0'); result[11] = ':'; >> + integer_to_string_padded (result + 12, 3, static_cast<const >> tm*>(value)->tm_min, 2, '0'); result[14] = ':'; >> + integer_to_string_padded (result + 15, 3, static_cast<const >> tm*>(value)->tm_sec, 2, '0'); >> >> if(true==showMilliseconds) >> { >> - len = sprintf(result+17,".%03d",value.getMillisecond()); >> - if ( len != 4) throw FieldConvertError(); >> + result[17] = '.'; >> + if (integer_to_string_padded (result + 18, 4, >> value.getMillisecond(), 3, '0') != result + 18) >> + throw FieldConvertError(); >> } >> return result; >> } >> @@ -242,19 +311,73 @@ >> throw( FieldConvertError& ) >> { >> UtcTimeStamp result; >> - const char* val = value.c_str(); >> - const char* len = strptime( val, "%Y%m%d-%H:%M:%S", result ); >> - if ( len - val != 17 ) throw FieldConvertError(); >> - >> - // if we have milliseconds in the string, *len should be ".sss" >> - result.setMillisecond(0); >> - if(NULL != len && strlen(len)==4 && len[0] == '.') >> - { >> - int ms = atoi(&len[1]); >> - if(ms < 0 || ms > 999) throw FieldConvertError(); >> - result.setMillisecond(ms); >> + bool have_milliseconds = false; >> + >> + switch (value.size()) { >> + case 21: have_milliseconds = true; >> + case 17: break; >> + default: throw FieldConvertError(); >> } >> >> + int i = 0; >> + for (int c=0; c<8; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (value[i++] != '-') throw >> FieldConvertError(); >> + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (value[i++] != ':') throw >> FieldConvertError(); >> + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (value[i++] != ':') throw >> FieldConvertError(); >> + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (have_milliseconds) { >> + if (value[i++] != '.') throw FieldConvertError(); >> + for (int c=0; c<3; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + } >> + >> + tm & result_tm = *static_cast<tm*>(result); >> + >> + i = 0; >> + >> + result_tm.tm_year = value[i++] - '0'; >> + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; >> + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; >> + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; >> + result_tm.tm_year -= 1900; >> + >> + result_tm.tm_mon = value[i++] - '0'; >> + result_tm.tm_mon = 10 * result_tm.tm_mon + value[i++] - '0'; >> + if (result_tm.tm_mon < 1 || 12 < result_tm.tm_mon) throw >> FieldConvertError(); >> + --result_tm.tm_mon; >> + >> + result_tm.tm_mday = value[i++] - '0'; >> + result_tm.tm_mday = 10 * result_tm.tm_mday + value[i++] - '0'; >> + if (result_tm.tm_mday < 1 || 31 < result_tm.tm_mday) throw >> FieldConvertError(); >> + >> + ++i; // skip '-' >> + >> + result_tm.tm_hour = value[i++] - '0'; >> + result_tm.tm_hour = 10 * result_tm.tm_hour + value[i++] - '0'; >> + if (23 < result_tm.tm_hour) throw FieldConvertError(); // No >> check for >= 0 as no '-' are converted here >> + >> + ++i; // skip ':' >> + >> + result_tm.tm_min = value[i++] - '0'; >> + result_tm.tm_min = 10 * result_tm.tm_min + value[i++] - '0'; >> + if (59 < result_tm.tm_min) throw FieldConvertError(); // No >> check for >= 0 as no '-' are converted here >> + >> + ++i; // skip ':' >> + >> + result_tm.tm_sec = value[i++] - '0'; >> + result_tm.tm_sec = 10 * result_tm.tm_sec + value[i++] - '0'; >> + if (60 < result_tm.tm_sec) throw FieldConvertError(); // No >> check for >= 0 as no '-' are converted here >> + >> + if (have_milliseconds) >> + result.setMillisecond (100 * (value[i+1] - '0') + >> + 10 * (value[i+2] - '0') + >> + (value[i+3] - '0')); >> + else >> + result.setMillisecond (0); >> + >> + result_tm.tm_isdst = -1; >> + >> return result; >> } >> }; >> @@ -266,13 +389,16 @@ >> throw( FieldConvertError& ) >> { >> char result[ 9+4 ]; >> - int len = strftime( result, 9, "%H:%M:%S", value ); >> - if ( len != 8 ) throw FieldConvertError(); >> + >> + integer_to_string_padded (result , 3, static_cast<const >> tm*>(value)->tm_hour, 2, '0'); result[2] = ':'; >> + integer_to_string_padded (result + 3, 3, static_cast<const >> tm*>(value)->tm_min, 2, '0'); result[5] = ':'; >> + integer_to_string_padded (result + 6, 3, static_cast<const >> tm*>(value)->tm_sec, 2, '0'); >> >> if(true==showMilliseconds) >> { >> - len = sprintf(result+8,".%03d",value.getMillisecond()); >> - if ( len != 4) throw FieldConvertError(); >> + result[8] = '.'; >> + if (integer_to_string_padded (result + 9, 4, >> value.getMillisecond(), 3, '0') != result + 9) >> + throw FieldConvertError(); >> } >> >> return result; >> @@ -282,19 +408,52 @@ >> throw( FieldConvertError& ) >> { >> UtcTimeOnly result; >> - const char* val = value.c_str(); >> - const char* len = strptime( val, "%H:%M:%S", result ); >> - if ( len - val != 8 ) throw FieldConvertError(); >> - >> - // if we have milliseconds in the string, *len should be ".sss" >> - result.setMillisecond(0); >> - if(NULL != len && strlen(len)==4 && len[0] == '.') >> - { >> - int ms = atoi(&len[1]); >> - if(ms < 0 || ms > 999) throw FieldConvertError(); >> - result.setMillisecond(ms); >> + bool have_milliseconds = false; >> + >> + switch (value.size()) { >> + case 12: have_milliseconds = true; >> + case 8: break; >> + default: throw FieldConvertError(); >> + } >> + >> + int i = 0; >> + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (value[i++] != ':') throw >> FieldConvertError(); >> + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (value[i++] != ':') throw >> FieldConvertError(); >> + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (have_milliseconds) { >> + // ++i instead of i++ skips the '.' separator >> + for (int c=0; c<3; ++c) if (!std::isdigit(value[++i])) throw >> FieldConvertError(); >> } >> >> + tm & result_tm = *static_cast<tm*>(result); >> + >> + i = 0; >> + >> + result_tm.tm_hour = value[i++] - '0'; >> + result_tm.tm_hour = 10 * result_tm.tm_hour + value[i++] - '0'; >> + if (23 < result_tm.tm_hour) throw FieldConvertError(); // No >> check for >= 0 as no '-' are converted here >> + ++i; // skip ':' >> + >> + result_tm.tm_min = value[i++] - '0'; >> + result_tm.tm_min = 10 * result_tm.tm_min + value[i++] - '0'; >> + if (59 < result_tm.tm_min) throw FieldConvertError(); // No >> check for >= 0 as no '-' are converted here >> + ++i; // skip ':' >> + >> + result_tm.tm_sec = value[i++] - '0'; >> + result_tm.tm_sec = 10 * result_tm.tm_sec + value[i++] - '0'; >> + if (60 < result_tm.tm_sec) throw FieldConvertError(); // No >> check for >= 0 as no '-' are converted here >> + >> + if (have_milliseconds) >> + result.setMillisecond (100 * (value[i+1] - '0') + >> + 10 * (value[i+2] - '0') + >> + (value[i+3] - '0')); >> + else >> + result.setMillisecond (0); >> + >> + result_tm.tm_isdst = -1; >> + >> return result; >> } >> }; >> @@ -306,8 +465,9 @@ >> throw( FieldConvertError& ) >> { >> char result[ 9 ]; >> - int len = strftime( result, 9, "%Y%m%d", value ); >> - if ( len != 8 ) throw FieldConvertError(); >> + integer_to_string_padded (result , 5, static_cast<const >> tm*>(value)->tm_year + 1900, 4, '0'); >> + integer_to_string_padded (result + 4, 3, static_cast<const >> tm*>(value)->tm_mon + 1, 2, '0'); >> + integer_to_string_padded (result + 6, 3, static_cast<const >> tm*>(value)->tm_mday, 2, '0'); >> return result; >> } >> >> @@ -315,9 +475,34 @@ >> throw( FieldConvertError& ) >> { >> UtcDate result; >> - const char* val = value.c_str(); >> - const char* len = strptime( val, "%Y%m%d", result ); >> - if ( len - val != 8 ) throw FieldConvertError(); >> + >> + if (value.size() != 8) throw FieldConvertError(); >> + >> + int i = 0; >> + for (int c=0; c<8; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + >> + tm & result_tm = *static_cast<tm*>(result); >> + >> + i = 0; >> + >> + result_tm.tm_year = value[i++] - '0'; >> + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; >> + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; >> + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; >> + result_tm.tm_year -= 1900; >> + >> + result_tm.tm_mon = value[i++] - '0'; >> + result_tm.tm_mon = 10 * result_tm.tm_mon + value[i++] - '0'; >> + if (result_tm.tm_mon < 1 || 12 < result_tm.tm_mon) throw >> FieldConvertError(); >> + --result_tm.tm_mon; >> + >> + result_tm.tm_mday = value[i++] - '0'; >> + result_tm.tm_mday = 10 * result_tm.tm_mday + value[i++] - '0'; >> + if (result_tm.tm_mday < 1 || 31 < result_tm.tm_mday) throw >> FieldConvertError(); >> + ++i; // skip '-' >> + >> + result_tm.tm_isdst = -1; >> + >> return result; >> } >> }; >> >> >> <font face="Times New Roman" size="3"> >> <p>------------------------------------------------------------------- >> -----------</p> >> <p> This email is intended only for the use of the individual(s) to >> whom it is addressed and may be privileged and confidential. >> Unauthorised use or disclosure is prohibited. If you receive this >> e-mail in error, please advise immediately and delete the original >> message. This message may have been altered without your or our >> knowledge and the sender does not accept any liability for any errors >> or omissions in the message.</p> >> <p>====================================================</p> >> </font> >> >> >> >> ------------------------------------------------------- >> SF.Net email is sponsored by Shop4tech.com-Lowest price on Blank Media >> 100pk Sonic DVD-R 4x for only $29 -100pk Sonic DVD+R for only $33 >> Save 50% off Retail on Ink & Toner - Free Shipping and Free Gift. >> http://www.shop4tech.com/z/Inkjet_Cartridges/9_108_r285 >> _______________________________________________ >> Quickfix-developers mailing list >> Qui...@li... >> https://lists.sourceforge.net/lists/listinfo/quickfix-developers >> > > > > ------------------------------------------------------- > SF.Net email is sponsored by Shop4tech.com-Lowest price on Blank Media > 100pk Sonic DVD-R 4x for only $29 -100pk Sonic DVD+R for only $33 > Save 50% off Retail on Ink & Toner - Free Shipping and Free Gift. > http://www.shop4tech.com/z/Inkjet_Cartridges/9_108_r285 > _______________________________________________ > Quickfix-developers mailing list > Qui...@li... > https://lists.sourceforge.net/lists/listinfo/quickfix-developers > |