[Quickfix-developers] FieldConvertor.h optimizations
Brought to you by:
orenmnero
From: Peterson, K. <kri...@rb...> - 2004-08-12 19:13:59
|
I have optimized some of the field convertors in FieldConvertors.h and have= achieved substantial improvements in performance (generally 50% - 200% fas= ter). 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 s= ignificant 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 k= ernel 2.6.7 compiled with g++ 3.3.4. 'normal' builds used the existing Fiel= dConvertors.h, 'modified' builds used the modified 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 -mcpu=3D= athlon-xp -Wall quickfix-optimized-normal/Makefile:CFLAGS =3D -g -O3 -ffast-math -mcpu=3Dat= hlon-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_seco= nd: 386100 times-debug-modified.txt : num: 100000, seconds: 0.032, num_per_seco= nd: 3.125e+06 times-optimized-normal.txt : num: 100000, seconds: 0.183, num_per_seco= nd: 546448 times-optimized-modified.txt: num: 100000, seconds: 0.026, num_per_seco= nd: 3.84615e+06 : Converting strings to integers: times-debug-normal.txt : num: 100000, seconds: 0.024, num_per_seco= nd: 4.16667e+06 times-debug-modified.txt : num: 100000, seconds: 0.008, num_per_seco= nd: 1.25e+07 times-optimized-normal.txt : num: 100000, seconds: 0.016, num_per_seco= nd: 6.25e+06 times-optimized-modified.txt: num: 100000, seconds: 0.004, num_per_seco= nd: 2.5e+07 : Converting doubles to strings: times-debug-normal.txt : num: 100000, seconds: 0.711, num_per_seco= nd: 140647 times-debug-modified.txt : num: 100000, seconds: 0.243, num_per_seco= nd: 411523 times-optimized-normal.txt : num: 100000, seconds: 0.531, num_per_seco= nd: 188324 times-optimized-modified.txt: num: 100000, seconds: 0.236, num_per_seco= nd: 423729 : Converting strings to doubles: times-debug-normal.txt : num: 100000, seconds: 0.987, num_per_seco= nd: 101317 times-debug-modified.txt : num: 100000, seconds: 0.058, num_per_seco= nd: 1.72414e+06 times-optimized-normal.txt : num: 100000, seconds: 0.725, num_per_seco= nd: 137931 times-optimized-modified.txt: num: 100000, seconds: 0.062, num_per_seco= nd: 1.6129e+06 : Creating Heartbeat messages: times-debug-normal.txt : num: 100000, seconds: 1.823, num_per_seco= nd: 54854.6 times-debug-modified.txt : num: 100000, seconds: 1.166, num_per_seco= nd: 85763.3 times-optimized-normal.txt : num: 100000, seconds: 1.242, num_per_seco= nd: 80515.3 times-optimized-modified.txt: num: 100000, seconds: 0.825, num_per_seco= nd: 121212 : Serializing Heartbeat messages to strings: times-debug-normal.txt : num: 100000, seconds: 2.35, num_per_secon= d: 42553.2 times-debug-modified.txt : num: 100000, seconds: 1.198, num_per_seco= nd: 83472.5 times-optimized-normal.txt : num: 100000, seconds: 1.425, num_per_seco= nd: 70175.4 times-optimized-modified.txt: num: 100000, seconds: 0.707, num_per_seco= nd: 141443 : Serializing Heartbeat messages from strings: times-debug-normal.txt : num: 100000, seconds: 4.749, num_per_seco= nd: 21057.1 times-debug-modified.txt : num: 100000, seconds: 2.671, num_per_seco= nd: 37439.2 times-optimized-normal.txt : num: 100000, seconds: 3.053, num_per_seco= nd: 32754.7 times-optimized-modified.txt: num: 100000, seconds: 1.782, num_per_seco= nd: 56116.7 : Creating NewOrderSingle messages: times-debug-normal.txt : num: 100000, seconds: 6.422, num_per_seco= nd: 15571.5 times-debug-modified.txt : num: 100000, seconds: 3.937, num_per_seco= nd: 25400.1 times-optimized-normal.txt : num: 100000, seconds: 4.778, num_per_seco= nd: 20929.3 times-optimized-modified.txt: num: 100000, seconds: 3.026, num_per_seco= nd: 33046.9 : Serializing NewOrderSingle messages to string= s: times-debug-normal.txt : num: 100000, seconds: 6.429, num_per_seco= nd: 15554.5 times-debug-modified.txt : num: 100000, seconds: 3.84, num_per_secon= d: 26041.7 times-optimized-normal.txt : num: 100000, seconds: 4.874, num_per_seco= nd: 20517 times-optimized-modified.txt: num: 100000, seconds: 3.029, num_per_seco= nd: 33014.2 : Serializing NewOrderSingle messages from stri= ngs: times-debug-normal.txt : num: 100000, seconds: 9.608, num_per_seco= nd: 10408 times-debug-modified.txt : num: 100000, seconds: 5.385, num_per_seco= nd: 18570.1 times-optimized-normal.txt : num: 100000, seconds: 7.202, num_per_seco= nd: 13885 times-optimized-modified.txt: num: 100000, seconds: 3.779, num_per_seco= nd: 26462 : Creating QuoteRequest messages: times-debug-normal.txt : num: 100000, seconds: 93.295, num_per_sec= ond: 1071.87 times-debug-modified.txt : num: 100000, seconds: 46.975, num_per_sec= ond: 2128.79 times-optimized-normal.txt : num: 100000, seconds: 61.148, num_per_sec= ond: 1635.38 times-optimized-modified.txt: num: 100000, seconds: 33.904, num_per_sec= ond: 2949.5 : Serializing QuoteRequest messages to strings: times-debug-normal.txt : num: 100000, seconds: 11.052, num_per_sec= ond: 9048.14 times-debug-modified.txt : num: 100000, seconds: 10.691, num_per_sec= ond: 9353.66 times-optimized-normal.txt : num: 100000, seconds: 5.667, num_per_seco= nd: 17646 times-optimized-modified.txt: num: 100000, seconds: 4.808, num_per_seco= nd: 20798.7 : Serializing QuoteRequest messages from string= s: times-debug-normal.txt : num: 100000, seconds: 74.91, num_per_seco= nd: 1334.94 times-debug-modified.txt : num: 100000, seconds: 48.556, num_per_sec= ond: 2059.48 times-optimized-normal.txt : num: 100000, seconds: 47.944, num_per_sec= ond: 2085.77 times-optimized-modified.txt: num: 100000, seconds: 28.627, num_per_sec= ond: 3493.21 : Reading fields from QuoteRequest message: times-debug-normal.txt : num: 100000, seconds: 34.526, num_per_sec= ond: 2896.37 times-debug-modified.txt : num: 100000, seconds: 12.988, num_per_sec= ond: 7699.41 times-optimized-normal.txt : num: 100000, seconds: 21.891, num_per_sec= ond: 4568.09 times-optimized-modified.txt: num: 100000, seconds: 6.875, num_per_seco= nd: 14545.5 : Storing NewOrderSingle messages: times-debug-normal.txt : num: 100000, seconds: 0.765, num_per_seco= nd: 130719 times-debug-modified.txt : num: 100000, seconds: 0.71, num_per_secon= d: 140845 times-optimized-normal.txt : num: 100000, seconds: 0.575, num_per_seco= nd: 173913 times-optimized-modified.txt: num: 100000, seconds: 0.521, num_per_seco= nd: 191939 : Validating NewOrderSingle messages with no da= ta dictionary: times-debug-normal.txt : num: 100000, seconds: 1.489, num_per_seco= nd: 67159.2 times-debug-modified.txt : num: 100000, seconds: 0.859, num_per_seco= nd: 116414 times-optimized-normal.txt : num: 100000, seconds: 0.843, num_per_seco= nd: 118624 times-optimized-modified.txt: num: 100000, seconds: 0.426, num_per_seco= nd: 234742 : Validating NewOrderSingle messages with data = dictionary: times-debug-normal.txt : num: 100000, seconds: 7.732, num_per_seco= nd: 12933.3 times-debug-modified.txt : num: 100000, seconds: 6.105, num_per_seco= nd: 16380 times-optimized-normal.txt : num: 100000, seconds: 2.485, num_per_seco= nd: 40241.4 times-optimized-modified.txt: num: 100000, seconds: 1.717, num_per_seco= nd: 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 =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, minus = and null + char buffer [std::numeric_limits<long>::digits10 + 3]; + const char * const start =3D 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 =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, '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 )); } 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 strin= gs + if (*i =3D=3D '-' && !*++i) return false; // Eat leading = '-' 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 ); bre= ak; } - 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 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] =3D '-'; + integer_to_string_padded (result + 9, 3, static_cast<const tm*>(value= )->tm_hour, 2, '0'); result[11] =3D ':'; + integer_to_string_padded (result + 12, 3, static_cast<const tm*>(value= )->tm_min, 2, '0'); result[14] =3D ':'; + integer_to_string_padded (result + 15, 3, static_cast<const 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, 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 FieldCo= nvertError(); + if (value[i++] !=3D '-') throw FieldConvertErr= or(); + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + if (value[i++] !=3D ':') throw FieldConvertErr= or(); + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + if (value[i++] !=3D ':') throw FieldConvertErr= or(); + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + if (have_milliseconds) { + if (value[i++] !=3D '.') throw FieldConvertError(); + for (int c=3D0; c<3; ++c) if (!std::isdigit(value[i++])) throw Field= ConvertError(); + } + + 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 FieldConvertE= rror(); + --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 FieldConver= tError(); + + ++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 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 tm*>(value)= ->tm_hour, 2, '0'); result[2] =3D ':'; + integer_to_string_padded (result + 3, 3, static_cast<const tm*>(value)= ->tm_min, 2, '0'); result[5] =3D ':'; + integer_to_string_padded (result + 6, 3, static_cast<const 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, 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 FieldCo= nvertError(); + if (value[i++] !=3D ':') throw FieldConvertErr= or(); + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + if (value[i++] !=3D ':') throw FieldConvertErr= or(); + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + if (have_milliseconds) { + // ++i instead of i++ skips the '.' separator + for (int c=3D0; c<3; ++c) if (!std::isdigit(value[++i])) throw Field= ConvertError(); } + 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 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 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 =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 FieldCo= nvertError(); + + 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 FieldConvertE= rror(); + --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 FieldConver= tError(); + ++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 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> |