Re: [Quickfix-developers] FieldConvertor.h optimizations
Brought to you by:
orenmnero
From: Oren M. <or...@qu...> - 2004-08-13 15:52:03
|
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 > |