Index: ace/OS_NS_math.inl =================================================================== --- ace/OS_NS_math.inl (revision 88738) +++ ace/OS_NS_math.inl (working copy) @@ -22,6 +22,22 @@ #endif } + ACE_INLINE long double + floor (long double x) + { +#if defined (ACE_LACKS_FLOORL) + // This method computes the largest integral value not greater than x. + if(x > 0) + return static_cast (x); + else if ((static_cast (x) < x) || (static_cast (x) > x) ) + return static_cast(x) - 1; + else + return x; +#else + return ::floorl (x); +#endif + } + ACE_INLINE double ceil (double x) { @@ -38,6 +54,22 @@ #endif } + ACE_INLINE long double + ceil (long double x) + { +#if defined (ACE_LACKS_CEILL) + // This method computes the smallest integral value not less than x. + if (x < 0) + return static_cast (x); + else if ((static_cast (x) < x) || (static_cast (x) > x)) + return static_cast (x) + 1; + else + return x; +#else + return ::ceill (x); +#endif + } + ACE_INLINE double log2 (double x) { Index: ace/OS_NS_math.h =================================================================== --- ace/OS_NS_math.h (revision 88738) +++ ace/OS_NS_math.h (working copy) @@ -91,9 +91,17 @@ ACE_NAMESPACE_INLINE_FUNCTION double floor (double x); + /// This method computes the largest integral value not greater than x. + ACE_NAMESPACE_INLINE_FUNCTION + long double floor (long double x); + /// This method computes the smallest integral value not less than x. ACE_NAMESPACE_INLINE_FUNCTION double ceil (double x); + + /// This method computes the smallest integral value not less than x. + ACE_NAMESPACE_INLINE_FUNCTION + long double ceil (long double x); /// This method computes the base-2 logarithm of x. ACE_NAMESPACE_INLINE_FUNCTION Index: ace/Time_Value.cpp =================================================================== --- ace/Time_Value.cpp (revision 88738) +++ ace/Time_Value.cpp (working copy) @@ -10,7 +10,13 @@ #include "ace/Numeric_Limits.h" #include "ace/If_Then_Else.h" +#include "ace/OS_NS_math.h" +#ifdef ACE_HAS_CPP98_IOSTREAMS +#include +#include +#endif + ACE_BEGIN_VERSIONED_NAMESPACE_DECL /// Static constant representing `zero-time'. @@ -162,7 +168,7 @@ } void -ACE_Time_Value::normalize (void) +ACE_Time_Value::normalize (bool bSaturate) { // // ACE_OS_TRACE ("ACE_Time_Value::normalize"); // From Hans Rohnert... @@ -171,23 +177,61 @@ { /*! \todo This loop needs some optimization. */ - do + if (!bSaturate) // keep the conditionnal expression outside the while loop to minimize performance cost + { + do { ++this->tv_.tv_sec; this->tv_.tv_usec -= ACE_ONE_SECOND_IN_USECS; } - while (this->tv_.tv_usec >= ACE_ONE_SECOND_IN_USECS); + while (this->tv_.tv_usec >= ACE_ONE_SECOND_IN_USECS); + } + else + { + do + { + if (this->tv_.tv_sec < ACE_Numeric_Limits::max()) + { + ++this->tv_.tv_sec; + this->tv_.tv_usec -= ACE_ONE_SECOND_IN_USECS; + } + else + { + this->tv_.tv_usec = ACE_ONE_SECOND_IN_USECS - 1; + } + } + while (this->tv_.tv_usec >= ACE_ONE_SECOND_IN_USECS); + } } else if (this->tv_.tv_usec <= -ACE_ONE_SECOND_IN_USECS) { /*! \todo This loop needs some optimization. */ - do + if (!bSaturate) + { + do { --this->tv_.tv_sec; this->tv_.tv_usec += ACE_ONE_SECOND_IN_USECS; } - while (this->tv_.tv_usec <= -ACE_ONE_SECOND_IN_USECS); + while (this->tv_.tv_usec <= -ACE_ONE_SECOND_IN_USECS); + } + else + { + do + { + if (this->tv_.tv_sec > ACE_Numeric_Limits::min()) + { + --this->tv_.tv_sec; + this->tv_.tv_usec += ACE_ONE_SECOND_IN_USECS; + } + else + { + this->tv_.tv_usec = -ACE_ONE_SECOND_IN_USECS + 1; + } + } + while (this->tv_.tv_usec <= -ACE_ONE_SECOND_IN_USECS); + } } if (this->tv_.tv_sec >= 1 && this->tv_.tv_usec < 0) @@ -209,51 +253,117 @@ ACE_Time_Value & ACE_Time_Value::operator *= (double d) { - // The floating type to be used in the computations. It should be - // large enough to hold a time_t. We actually want a floating type - // with enough digits in its mantissa to hold a time_t without - // losing precision. For example, if FLT_RADIX is 2 and - // LDBL_MANT_DIG is 64, a long double has a 64 bit wide mantissa, - // which would be sufficient to hold a 64 bit time_t value without - // losing precision. + // To work around the lack of precision of a long double to contain + // a 64-bits time_t + 6 digits after the decimal point for the usec part, + // we perform the multiplication of the 2 timeval parts separately. // - // For now we'll simply go with long double if it is larger than - // time_t. We're hosed if long double isn't large enough. + // This extra precision step is adding a cost when + // transfering the seconds resulting from the usec multiplication. + // This operation correspond to the normalization process performed in + // normalize() but we must absolutly do it here because the + // usec multiplication result value could exceed what can be stored + // in a suseconds_t type variable. + // + // Since this is a costly operation, we try to detect as soon as + // possible if we are having a saturation in order to abort the + // rest of the computation. typedef ACE::If_Then_Else<(sizeof (double) > sizeof (time_t)), double, long double>::result_type float_type; - float_type time_total = - (this->sec () - + static_cast (this->usec ()) / ACE_ONE_SECOND_IN_USECS) * d; - + float_type sec_total = this->sec(); + sec_total *= d; + // shall we saturate the result? static const float_type max_int = - ACE_Numeric_Limits::max () + 0.999999; + ACE_Numeric_Limits::max() + 0.999999; static const float_type min_int = - ACE_Numeric_Limits::min () - 0.999999; + ACE_Numeric_Limits::min() - 0.999999; + + if (sec_total > max_int) + { + this->set(ACE_Numeric_Limits::max(),ACE_ONE_SECOND_IN_USECS-1); + } + else if (sec_total < min_int) + { + this->set(ACE_Numeric_Limits::min(),-ACE_ONE_SECOND_IN_USECS+1); + } + else + { + time_t time_sec = static_cast (sec_total); + + float_type usec_total = this->usec(); + usec_total *= d; - if (time_total > max_int) - time_total = max_int; - if (time_total < min_int) - time_total = min_int; + // adding usec resulting from tv_sec mult + usec_total += (sec_total-time_sec)*ACE_ONE_SECOND_IN_USECS; + + // extract seconds component of the usec mult + sec_total = usec_total / ACE_ONE_SECOND_IN_USECS; + // keep remaining usec + if (sec_total > 0) + usec_total = (sec_total - ACE_OS::floor(sec_total)); + else + usec_total = (sec_total - ACE_OS::ceil(sec_total)); + + sec_total -= usec_total; + usec_total *= ACE_ONE_SECOND_IN_USECS; + + // add the seconds component of the usec mult with the tv_sec mult prod. + sec_total += time_sec; + + // recheck for saturation + if (sec_total > max_int) + { + this->set(ACE_Numeric_Limits::max(),ACE_ONE_SECOND_IN_USECS-1); + } + else if (sec_total < min_int) + { + this->set(ACE_Numeric_Limits::min(),-ACE_ONE_SECOND_IN_USECS+1); + } + else + { + time_sec = sec_total; + suseconds_t time_usec = static_cast (usec_total); - const time_t time_sec = static_cast (time_total); + // round up the result to save the last usec + if (time_usec > 0 && (usec_total - time_usec) >= 0.5) + ++time_usec; + else if (time_usec < 0 && (usec_total - time_usec) <= -0.5) + --time_usec; - time_total -= time_sec; - time_total *= ACE_ONE_SECOND_IN_USECS; + this->set(time_sec,time_usec); + } + } + return *this; +} - suseconds_t time_usec = static_cast (time_total); +#ifdef ACE_HAS_CPP98_IOSTREAMS +ostream &operator<<(ostream &o, const ACE_Time_Value &v) +{ + char oldFiller = o.fill(); + o.fill('0'); + const timeval *tv = v; + if( tv->tv_sec ) + { + o << tv->tv_sec; + if( tv->tv_usec ) + o << '.' << std::setw(6) << abs(tv->tv_usec); + } + else if( tv->tv_usec < 0 ) + { + o << "-0." << std::setw(6) << -tv->tv_usec; + } + else + { + o << '0'; + if( tv->tv_usec > 0 ) + o << '.'<< std::setw(6) << tv->tv_usec; + } - // round up the result to save the last usec - if (time_usec > 0 && (time_total - time_usec) >= 0.5) - ++time_usec; - else if (time_usec < 0 && (time_total - time_usec) <= -0.5) - --time_usec; - - this->set (time_sec, time_usec); - - return *this; + o.fill(oldFiller); + return o; } +#endif ACE_END_VERSIONED_NAMESPACE_DECL Index: ace/Time_Value.h =================================================================== --- ace/Time_Value.h (revision 88738) +++ ace/Time_Value.h (working copy) @@ -32,6 +32,9 @@ // needed for ACE_UINT64 #include "ace/Basic_Types.h" +// needed to determine if iostreams are present +#include "ace/iosfwd.h" + // This forward declaration is needed by the set() and FILETIME() functions #if defined (ACE_LACKS_LONGLONG_T) ACE_BEGIN_VERSIONED_NAMESPACE_DECL @@ -324,7 +327,7 @@ private: /// Put the timevalue into a canonical form. - void normalize (void); + void normalize (bool bSaturate = false); /// Store the values as a timeval. #if defined (ACE_HAS_TIME_T_LONG_MISMATCH) @@ -346,6 +349,10 @@ #endif /* ACE_HAS_TIME_T_LONG_MISMATCH */ }; +#ifdef ACE_HAS_CPP98_IOSTREAMS +extern ACE_Export ostream &operator<<( ostream &o, const ACE_Time_Value &v ); +#endif + ACE_END_VERSIONED_NAMESPACE_DECL #if defined (__ACE_INLINE__) Index: ace/iosfwd.h =================================================================== --- ace/iosfwd.h (revision 88738) +++ ace/iosfwd.h (working copy) @@ -44,6 +44,7 @@ # if !defined (ACE_USES_OLD_IOSTREAMS) # include /**/ +#define ACE_HAS_CPP98_IOSTREAMS 1 # else // @note If these forward declarations don't work (e.g. aren't // portable), we may have to include "ace/streams.h" as a last Index: tests/Time_Value_Test.cpp =================================================================== --- tests/Time_Value_Test.cpp (revision 88738) +++ tests/Time_Value_Test.cpp (working copy) @@ -30,6 +30,10 @@ #include "ace/Numeric_Limits.h" +#ifdef ACE_HAS_CPP98_IOSTREAMS +#include +#endif + int run_main (int, ACE_TCHAR *[]) { @@ -94,6 +98,32 @@ tv1 *= -10.0; ACE_ASSERT (tv1 == tv2); + const time_t max_time_t = ACE_Numeric_Limits::max (); + const time_t min_time_t = ACE_Numeric_Limits::min (); + + // test protection against overflows + // ACE_ASSERT( ACE_Time_Value(max_time_t,ACE_ONE_SECOND_IN_USECS) != ACE_Time_Value(ACE_Numeric_Limits::min()) ); + + // test saturated result + tv1.set (max_time_t - 1, 499999); + tv2.set (max_time_t, 999999); // ACE_Time_Value::max_time + tv1 *= 10.0; + ACE_ASSERT (tv1 == tv2); + tv1.set (max_time_t - 1, 499999); + tv2.set (min_time_t, -999999); + tv1 *= -10.0; + ACE_ASSERT (tv1 == tv2); + + // test results near limits + tv1.set ((max_time_t >> 1), 499999); + tv2.set ((-(max_time_t >> 1) << 1), -999998); + tv1 *= -2.0; + ACE_ASSERT (tv1 == tv2); + tv1.set (max_time_t >> 1, 499999); + tv2.set (((max_time_t >> 1) << 1), 999998); + tv1 *= 2.0; + ACE_ASSERT (tv1 == tv2); + // Test correct msec() convert; also checks for compile error reported in // Bugzilla 3336. ACE_Time_Value msec_test (42, 555000); @@ -111,6 +141,27 @@ ACE_TEXT ("msec const test failed: %Q should be 42555\n"), ms)); +#ifdef ACE_HAS_CPP98_IOSTREAMS + std::ostringstream ost; + ost << ACE_Time_Value(1); + ACE_ASSERT( ost.str() == "1" ); + ost.str(""); + ost << ACE_Time_Value(1,1); + ACE_ASSERT( ost.str() == "1.000001" ); + ost.str(""); + ost << ACE_Time_Value(-1,-1); + ACE_ASSERT( ost.str() == "-1.000001" ); + ost.str(""); + ost << ACE_Time_Value(0,1); + ACE_ASSERT( ost.str() == "0.000001" ); + ost.str(""); + ost << ACE_Time_Value(0,-1); + ACE_ASSERT( ost.str() == "-0.000001" ); + ost.str(""); + ost << ACE_Time_Value(); + ACE_ASSERT( ost.str() == "0" ); +#endif + ACE_END_TEST; return ret;