RE: [Quickfix-developers] FieldConvertor.h optimizations
Brought to you by:
orenmnero
From: Peterson, K. <kri...@rb...> - 2004-08-16 10:02:17
|
Oren - I've replaced atol() and printf() for the string->long and long->string con= versions with inlined, handwritten routines that are 4x-8x faster. For doub= le->string, I've removed the unnecessary memset() which is why speed up is = only 3x. With string->double conversions, prior code was effectively conver= ting string to double, then converting the double back to a string and then= comparing the strings, before using strtod() to convert the string to a do= uble. I replaced this with code that effectively checks the string against = the regex ^-?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)$ and then uses strtod() to do = the conversion, resulting in a 12x-18x speedup. I've tested these changes on Linux/AthlonXP and Solaris/SPARC and have seen= substantial performance improvements in both, and can not explain why you = aren't seeing the full effect. The only thing that comes to mind is that pe= rhaps 'pt' is being linked against a libquickfix.a build with the old Field= Convertors.h. As the methods are inlined, it is not enough to rebuild pt an= d libquickfixcpptest.a; libquickfix.a must be recompiled as well. - Kris -----Original Message----- From: Oren Miller [mailto:or...@qu...] Sent: 13 August 2004 16:52 To: Peterson, Kristofer Cc: QuickFIX Development (E-mail) Subject: Re: [Quickfix-developers] FieldConvertor.h optimizations Thanks Kristopher, I ran these through and was able to see nice performance improvements =20 on serializing to strings, however for some reasons the performance for =20 serializing from strings remained unchanged on the systems I tested =20 under. Also converting from doubles to strings showed no improvement. =20 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: =20 > http://www.quickfixengine.org/quickfix/doc/html/index.html > QuickFIX FAQ: =20 > 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 =20 > from quickfix-1.7.1. > > The code is admittedly 'C-ish', however no interface has been changed =20 > and I believe the results speak for themselves. > > Replacing strtod() in the double to string conversion would probably =20 > be a significant win, although I don't know of any efficient =20 > 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 =20 > Linux kernel 2.6.7 compiled with g++ 3.3.4. 'normal' builds used the =20 > existing FieldConvertors.h, 'modified' builds used the modified =20 > FieldConvertors.h. > > Optimization flags for the builds were as follows: > > quickfix-debug-modified/Makefile:CFLAGS =3D -g -Wall > quickfix-debug-normal/Makefile:CFLAGS =3D -g -Wall > quickfix-optimized-modified/Makefile:CFLAGS =3D -g -O3 -ffast-math =20 > -mcpu=3Dathlon-xp -Wall > quickfix-optimized-normal/Makefile:CFLAGS =3D -g -O3 -ffast-math =20 > -mcpu=3Dathlon-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, =20 > num_per_second: 386100 > times-debug-modified.txt : num: 100000, seconds: 0.032, =20 > num_per_second: 3.125e+06 > times-optimized-normal.txt : num: 100000, seconds: 0.183, =20 > num_per_second: 546448 > times-optimized-modified.txt: num: 100000, seconds: 0.026, =20 > num_per_second: 3.84615e+06 > : Converting strings to integers: > times-debug-normal.txt : num: 100000, seconds: 0.024, =20 > num_per_second: 4.16667e+06 > times-debug-modified.txt : num: 100000, seconds: 0.008, =20 > num_per_second: 1.25e+07 > times-optimized-normal.txt : num: 100000, seconds: 0.016, =20 > num_per_second: 6.25e+06 > times-optimized-modified.txt: num: 100000, seconds: 0.004, =20 > num_per_second: 2.5e+07 > : Converting doubles to strings: > times-debug-normal.txt : num: 100000, seconds: 0.711, =20 > num_per_second: 140647 > times-debug-modified.txt : num: 100000, seconds: 0.243, =20 > num_per_second: 411523 > times-optimized-normal.txt : num: 100000, seconds: 0.531, =20 > num_per_second: 188324 > times-optimized-modified.txt: num: 100000, seconds: 0.236, =20 > num_per_second: 423729 > : Converting strings to doubles: > times-debug-normal.txt : num: 100000, seconds: 0.987, =20 > num_per_second: 101317 > times-debug-modified.txt : num: 100000, seconds: 0.058, =20 > num_per_second: 1.72414e+06 > times-optimized-normal.txt : num: 100000, seconds: 0.725, =20 > num_per_second: 137931 > times-optimized-modified.txt: num: 100000, seconds: 0.062, =20 > num_per_second: 1.6129e+06 > : Creating Heartbeat messages: > times-debug-normal.txt : num: 100000, seconds: 1.823, =20 > num_per_second: 54854.6 > times-debug-modified.txt : num: 100000, seconds: 1.166, =20 > num_per_second: 85763.3 > times-optimized-normal.txt : num: 100000, seconds: 1.242, =20 > num_per_second: 80515.3 > times-optimized-modified.txt: num: 100000, seconds: 0.825, =20 > num_per_second: 121212 > : Serializing Heartbeat messages to =20 > strings: > times-debug-normal.txt : num: 100000, seconds: 2.35, =20 > num_per_second: 42553.2 > times-debug-modified.txt : num: 100000, seconds: 1.198, =20 > num_per_second: 83472.5 > times-optimized-normal.txt : num: 100000, seconds: 1.425, =20 > num_per_second: 70175.4 > times-optimized-modified.txt: num: 100000, seconds: 0.707, =20 > num_per_second: 141443 > : Serializing Heartbeat messages from =20 > strings: > times-debug-normal.txt : num: 100000, seconds: 4.749, =20 > num_per_second: 21057.1 > times-debug-modified.txt : num: 100000, seconds: 2.671, =20 > num_per_second: 37439.2 > times-optimized-normal.txt : num: 100000, seconds: 3.053, =20 > num_per_second: 32754.7 > times-optimized-modified.txt: num: 100000, seconds: 1.782, =20 > num_per_second: 56116.7 > : Creating NewOrderSingle messages: > times-debug-normal.txt : num: 100000, seconds: 6.422, =20 > num_per_second: 15571.5 > times-debug-modified.txt : num: 100000, seconds: 3.937, =20 > num_per_second: 25400.1 > times-optimized-normal.txt : num: 100000, seconds: 4.778, =20 > num_per_second: 20929.3 > times-optimized-modified.txt: num: 100000, seconds: 3.026, =20 > num_per_second: 33046.9 > : Serializing NewOrderSingle messages to =20 > strings: > times-debug-normal.txt : num: 100000, seconds: 6.429, =20 > num_per_second: 15554.5 > times-debug-modified.txt : num: 100000, seconds: 3.84, =20 > num_per_second: 26041.7 > times-optimized-normal.txt : num: 100000, seconds: 4.874, =20 > num_per_second: 20517 > times-optimized-modified.txt: num: 100000, seconds: 3.029, =20 > num_per_second: 33014.2 > : Serializing NewOrderSingle messages from = > strings: > times-debug-normal.txt : num: 100000, seconds: 9.608, =20 > num_per_second: 10408 > times-debug-modified.txt : num: 100000, seconds: 5.385, =20 > num_per_second: 18570.1 > times-optimized-normal.txt : num: 100000, seconds: 7.202, =20 > num_per_second: 13885 > times-optimized-modified.txt: num: 100000, seconds: 3.779, =20 > num_per_second: 26462 > : Creating QuoteRequest messages: > times-debug-normal.txt : num: 100000, seconds: 93.295, =20 > num_per_second: 1071.87 > times-debug-modified.txt : num: 100000, seconds: 46.975, =20 > num_per_second: 2128.79 > times-optimized-normal.txt : num: 100000, seconds: 61.148, =20 > num_per_second: 1635.38 > times-optimized-modified.txt: num: 100000, seconds: 33.904, =20 > num_per_second: 2949.5 > : Serializing QuoteRequest messages to =20 > strings: > times-debug-normal.txt : num: 100000, seconds: 11.052, =20 > num_per_second: 9048.14 > times-debug-modified.txt : num: 100000, seconds: 10.691, =20 > num_per_second: 9353.66 > times-optimized-normal.txt : num: 100000, seconds: 5.667, =20 > num_per_second: 17646 > times-optimized-modified.txt: num: 100000, seconds: 4.808, =20 > num_per_second: 20798.7 > : Serializing QuoteRequest messages from =20 > strings: > times-debug-normal.txt : num: 100000, seconds: 74.91, =20 > num_per_second: 1334.94 > times-debug-modified.txt : num: 100000, seconds: 48.556, =20 > num_per_second: 2059.48 > times-optimized-normal.txt : num: 100000, seconds: 47.944, =20 > num_per_second: 2085.77 > times-optimized-modified.txt: num: 100000, seconds: 28.627, =20 > num_per_second: 3493.21 > : Reading fields from QuoteRequest message: > times-debug-normal.txt : num: 100000, seconds: 34.526, =20 > num_per_second: 2896.37 > times-debug-modified.txt : num: 100000, seconds: 12.988, =20 > num_per_second: 7699.41 > times-optimized-normal.txt : num: 100000, seconds: 21.891, =20 > num_per_second: 4568.09 > times-optimized-modified.txt: num: 100000, seconds: 6.875, =20 > num_per_second: 14545.5 > : Storing NewOrderSingle messages: > times-debug-normal.txt : num: 100000, seconds: 0.765, =20 > num_per_second: 130719 > times-debug-modified.txt : num: 100000, seconds: 0.71, =20 > num_per_second: 140845 > times-optimized-normal.txt : num: 100000, seconds: 0.575, =20 > num_per_second: 173913 > times-optimized-modified.txt: num: 100000, seconds: 0.521, =20 > num_per_second: 191939 > : Validating NewOrderSingle messages with =20 > no data dictionary: > times-debug-normal.txt : num: 100000, seconds: 1.489, =20 > num_per_second: 67159.2 > times-debug-modified.txt : num: 100000, seconds: 0.859, =20 > num_per_second: 116414 > times-optimized-normal.txt : num: 100000, seconds: 0.843, =20 > num_per_second: 118624 > times-optimized-modified.txt: num: 100000, seconds: 0.426, =20 > num_per_second: 234742 > : Validating NewOrderSingle messages with =20 > data dictionary: > times-debug-normal.txt : num: 100000, seconds: 7.732, =20 > num_per_second: 12933.3 > times-debug-modified.txt : num: 100000, seconds: 6.105, =20 > num_per_second: 16380 > times-optimized-normal.txt : num: 100000, seconds: 2.485, =20 > num_per_second: 40241.4 > times-optimized-modified.txt: num: 100000, seconds: 1.717, =20 > num_per_second: 58241.1 > > > --- quickfix/src/C++/FieldConvertors.h 2004-04-29 20:58:20.000000000 =20 > +0100 > +++ quickfix-debug-modified/src/C++/FieldConvertors.h 2004-08-12 =20 > 19:13:22.036829060 +0100 > @@ -1,3 +1,4 @@ > + > /* -*- C++ -*- */ > > =20 > /=20 > ***********************************************************************= > ***** > @@ -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 =3D t < 0; > + char * p =3D buf + len; > + > + *--p =3D '\0'; > + > + if (is_neg) { > + if (t =3D=3D std::numeric_limits<T>::min()) { > + *--p =3D '0' + (10-t%10)%10; > + t/=3D10; > + } > + t=3D-t; > + do { > + *--p =3D '0' + t % 10; > + t /=3D 10; > + } while (t > 0); > + *--p =3D '-'; > + } else { > + do { > + *--p =3D '0' + t % 10; > + t /=3D 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 =3D 0, > + const char padding_char =3D '0') > +{ > + if (!width) return integer_to_string (buf, len, t); > + > + const bool is_neg =3D t < 0; > + char * p =3D buf + len; > + > + *--p =3D '\0'; > + > + if (is_neg) { > + if (t =3D=3D std::numeric_limits<T>::min()) { > + *--p =3D '0' + (10-t%10)%10; > + t/=3D10; > + } > + t=3D-t; > + do { > + *--p =3D '0' + t % 10; > + t /=3D 10; > + } while (t > 0); > + if (p > buf) > + *--p =3D '-'; > + } else { > + do { > + *--p =3D '0' + t % 10; > + t /=3D 10; > + } while (t > 0); > + } > + const char * stop_p =3D buf + len - width - 1; > + if (stop_p < buf) stop_p =3D buf; > + while (p > stop_p) > + *--p =3D 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, =20 > minus and null > + char buffer [std::numeric_limits<long>::digits10 + 3]; > + const char * const start =3D integer_to_string (buffer, sizeof =20 > (buffer), value); > + return std::string (start, buffer + sizeof (buffer) - start - 1); > } > > static bool convert( const std::string& value, long& result ) > { > const char * str =3D value.c_str(); > - if ( *str =3D=3D '-' ) ++str; > - for ( const char * p =3D str; *p !=3D 0; ++p ) > - { > - if ( !isdigit( *p ) ) return false; > + bool is_neg =3D false; > + long x =3D 0; > + > + if (*str =3D=3D '-') { > + is_neg =3D true; > + ++str; > } > - result =3D atol( value.c_str() ); > + > + do { > + const int c =3D *str - '0'; > + if (c < 0 || 9 < c) return false; > + x =3D 10 * x + c; > + } while (*++str); > + > + if (is_neg) x =3D -x; > + > + result =3D 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, =20 > '0') !=3D 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 =20 > )); > } > > static bool convert( const std::string& value, double& result ) > { > - if ( !value.size() ) return false; > - if ( value =3D=3D "." ) return false; > - > - result =3D atof( const_cast < char* > ( value.c_str() ) ); > - std::string stripped =3D value; > - > - // add leading zero if none exists > - if ( *stripped.begin() =3D=3D '.' ) > - stripped =3D '0' + stripped; > - > - // remove extra leading zeros > - while ( stripped.size() > 1 > - && *(stripped.begin()) =3D=3D '0' > - && isdigit(*(stripped.begin()+1)) ) > - { > - stripped.erase( stripped.begin() ); > + const char * i =3D value.c_str(); > + > + if (!*i) return false; // Catch null =20 > strings > + if (*i =3D=3D '-' && !*++i) return false; // Eat leadin= g =20 > '-' and recheck for null string > + > + bool have_digit =3D false; > + > + if (std::isdigit (*i)) { > + have_digit =3D true; > + while (std::isdigit (*++i)); > } > > - if ( stripped.find( '.' ) !=3D std::string::npos ) > - { > - std::string::reverse_iterator r =3D stripped.rbegin(); > - while ( r !=3D stripped.rend() && ( *r =3D=3D '0' || *r =3D=3D '.'= ) ) > - { > - if ( *r =3D=3D '.' ) { stripped.resize( stripped.size() - 1 ); = > break; } > - stripped.resize( stripped.size() - 1 ); r++; > - } > - } > - if ( *stripped.begin() =3D=3D '0' ) > - { > - std::string::size_type i =3D stripped.find_first_not_of( '0' ); > - std::string::size_type dot =3D stripped.find( '.' ); > - if ( i !=3D dot ) > - stripped =3D stripped.substr( i ); > + if (*i =3D=3D '.' && std::isdigit (*++i)) { > + have_digit =3D true; > + while (std::isdigit (*++i)); > } > > - std::string converted =3D convert( result ); > - if ( stripped.size() !=3D converted.size() ) return false; > + if (*i || !have_digit) return false; > > - // strcmp is being used because =3D=3D operator is funky under linux > - return ( strcmp( stripped.c_str(), converted.c_str() ) =3D=3D 0 ); > + result =3D 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 ] =3D value; > - temp[ 1 ] =3D '\0'; > - return temp; > + return std::string (1, value); > } > > static bool convert( const std::string& value, char& result ) > { > - if ( value.size() =3D=3D 0 ) return false; > - result =3D *value.c_str(); > + if ( value.size() !=3D 1 ) return false; > + result =3D value[0]; > return true; > } > > @@ -197,15 +256,19 @@ > struct BoolConvertor > { > static std::string convert( bool value ) > - { return value ? "Y" : "N"; } > + { > + const char ch =3D value ? 'Y' : 'N'; > + return std::string (1, ch); > + } > > static bool convert( const std::string& value, bool& result ) > { > - if ( value =3D=3D "Y" ) result =3D true; > - else > - if ( value =3D=3D "N" ) result =3D false; > - else > - return false; > + if (value.size() !=3D 1) return false; > + switch (value[0]) { > + case 'Y': result =3D true; break; > + case 'N': result =3D false; break; > + default: return false; > + } > return true; > } > > @@ -227,13 +290,19 @@ > throw( FieldConvertError& ) > { > char result[ 18+4 ]; > - int len =3D strftime( result, 18, "%Y%m%d-%H:%M:%S", value ); > - if ( len !=3D 17 ) throw FieldConvertError(); > + > + integer_to_string_padded (result , 5, static_cast<const =20 > tm*>(value)->tm_year + 1900, 4, '0'); > + integer_to_string_padded (result + 4, 3, static_cast<const =20 > tm*>(value)->tm_mon + 1, 2, '0'); > + integer_to_string_padded (result + 6, 3, static_cast<const =20 > tm*>(value)->tm_mday, 2, '0'); result[8] =3D '-'; > + integer_to_string_padded (result + 9, 3, static_cast<const =20 > tm*>(value)->tm_hour, 2, '0'); result[11] =3D ':'; > + integer_to_string_padded (result + 12, 3, static_cast<const =20 > tm*>(value)->tm_min, 2, '0'); result[14] =3D ':'; > + integer_to_string_padded (result + 15, 3, static_cast<const =20 > tm*>(value)->tm_sec, 2, '0'); > > if(true=3D=3DshowMilliseconds) > { > - len =3D sprintf(result+17,".%03d",value.getMillisecond()); > - if ( len !=3D 4) throw FieldConvertError(); > + result[17] =3D '.'; > + if (integer_to_string_padded (result + 18, 4, =20 > value.getMillisecond(), 3, '0') !=3D result + 18) > + throw FieldConvertError(); > } > return result; > } > @@ -242,19 +311,73 @@ > throw( FieldConvertError& ) > { > UtcTimeStamp result; > - const char* val =3D value.c_str(); > - const char* len =3D strptime( val, "%Y%m%d-%H:%M:%S", result ); > - if ( len - val !=3D 17 ) throw FieldConvertError(); > - > - // if we have milliseconds in the string, *len should be ".sss" > - result.setMillisecond(0); > - if(NULL !=3D len && strlen(len)=3D=3D4 && len[0] =3D=3D '.') > - { > - int ms =3D atoi(&len[1]); > - if(ms < 0 || ms > 999) throw FieldConvertError(); > - result.setMillisecond(ms); > + bool have_milliseconds =3D false; > + > + switch (value.size()) { > + case 21: have_milliseconds =3D true; > + case 17: break; > + default: throw FieldConvertError(); > } > > + int i =3D 0; > + for (int c=3D0; c<8; ++c) if (!std::isdigit(value[i++])) throw =20 > FieldConvertError(); > + if (value[i++] !=3D '-') throw =20 > FieldConvertError(); > + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw =20 > FieldConvertError(); > + if (value[i++] !=3D ':') throw =20 > FieldConvertError(); > + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw =20 > FieldConvertError(); > + if (value[i++] !=3D ':') throw =20 > FieldConvertError(); > + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw =20 > FieldConvertError(); > + if (have_milliseconds) { > + if (value[i++] !=3D '.') throw FieldConvertError(); > + for (int c=3D0; c<3; ++c) if (!std::isdigit(value[i++])) throw =20 > FieldConvertError(); > + } > + > + tm & result_tm =3D *static_cast<tm*>(result); > + > + i =3D 0; > + > + result_tm.tm_year =3D value[i++] - '0'; > + result_tm.tm_year =3D 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year =3D 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year =3D 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year -=3D 1900; > + > + result_tm.tm_mon =3D value[i++] - '0'; > + result_tm.tm_mon =3D 10 * result_tm.tm_mon + value[i++] - '0'; > + if (result_tm.tm_mon < 1 || 12 < result_tm.tm_mon) throw =20 > FieldConvertError(); > + --result_tm.tm_mon; > + > + result_tm.tm_mday =3D value[i++] - '0'; > + result_tm.tm_mday =3D 10 * result_tm.tm_mday + value[i++] - '0'; > + if (result_tm.tm_mday < 1 || 31 < result_tm.tm_mday) throw =20 > FieldConvertError(); > + > + ++i; // skip '-' > + > + result_tm.tm_hour =3D value[i++] - '0'; > + result_tm.tm_hour =3D 10 * result_tm.tm_hour + value[i++] - '0'; > + if (23 < result_tm.tm_hour) throw FieldConvertError(); // No =20 > check for >=3D 0 as no '-' are converted here > + > + ++i; // skip ':' > + > + result_tm.tm_min =3D value[i++] - '0'; > + result_tm.tm_min =3D 10 * result_tm.tm_min + value[i++] - '0'; > + if (59 < result_tm.tm_min) throw FieldConvertError(); // No check = > for >=3D 0 as no '-' are converted here > + > + ++i; // skip ':' > + > + result_tm.tm_sec =3D value[i++] - '0'; > + result_tm.tm_sec =3D 10 * result_tm.tm_sec + value[i++] - '0'; > + if (60 < result_tm.tm_sec) throw FieldConvertError(); // No check = > for >=3D 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 =3D -1; > + > return result; > } > }; > @@ -266,13 +389,16 @@ > throw( FieldConvertError& ) > { > char result[ 9+4 ]; > - int len =3D strftime( result, 9, "%H:%M:%S", value ); > - if ( len !=3D 8 ) throw FieldConvertError(); > + > + integer_to_string_padded (result , 3, static_cast<const =20 > tm*>(value)->tm_hour, 2, '0'); result[2] =3D ':'; > + integer_to_string_padded (result + 3, 3, static_cast<const =20 > tm*>(value)->tm_min, 2, '0'); result[5] =3D ':'; > + integer_to_string_padded (result + 6, 3, static_cast<const =20 > tm*>(value)->tm_sec, 2, '0'); > > if(true=3D=3DshowMilliseconds) > { > - len =3D sprintf(result+8,".%03d",value.getMillisecond()); > - if ( len !=3D 4) throw FieldConvertError(); > + result[8] =3D '.'; > + if (integer_to_string_padded (result + 9, 4, =20 > value.getMillisecond(), 3, '0') !=3D result + 9) > + throw FieldConvertError(); > } > > return result; > @@ -282,19 +408,52 @@ > throw( FieldConvertError& ) > { > UtcTimeOnly result; > - const char* val =3D value.c_str(); > - const char* len =3D strptime( val, "%H:%M:%S", result ); > - if ( len - val !=3D 8 ) throw FieldConvertError(); > - > - // if we have milliseconds in the string, *len should be ".sss" > - result.setMillisecond(0); > - if(NULL !=3D len && strlen(len)=3D=3D4 && len[0] =3D=3D '.') > - { > - int ms =3D atoi(&len[1]); > - if(ms < 0 || ms > 999) throw FieldConvertError(); > - result.setMillisecond(ms); > + bool have_milliseconds =3D false; > + > + switch (value.size()) { > + case 12: have_milliseconds =3D true; > + case 8: break; > + default: throw FieldConvertError(); > + } > + > + int i =3D 0; > + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw =20 > FieldConvertError(); > + if (value[i++] !=3D ':') throw =20 > FieldConvertError(); > + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw =20 > FieldConvertError(); > + if (value[i++] !=3D ':') throw =20 > FieldConvertError(); > + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw =20 > FieldConvertError(); > + if (have_milliseconds) { > + // ++i instead of i++ skips the '.' separator > + for (int c=3D0; c<3; ++c) if (!std::isdigit(value[++i])) throw =20 > FieldConvertError(); > } > > + tm & result_tm =3D *static_cast<tm*>(result); > + > + i =3D 0; > + > + result_tm.tm_hour =3D value[i++] - '0'; > + result_tm.tm_hour =3D 10 * result_tm.tm_hour + value[i++] - '0'; > + if (23 < result_tm.tm_hour) throw FieldConvertError(); // No =20 > check for >=3D 0 as no '-' are converted here > + ++i; // skip ':' > + > + result_tm.tm_min =3D value[i++] - '0'; > + result_tm.tm_min =3D 10 * result_tm.tm_min + value[i++] - '0'; > + if (59 < result_tm.tm_min) throw FieldConvertError(); // No check = > for >=3D 0 as no '-' are converted here > + ++i; // skip ':' > + > + result_tm.tm_sec =3D value[i++] - '0'; > + result_tm.tm_sec =3D 10 * result_tm.tm_sec + value[i++] - '0'; > + if (60 < result_tm.tm_sec) throw FieldConvertError(); // No check = > for >=3D 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 =3D -1; > + > return result; > } > }; > @@ -306,8 +465,9 @@ > throw( FieldConvertError& ) > { > char result[ 9 ]; > - int len =3D strftime( result, 9, "%Y%m%d", value ); > - if ( len !=3D 8 ) throw FieldConvertError(); > + integer_to_string_padded (result , 5, static_cast<const =20 > tm*>(value)->tm_year + 1900, 4, '0'); > + integer_to_string_padded (result + 4, 3, static_cast<const =20 > tm*>(value)->tm_mon + 1, 2, '0'); > + integer_to_string_padded (result + 6, 3, static_cast<const =20 > tm*>(value)->tm_mday, 2, '0'); > return result; > } > > @@ -315,9 +475,34 @@ > throw( FieldConvertError& ) > { > UtcDate result; > - const char* val =3D value.c_str(); > - const char* len =3D strptime( val, "%Y%m%d", result ); > - if ( len - val !=3D 8 ) throw FieldConvertError(); > + > + if (value.size() !=3D 8) throw FieldConvertError(); > + > + int i =3D 0; > + for (int c=3D0; c<8; ++c) if (!std::isdigit(value[i++])) throw =20 > FieldConvertError(); > + > + tm & result_tm =3D *static_cast<tm*>(result); > + > + i =3D 0; > + > + result_tm.tm_year =3D value[i++] - '0'; > + result_tm.tm_year =3D 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year =3D 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year =3D 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year -=3D 1900; > + > + result_tm.tm_mon =3D value[i++] - '0'; > + result_tm.tm_mon =3D 10 * result_tm.tm_mon + value[i++] - '0'; > + if (result_tm.tm_mon < 1 || 12 < result_tm.tm_mon) throw =20 > FieldConvertError(); > + --result_tm.tm_mon; > + > + result_tm.tm_mday =3D value[i++] - '0'; > + result_tm.tm_mday =3D 10 * result_tm.tm_mday + value[i++] - '0'; > + if (result_tm.tm_mday < 1 || 31 < result_tm.tm_mday) throw =20 > FieldConvertError(); > + ++i; // skip '-' > + > + result_tm.tm_isdst =3D -1; > + > return result; > } > }; > > > <font face=3D"Times New Roman" size=3D"3"> > <p>--------------------------------------------------------------------= > ----------</p> > <p> This email is intended only for the use of the individual(s) to =20 > whom it is addressed and may be privileged and confidential. =20 > Unauthorised use or disclosure is prohibited. If you receive this =20 > e-mail in error, please advise immediately and delete the original =20 > message. This message may have been altered without your or our =20 > knowledge and the sender does not accept any liability for any errors =20 > or omissions in the message.</p> > <p>=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D</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 > <font face=3D"Times New Roman" size=3D"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 d= isclosure is prohibited. If you receive this e-mail in error, please advise= immediately and delete the original message. This message may have been al= tered without your or our knowledge and the sender does not accept any liab= ility for any errors or omissions in the message.</p> <p>=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D</p> </font> |