]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
Refactoring of struct timeval string conversion, extended regression tests for struct...
authorJuergen Perlinger <perlinger@ntp.org>
Tue, 25 Jan 2011 20:08:29 +0000 (21:08 +0100)
committerJuergen Perlinger <perlinger@ntp.org>
Tue, 25 Jan 2011 20:08:29 +0000 (21:08 +0100)
bk: 4d3f2dbdh3s4SofA3nqDe_OecpljgQ

include/timespecops.h
include/timetoa.h [new file with mode: 0644]
include/timevalops.h
libntp/Makefile.am
libntp/timetoa.c [new file with mode: 0644]
libntp/timevalops.c
tests/libntp/Makefile.am
tests/libntp/timestructs.cpp [new file with mode: 0644]
tests/libntp/timestructs.h [new file with mode: 0644]
tests/libntp/tvalops.cpp

index 29a1cf26d2cf9195c63f192091ed8083bc017da8..6b1e2eeeacd46b4eb340690bdcda5b2b39dc6da4 100644 (file)
@@ -53,7 +53,7 @@
 
 /* predicate: returns TRUE if the nanoseconds are in nominal range */
 #define timespec_isnormal(x) \
-       ((u_long)(x)->tv_nsec < 1000000000)
+       ((x)->tv_nsec >= 0 && (x)->tv_nsec < 1000000000)
 
 /* predicate: returns TRUE if the nanoseconds are out-of-bounds */
 #define timespec_isdenormal(x) (!timespec_isnormal(x))
diff --git a/include/timetoa.h b/include/timetoa.h
new file mode 100644 (file)
index 0000000..edcd05b
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * timetoa.h -- time_t related string formatting
+ *
+ * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ *
+ * Printing a 'time_t' has some portability pitfalls, due to it's opaque
+ * base type. The only requirement imposed by the standard is that it
+ * must be a numeric type. For all practical purposes it's a signed int,
+ * and 32 bits are common.
+ *
+ * Since the UN*X time epoch will cause a signed integer overflow for
+ * 32-bit signed int values in the year 2038, implementations slowly
+ * move to 64bit base types for time_t, even in 32-bit environments. In
+ * such an environment sizeof(time_t) could be bigger than sizeof(long)
+ * and the commonly used idiom of casting to long leads to truncation.
+ *
+ * As the printf() family has no standardised type specifier for time_t,
+ * guessing the right output format specifier is a bit troublesome and
+ * best done with the help of the preprocessor and "config.h".
+ */
+#ifndef TIMETOA_H
+#define TIMETOA_H
+
+#include "config.h"
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+
+/*
+ * Given the size of time_t, guess what can be used as an unsigned value
+ * to hold a time_t and the printf() format specifcation.
+ *
+ * These should be used with the string constant concatenation feature
+ * of the compiler like this:
+ *
+ * printf("a time stamp: %" TIME_FORMAT " and more\n", a_time_t_value);
+ *
+ * It's not exactly nice, but there's not much leeway once we want to
+ * use the printf() family on time_t values.
+ */
+
+#if SIZEOF_TIME_T <= SIZEOF_INT
+
+typedef unsigned int u_time;
+#define TIME_FORMAT "d"
+#define UTIME_FORMAT "u"
+
+#elif SIZEOF_TIME_T <= SIZEOF_LONG
+
+typedef unsigned long u_time;
+#define TIME_FORMAT "ld"
+#define UTIME_FORMAT "lu"
+
+#elif defined(SIZEOF_LONG_LONG) && SIZEOF_TIME_T <= SIZEOF_LONG_LONG
+
+typedef unsigned long long u_time;
+#define TIME_FORMAT "lld"
+#define UTIME_FORMAT "llu"
+
+#else
+#include "GRONK: what size has a time_t here?"
+#endif
+
+/* general fractional time stamp formatting.
+ *
+ * secs - integral seconds of time stamp
+ * frac - fractional units
+ * prec - log10 of units per second (3=miliseconds, 6=microseconds,..)
+ *       or in other words: the number decimal digits required.
+ *       If prec is < 0, abs(prec) is taken and for the precision
+ *       and 'secs' is treated as an unsigned value.
+ *
+ * abs(prec) must be in [1 .. 9], or only the seconds are formatted.
+ *
+ * The function will eventually normalise the fraction and adjust the
+ * seconds accordingly.
+ *
+ * This function uses the string buffer library for the return value,
+ * so do not keep the resulting pointers around.
+ */
+extern const char *
+format_time_fraction(time_t secs, long frac, int prec);
+
+#endif /* !defined(TIMETOA_H) */
index 2aec5fb48fca5b5af08eb47effaa8a247eca4162..7b475a8d424f3d5511bc205897773d672722aecf 100644 (file)
@@ -27,7 +27,7 @@
  * use like: int timeval_isnormal(const struct timeval *x)
  */
 #define timeval_isnormal(x) \
-       ((u_long)(x)->tv_usec < 1000000)
+       ((x)->tv_usec >= 0 && (x)->tv_usec < 1000000)
 
 /*
  * predicate: returns TRUE if the microseconds are out-of-bounds
index 17b32266b0ea029b4825f2ee3f32df314b48d194..52177abb29b493b4185070ff394825de09ea647f 100644 (file)
@@ -65,6 +65,7 @@ libntp_a_SRCS =                                               \
        strdup.c                                        \
        syssignal.c                                     \
        timespecops.c                                   \
+       timetoa.c                                       \
        timevalops.c                                    \
        tsftomsu.c                                      \
        tstotv.c                                        \
diff --git a/libntp/timetoa.c b/libntp/timetoa.c
new file mode 100644 (file)
index 0000000..c8e01a6
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * timetoa.c -- time_t related string formatting
+ *
+ * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ *
+ * Printing a 'time_t' has a lot of portability pitfalls, due to it's
+ * opaque base type. The only requirement imposed by the standard is
+ * that it must be a numeric type. For all practical purposes it's a
+ * signed int, and 32 bits are common.
+ *
+ * Since the UN*X time epoch will cause a signed integer overflow for
+ * 32-bit signed int in the year 2038, implementations slowly move to
+ * 64bit base types for time_t, even in 32-bit environments.
+ *
+ * As the printf() family has no standardised type specifier for time_t,
+ * guessing the right output format specifier is a bit troublesome and
+ * best done with the help of the preprocessor and "config.h".
+ */
+
+#include "config.h"
+
+#include "timetoa.h"
+#include "lib_strbuf.h"
+#include <stdio.h>
+
+/*
+ * Formatting to string needs at max 40 bytes (even with 64 bit time_t),
+ * so we check LIB_BUFLENGTH is big enough for our pupose.
+ */
+#if LIB_BUFLENGTH < 40
+# include "GRONK: LIB_BUFLENGTH is not sufficient"
+#endif
+
+/*
+ * general fractional timestamp formatting
+ *
+ * Many pieces of ntpd require a machine with two's complement
+ * representation of signed integers, so we don't go through the whole
+ * rigamarole of creating fully portable code here. But we have to stay
+ * away from signed integer overflow, as this might cause trouble even
+ * with two's complement representation.
+ */
+const char *
+format_time_fraction(
+       time_t secs,
+       long   frac,
+       int    prec
+       )
+{
+       static const long   limit[10] = {
+               1,
+               10, 100, 1000,
+               10000, 100000, 1000000,
+               10000000, 100000000, 1000000000
+       };
+
+       char * cp;
+       u_time ttmp;    /* unsigned storage for seconds */
+       int    notneg;  /* flag for non-negative value  */
+       const char * fmt;
+
+       LIB_GETBUF(cp);
+       ttmp = (u_time)secs;
+       fmt = "-%" UTIME_FORMAT ".%0*ld";
+       
+       /* check if we need signed or unsigned mode */
+       notneg = (prec < 0);
+       prec   = abs(prec);
+       if (prec <= 0 || prec > 9) {
+               if (notneg)
+                       fmt = "%" UTIME_FORMAT;
+               else
+                       fmt = "%" TIME_FORMAT;
+               snprintf(cp, LIB_BUFLENGTH, fmt, secs);
+               return (cp);
+       }
+
+       /*
+        * Since conversion to string uses lots of divisions anyway,
+        * there's no big extra penalty for normalisation. We do it for
+        * consistency.
+        */
+       if (frac < 0 || frac >= limit[prec]) {
+               ldiv_t qr;
+               qr = ldiv(frac, limit[prec]);
+               if (qr.rem < 0) {
+                       qr.quot--;
+                       qr.rem += limit[prec];
+               }
+               ttmp += (time_t)qr.quot;
+               frac = qr.rem;
+       }
+
+       /*
+        * Get the absolute value of the time stamp.
+        */
+       notneg = notneg || ((time_t)ttmp >= 0);
+       if (notneg == 0) {
+               ttmp = ~ttmp;
+               if (frac != 0)
+                       frac = limit[prec] - frac;
+               else
+                         ttmp += 1;
+       } else
+               fmt++; /* skip sign char in format string */
+
+       /* finally format the data and return the result */
+       snprintf(cp, LIB_BUFLENGTH, fmt, ttmp, prec, frac);
+       
+       return (cp);
+}
index 623c2f9782be83332d44a0003f9e5b880bf82a51..63a842f70fe0130735b9b1c7397ac705158feb55 100644 (file)
@@ -8,17 +8,10 @@
 #include <config.h>
 #include <math.h>
 
-#include "lib_strbuf.h"
-#include "ntp_calendar.h"
-
 #include "timevalops.h"
 
-/* formatting to string needs at max 29 bytes (even with 64 bit time_t),
- * so we check LIB_BUFLENGTH is big enough for our pupose.
- */
-#if LIB_BUFLENGTH < 29
-#error LIB_BUFLENGTH not big enough
-#endif
+#include "timetoa.h"
+#include "ntp_calendar.h"
 
 /* make sure we have the right definition for MICROSECONDS */
 #undef MICROSECONDS
 # define MYTVUTOF(tvu, tsf)    TVUTOTSF(tvu, tsf)
 #endif
 
-/* using snprintf is troublesome with time_t. Try to resolve it. */
-#if SIZEOF_TIME_T <= SIZEOF_INT
-typedef unsigned int u_time;
-#define TIMEFMT ""
-#elif SIZEOF_TIME_T <= SIZEOF_LONG
-typedef unsigned long u_time;
-#define TIMEFMT "l"
-#elif defined(SIZEOF_LONG_LONG) && SIZEOF_TIME_T <= SIZEOF_LONG_LONG
-typedef unsigned long long u_time;
-#define TIMEFMT "ll"
-#else
-#include "GRONK: what size has a time_t here?"
-#endif
-
 /* copy and normalise. Used often enough to warrant a macro. */
 #define COPYNORM(dst, src)                             \
        do {                                            \
@@ -70,7 +49,7 @@ timeval_norm(
         * to do first partial normalisation. The normalisation loops
         * following will do the remaining cleanup. Since the size of
         * tv_usec has a peculiar definition by the standard the range
-        * check is coded manualla.
+        * check is coded manually.
         */
        if (x->tv_usec < -3l * MICROSECONDS ||
            x->tv_usec >  3l * MICROSECONDS  ) {
@@ -290,40 +269,7 @@ timeval_tostr(
        const struct timeval *x
        )
 {
-       /* see timespecops.c for rationale -- this needs refactoring
-        *
-        * Even with 64 bit time_t, 32 chars will suffice. Hopefully,
-        * LIB_BUFLENGTH is big enough; the current definiton checks
-        * this by the preprocessor just at the top of this file. */
-       static const char *fmt = "-%" TIMEFMT "u.%06lu";
-       
-       struct timeval v;
-       char *          cp;
-       int            notneg;
-       u_time         itmp;
-       u_long         ftmp;
-       
-       /* normalise and get absolute value into unsigned values. Since
-        * the negation of TIME_T_MIN (if it existed) is implementation
-        * defined, we try to avoid it. */
-       COPYNORM(&v, x);
-       notneg = v.tv_sec >= 0;
-       if (notneg != 0) {
-               itmp = (u_time)v.tv_sec;
-               ftmp = (u_long)v.tv_usec;
-       } else if (v.tv_usec != 0) {
-               itmp = (u_time)-(v.tv_sec + 1);
-               ftmp = (u_long)(MICROSECONDS - v.tv_usec);
-       } else {
-               itmp = ((u_time) -(v.tv_sec + 1)) + 1;
-               ftmp = 0;
-       }
-
-       /* get buffer and format data */
-       LIB_GETBUF(cp);
-       snprintf(cp, LIB_BUFLENGTH, fmt + notneg, itmp, ftmp);
-       
-       return cp;
+       return format_time_fraction(x->tv_sec, x->tv_usec, 6);
 }
 
 void
index 3439f57578d8758437aa4dd3dbd2cd16109594ef..e9efb2a99b39a1272d6b76d3af34c1fd2b1ddf49 100644 (file)
@@ -45,6 +45,7 @@ tests_SOURCES = $(top_srcdir)/sntp/tests_main.cpp     \
                ssl_init.cpp            \
                statestr.cpp            \
                strtolfp.cpp            \
+               timestructs.cpp         \
                tspecops.cpp            \
                tvalops.cpp             \
                tsftomsu.cpp            \
diff --git a/tests/libntp/timestructs.cpp b/tests/libntp/timestructs.cpp
new file mode 100644 (file)
index 0000000..28f0668
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * timestructs.cpp -- test bed adaptors for time structs.
+ *
+ * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ */
+#include "libntptest.h"
+#include "timestructs.h"
+
+extern "C" {
+#include "ntp_unixtime.h"
+#include "timetoa.h"
+#include "ntp_fp.h"
+
+#include "timevalops.h"
+}
+
+std::ostream&
+operator << (std::ostream& os, const timeStruct::l_fp_wrap& val)
+{
+       // raw data formatting
+       os << "0x" << std::hex << val.V.l_ui << ':'
+          << std::setfill('0') << std::setw(8) << val.V.l_uf
+          << std::dec;
+       // human-readable format
+       os << '[' << dolfptoa(val.V.l_ui, val.V.l_uf, 0, 10, 0) << ']';
+       return os;
+}
+
+std::ostream&
+operator << (std::ostream& os, const timeStruct::timeval_wrap& val)
+{
+       // raw data formatting
+       os << val.V.tv_sec << ':' << val.V.tv_usec;
+       // human-readable format
+       os << '['
+          << format_time_fraction(val.V.tv_sec, val.V.tv_usec, 6)
+          << ']';
+       return os;
+}
+
+std::ostream&
+operator << (std::ostream& os, const timeStruct::timespec_wrap& val)
+{
+       // raw data formatting
+       os << val.V.tv_sec << ':' << val.V.tv_nsec;
+       // human-readable format
+       os << '['
+          << format_time_fraction(val.V.tv_sec, val.V.tv_nsec, 9)
+          << ']';
+       return os;
+}
+
+namespace timeStruct {
+
+// Implementation of the l_fp closeness predicate
+
+AssertFpClose::AssertFpClose(
+       u_int32 hi,
+       u_int32 lo
+       )
+{
+       limit.l_ui = hi;
+       limit.l_uf = lo;
+}
+
+::testing::AssertionResult
+AssertFpClose::operator()(
+       const char* m_expr,
+       const char* n_expr,
+       const l_fp & m,
+       const l_fp & n
+       )
+{
+       l_fp diff;
+
+       if (L_ISGEQ(&m, &n)) {
+               diff = m;
+               L_SUB(&diff, &n);
+       } else {
+               diff = n;
+               L_SUB(&diff, &m);
+       }
+       if (L_ISGEQ(&limit, &diff))
+               return ::testing::AssertionSuccess();
+
+       return ::testing::AssertionFailure()
+           << m_expr << " which is " << l_fp_wrap(m)
+           << "\nand\n"
+           << n_expr << " which is " << l_fp_wrap(n)
+           << "\nare not close; diff=" << l_fp_wrap(diff);
+}
+
+// Implementation of the timeval closeness predicate
+
+AssertTimevalClose::AssertTimevalClose(
+       time_t hi,
+       int32  lo
+       )
+{
+       limit.tv_sec = hi;
+       limit.tv_usec = lo;
+}
+
+::testing::AssertionResult
+AssertTimevalClose::operator()(
+       const char* m_expr,
+       const char* n_expr,
+       const struct timeval & m,
+       const struct timeval & n
+       )
+{
+       struct timeval diff;
+
+       timeval_sub(&diff, &m, &n);
+       timeval_abs(&diff, &diff);
+       if (timeval_cmp(&limit, &diff) >= 0)
+               return ::testing::AssertionSuccess();
+
+       return ::testing::AssertionFailure()
+           << m_expr << " which is " << timeval_wrap(m)
+           << "\nand\n"
+           << n_expr << " which is " << timeval_wrap(n)
+           << "\nare not close; diff=" << timeval_wrap(diff);
+}
+
+} // namespace timeStruct
diff --git a/tests/libntp/timestructs.h b/tests/libntp/timestructs.h
new file mode 100644 (file)
index 0000000..14e2c44
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * timestructs.h -- test bed adaptors for time structs.
+ *
+ * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ *
+ * Some wrapper classes and a closeness predicate that are used to
+ * bridge the gap between the goggletest framework and the structs used
+ * for representing time stamps (l_fp, struct timeval, struct timespec).
+ *
+ * Some ostream conversion operators are provided to give diagnostic
+ * output on errors. The normal string conversion functions will give
+ * HRVs (human readable values) but we might also be interested in the
+ * machine representation for diagnostic purposes.
+ */
+#ifndef TIMESTRUCTS_H
+#define TIMESTRUCTS_H
+
+extern "C" {
+#include "ntp_types.h"
+#include "ntp_fp.h"    
+}
+
+namespace timeStruct {
+
+// wrap a l_fp struct with common operations
+class l_fp_wrap {
+  public:
+       l_fp V;
+       
+       l_fp_wrap()
+               { ZERO(V); }
+       l_fp_wrap(u_int32 hi, u_int32 lo)
+               { V.l_ui = hi; V.l_uf = lo; }
+       l_fp_wrap(const l_fp &rhs)
+               { V = rhs; }
+       bool operator == (const l_fp_wrap& rhs) const
+               { return L_ISEQU(&V, &rhs.V); }
+       operator l_fp* () 
+               { return &V; }
+       operator l_fp& ()
+               { return V; }
+       l_fp_wrap & operator = (const l_fp_wrap& rhs)
+               { V = rhs.V; return *this; }
+       l_fp_wrap& operator = (const l_fp& rhs)
+               { V = rhs; return *this; }
+       };
+       
+// wrap a 'struct timeval' with common operations
+class timeval_wrap {
+public:
+       struct timeval V;
+
+       timeval_wrap()
+               { ZERO(V); }
+       timeval_wrap(time_t hi, long lo)
+               { V.tv_sec = hi; V.tv_usec = lo; }
+       timeval_wrap(const struct timeval & rhs)
+               { V = rhs; }
+       bool operator == (const timeval_wrap& rhs) const
+               { return V.tv_sec == rhs.V.tv_sec &&
+                        V.tv_usec == rhs.V.tv_usec ; }
+       bool valid() const
+               { return V.tv_usec >= 0 && V.tv_usec < 1000000; }
+       operator struct timeval* () 
+               { return &V; }
+       operator struct timeval& ()
+               { return V; }
+       timeval_wrap& operator = (const timeval_wrap& rhs)
+               { V = rhs.V; return *this; }
+       timeval_wrap& operator = (const struct timeval& rhs)
+               { V = rhs; return *this; }
+};
+
+// wrap a 'struct timespec' with common operations
+class timespec_wrap {
+public:
+       struct timespec V;
+
+       timespec_wrap()
+               { ZERO(V); }
+       timespec_wrap(time_t hi, long lo)
+               { V.tv_sec = hi; V.tv_nsec = lo; }
+       timespec_wrap(const struct timespec & rhs)
+               { V = rhs; }
+       bool operator == (const timespec_wrap& rhs) const
+               { return V.tv_sec == rhs.V.tv_sec &&
+                        V.tv_nsec == rhs.V.tv_nsec ; }
+       bool valid() const
+               { return V.tv_nsec >= 0 && V.tv_nsec < 1000000000; }
+       operator struct timespec* () 
+               { return &V; }
+       operator struct timespec& ()
+               { return V;     }
+       timespec_wrap& operator = (const timespec_wrap& rhs)
+               { V = rhs.V; return *this; }
+       timespec_wrap& operator = (const struct timespec& rhs)
+               { V = rhs; return *this; }
+};
+
+// l_fp closeness testing predicate
+//
+// This predicate is used for the closeness ('near') testing of l_fp
+// values. Once constructed with a limit, it can be used to check the
+// absolute difference of two l_fp structs against that limit; if the
+// difference is less or equal to this limit, the test passes.
+class AssertFpClose {
+private:
+       l_fp limit;
+
+public:
+       AssertFpClose(u_int32 hi, u_int32 lo);
+
+       ::testing::AssertionResult
+       operator()(const char* m_expr, const char* n_expr,
+                  const l_fp & m, const l_fp & n);
+};
+
+
+// timeval closeness testing predicate
+//
+// CAVEAT: This class uses the timevalops functions
+// - timeval_sub
+// - timeval_abs
+// - timeval_cmp
+//
+// This creates a dependency loop of sorts. The loop is defused by the
+// fact that these basic operations can be tested by exact value tests,
+// so once the basic timeval operations passed it's safe to use this
+// predicate.
+class AssertTimevalClose {
+private:
+       struct timeval limit;
+
+public:
+       // note: (hi,lo) should be a positive normalised timeval;
+       // the constructor does not normalise the values!
+       AssertTimevalClose(time_t hi, int32 lo);
+
+       ::testing::AssertionResult
+       operator()(const char* m_expr, const char* n_expr,
+                  const struct timeval & m, const struct timeval & n);
+};
+
+
+} // namespace timeStruct
+
+// since googletest wants to string format items, we declare the
+// necessary operators. Since all adaptors have only public members
+// there is need for friend declarations anywhere.
+
+extern std::ostream& operator << (std::ostream& os,
+                                 const timeStruct::l_fp_wrap& val);
+extern std::ostream& operator << (std::ostream& os,
+                                 const timeStruct::timeval_wrap& val);
+extern std::ostream& operator << (std::ostream& os,
+                                 const timeStruct::timespec_wrap& val);
+
+#endif // TIMESTRUCTS_H
index 2a8950b0181ea79cef16d9adf42febf47b363466..5fb5f7a77aa055870dcdb8f1a4c0bdbc14c9e051 100644 (file)
@@ -1,4 +1,5 @@
 #include "libntptest.h"
+#include "timestructs.h"
 
 extern "C" {
 #include <math.h>
@@ -8,8 +9,14 @@ extern "C" {
 #include <string>
 #include <sstream>
 
+using namespace timeStruct;
+
 class timevalTest : public libntptest {
 protected:
+
+       static u_int32 my_tick_to_tsf(u_int32 ticks);
+       static u_int32 my_tsf_to_tick(u_int32 tsf);
+
        static const long MICROSECONDS;
        // that's it...
        struct lfpfracdata {
@@ -19,6 +26,39 @@ protected:
        static const lfpfracdata fdata[];
 };
 
+u_int32
+timevalTest::my_tick_to_tsf(
+       u_int32 ticks
+       )
+{
+       // convert microseconds to l_fp fractional units, using double
+       // precision float calculations or, if available, 64bit integer
+       // arithmetic. This should give the precise fraction, rounded to
+       // the nearest representation.
+#if SIZEOF_LONG >= 8
+       return (u_int32)((((u_long)ticks << 32) + 500000) / 1000000);
+#else
+       return (u_int32)floor((double)ticks * 4294.967296 + 0.5);
+#endif
+       // And before you ask: if ticks >= 1000000, the result is
+       // truncated nonsense, so don't use it out-of-bounds.
+}
+
+u_int32
+timevalTest::my_tsf_to_tick(
+       u_int32 tsf
+       )
+{
+       // Inverse operation: converts fraction to microseconds.
+#if SIZEOF_LONG >= 8
+       return (u_int32)(((u_long)tsf * 1000000 + 0x80000000) >> 32);
+#else
+       return (u_int32)floor((double)ticks / 4294.967296 + 0.5);
+#endif
+       // Beware: The result might be 10^6 due to rounding!
+}
+
+
 const long timevalTest::MICROSECONDS = 1000000;
 const timevalTest::lfpfracdata timevalTest::fdata [] = {
        {      0, 0x00000000 }, {   7478, 0x01ea1405 },
@@ -38,63 +78,11 @@ const timevalTest::lfpfracdata timevalTest::fdata [] = {
 };
 
 
-class TVAL {
-public:
-       struct timeval V;
-
-       TVAL()
-               { ZERO(V); }
-       TVAL(time_t hi, long lo)
-               { V.tv_sec = hi; V.tv_usec = lo; }
-       bool operator == (const TVAL& rhs) const
-               { return timeval_cmp(&V, &rhs.V) == 0; }
-       bool valid() const
-               { return timeval_isnormal(&V); }
-       operator struct timeval* () 
-               { return &V; }
-       operator struct timeval& ()
-               { return V; }
-       TVAL& operator = (const TVAL& rhs)
-               { V = rhs.V; return *this; }
-       TVAL& operator = (const struct timeval& rhs)
-               { V = rhs; return *this; }
-};
-
-std::ostream&
-operator << (std::ostream& os, const TVAL& val)
-{
-       os << timeval_tostr(&val.V);
-       return os;
-}
+// and the global predicate instances we're using here
 
+static AssertFpClose FpClose(0, 1);
 
-class LFP {
-public:
-       l_fp V;
-
-       LFP()
-               { ZERO(V); }
-       LFP(u_int32 hi, u_int32 lo)
-               { V.l_ui = hi; V.l_uf = lo; }
-       bool operator == (const LFP& rhs) const
-               { return L_ISEQU(&V, &rhs.V); }
-       operator l_fp* () 
-               { return &V; }
-       operator l_fp& ()
-               { return V; }
-       LFP& operator = (const LFP& rhs)
-               { V = rhs.V; return *this; }
-       LFP& operator = (const l_fp& rhs)
-               { V = rhs; return *this; }
-};
-
-static std::ostream&
-operator << (std::ostream& os, const LFP& val)
-{
-       os << ulfptoa(&val.V, 10);
-       return os;
-}
-
+static AssertTimevalClose TimevalClose(0, 1);
 
 // ---------------------------------------------------------------------
 // test support stuff
@@ -102,7 +90,7 @@ operator << (std::ostream& os, const LFP& val)
 
 TEST_F(timevalTest, Normalise) {
        for (long ns = -2000000000; ns <= 2000000000; ns += 10000000) {
-               TVAL    x(0, ns);
+               timeval_wrap x(0, ns);
 
                timeval_norm(x);
                ASSERT_TRUE(x.valid());
@@ -112,9 +100,9 @@ TEST_F(timevalTest, Normalise) {
 TEST_F(timevalTest, SignNoFrac) {
        // sign test, no fraction
        for (int i = -4; i <= 4; ++i) {
-               TVAL    a(i, 0);
-               int     E = (i > 0) - (i < 0);
-               int     r = timeval_test(a);
+               timeval_wrap a(i, 0);
+               int          E = (i > 0) - (i < 0);
+               int          r = timeval_test(a);
 
                ASSERT_EQ(E, r);
        }
@@ -123,9 +111,9 @@ TEST_F(timevalTest, SignNoFrac) {
 TEST_F(timevalTest, SignWithFrac) {
        // sign test, with fraction
        for (int i = -4; i <= 4; ++i) {
-               TVAL    a(i, 10);
-               int     E = (i >= 0) - (i < 0);
-               int     r = timeval_test(a);
+               timeval_wrap a(i, 10);
+               int          E = (i >= 0) - (i < 0);
+               int          r = timeval_test(a);
 
                ASSERT_EQ(E, r);
        }
@@ -136,10 +124,10 @@ TEST_F(timevalTest, CmpFracEQ) {
        // fractions are equal
        for (int i = -4; i <= 4; ++i)
                for (int j = -4; j <= 4; ++j) {
-                       TVAL    a(i, 200);
-                       TVAL    b(j, 200);
-                       int     E = (i > j) - (i < j);
-                       int     r = timeval_cmp(a, b);
+                       timeval_wrap a(i, 200);
+                       timeval_wrap b(j, 200);
+                       int          E = (i > j) - (i < j);
+                       int          r = timeval_cmp(a, b);
 
                        ASSERT_EQ(E, r);
                }
@@ -149,10 +137,10 @@ TEST_F(timevalTest, CmpFracGT) {
        // fraction a bigger fraction b
        for (int i = -4; i <= 4; ++i)
                for (int j = -4; j <= 4; ++j) {
-                       TVAL    a( i , 999800);
-                       TVAL    b( j , 200);
-                       int     E = (i >= j) - (i < j);
-                       int     r = timeval_cmp(a, b);
+                       timeval_wrap a( i , 999800);
+                       timeval_wrap b( j , 200);
+                       int          E = (i >= j) - (i < j);
+                       int          r = timeval_cmp(a, b);
 
                        ASSERT_EQ(E, r);
                }
@@ -162,10 +150,10 @@ TEST_F(timevalTest, CmpFracLT) {
        // fraction a less fraction b
        for (int i = -4; i <= 4; ++i)
                for (int j = -4; j <= 4; ++j) {
-                       TVAL    a(i, 200);
-                       TVAL    b(j, 999800);
-                       int     E = (i > j) - (i <= j);
-                       int     r = timeval_cmp(a, b);
+                       timeval_wrap a(i, 200);
+                       timeval_wrap b(j, 999800);
+                       int          E = (i > j) - (i <= j);
+                       int          r = timeval_cmp(a, b);
 
                        ASSERT_EQ(E, r);
                }
@@ -175,10 +163,10 @@ TEST_F(timevalTest, CmpFracLT) {
 TEST_F(timevalTest, AddFullNorm) {
        for (int i = -4; i <= 4; ++i)
                for (int j = -4; j <= 4; ++j) {
-                       TVAL a(i, 200);
-                       TVAL b(j, 400);
-                       TVAL E(i + j, 200 + 400);
-                       TVAL c;
+                       timeval_wrap a(i, 200);
+                       timeval_wrap b(j, 400);
+                       timeval_wrap E(i + j, 200 + 400);
+                       timeval_wrap c;
 
                        timeval_add(c, a, b);
                        ASSERT_EQ(E, c);
@@ -188,10 +176,10 @@ TEST_F(timevalTest, AddFullNorm) {
 TEST_F(timevalTest, AddFullOflow1) {
        for (int i = -4; i <= 4; ++i)
                for (int j = -4; j <= 4; ++j) {
-                       TVAL a(i, 200);
-                       TVAL b(j, 999900);
-                       TVAL E(i + j + 1, 100);
-                       TVAL c;
+                       timeval_wrap a(i, 200);
+                       timeval_wrap b(j, 999900);
+                       timeval_wrap E(i + j + 1, 100);
+                       timeval_wrap c;
                        timeval_add(c, a, b);
                        ASSERT_EQ(E, c);
                }
@@ -199,9 +187,9 @@ TEST_F(timevalTest, AddFullOflow1) {
 
 TEST_F(timevalTest, AddUsecNorm) {
        for (int i = -4; i <= 4; ++i) {
-               TVAL a(i, 200);
-               TVAL E(i, 600);
-               TVAL c;
+               timeval_wrap a(i, 200);
+               timeval_wrap E(i, 600);
+               timeval_wrap c;
 
                timeval_addus(c, a, 400);
                ASSERT_EQ(E, c);
@@ -210,9 +198,9 @@ TEST_F(timevalTest, AddUsecNorm) {
 
 TEST_F(timevalTest, AddUsecOflow1) {
        for (int i = -4; i <= 4; ++i) {
-               TVAL a(i, 200);
-               TVAL E(i + 1, 100);
-               TVAL c;
+               timeval_wrap a(i, 200);
+               timeval_wrap E(i + 1, 100);
+               timeval_wrap c;
 
                timeval_addus(c, a, MICROSECONDS - 100);
                ASSERT_EQ(E, c);
@@ -223,10 +211,10 @@ TEST_F(timevalTest, AddUsecOflow1) {
 TEST_F(timevalTest, SubFullNorm) {
        for (int i = -4; i <= 4; ++i)
                for (int j = -4; j <= 4; ++j) {
-                       TVAL a(i, 600);
-                       TVAL b(j, 400);
-                       TVAL E(i - j, 600 - 400);
-                       TVAL c;
+                       timeval_wrap a(i, 600);
+                       timeval_wrap b(j, 400);
+                       timeval_wrap E(i - j, 600 - 400);
+                       timeval_wrap c;
 
                        timeval_sub(c, a, b);
                        ASSERT_EQ(E, c);
@@ -236,10 +224,10 @@ TEST_F(timevalTest, SubFullNorm) {
 TEST_F(timevalTest, SubFullOflow) {
        for (int i = -4; i <= 4; ++i)
                for (int j = -4; j <= 4; ++j) {
-                       TVAL a(i, 100);
-                       TVAL b(j, 999900);
-                       TVAL E(i - j - 1, 200);
-                       TVAL c;
+                       timeval_wrap a(i, 100);
+                       timeval_wrap b(j, 999900);
+                       timeval_wrap E(i - j - 1, 200);
+                       timeval_wrap c;
 
                        timeval_sub(c, a, b);
                        ASSERT_EQ(E, c);
@@ -248,9 +236,9 @@ TEST_F(timevalTest, SubFullOflow) {
 
 TEST_F(timevalTest, SubUsecNorm) {
        for (int i = -4; i <= 4; ++i) {
-               TVAL a(i, 600);
-               TVAL E(i, 200);
-               TVAL c;
+               timeval_wrap a(i, 600);
+               timeval_wrap E(i, 200);
+               timeval_wrap c;
 
                timeval_subus(c, a, 600 - 200);
                ASSERT_EQ(E, c);
@@ -259,9 +247,9 @@ TEST_F(timevalTest, SubUsecNorm) {
 
 TEST_F(timevalTest, SubUsecOflow) {
        for (int i = -4; i <= 4; ++i) {
-               TVAL a(i, 100);
-               TVAL E(i - 1, 200);
-               TVAL c;
+               timeval_wrap a(i, 100);
+               timeval_wrap E(i - 1, 200);
+               timeval_wrap c;
 
                timeval_subus(c, a, MICROSECONDS - 100);
                ASSERT_EQ(E, c);
@@ -271,9 +259,10 @@ TEST_F(timevalTest, SubUsecOflow) {
 // test negation
 TEST_F(timevalTest, Neg) {
        for (int i = -4; i <= 4; ++i) {
-               TVAL a(i, 100);
-               TVAL b;
-               TVAL c;
+               timeval_wrap a(i, 100);
+               timeval_wrap b;
+               timeval_wrap c;
+
                timeval_neg(b, a);
                timeval_add(c, a, b);
                ASSERT_EQ(0, timeval_test(c));
@@ -283,9 +272,9 @@ TEST_F(timevalTest, Neg) {
 // test abs value
 TEST_F(timevalTest, AbsNoFrac) {
        for (int i = -4; i <= 4; ++i) {
-               TVAL    a(i, 0);
-               TVAL    b;
-               int     c;
+               timeval_wrap a(i, 0);
+               timeval_wrap b;
+               int          c;
 
                c = timeval_abs(b, a);
                ASSERT_EQ((i < 0), c);
@@ -295,9 +284,9 @@ TEST_F(timevalTest, AbsNoFrac) {
 
 TEST_F(timevalTest, AbsWithFrac) {
        for (int i = -4; i <= 4; ++i) {
-               TVAL    a(i, 100);
-               TVAL    b;
-               int     c;
+               timeval_wrap a(i, 100);
+               timeval_wrap b;
+               int          c;
 
                c = timeval_abs(b, a);
                ASSERT_EQ((i < 0), c);
@@ -306,56 +295,88 @@ TEST_F(timevalTest, AbsWithFrac) {
 }
 
 // conversion to l_fp
+TEST_F(timevalTest, ToLFPbittest) {
+       for (u_int32 i = 0; i < 1000000; i++) {
+               timeval_wrap a(1, i);
+               l_fp_wrap    E(1, my_tick_to_tsf(i));
+               l_fp_wrap    r;
+
+               timeval_reltolfp(r, a);
+               ASSERT_PRED_FORMAT2(FpClose, E, r);
+       }
+}
+
 TEST_F(timevalTest, ToLFPrelPos) {
        for (int i = 0; i < COUNTOF(fdata); i++) {
-               TVAL    a(1, fdata[i].usec);
-               LFP     E(1, fdata[i].frac);
-               LFP     r;
-               double  Ef;
-               double  rf;
+               timeval_wrap a(1, fdata[i].usec);
+               l_fp_wrap    E(1, fdata[i].frac);
+               l_fp_wrap    r;
 
                timeval_reltolfp(r, a);
-               LFPTOD(&E.V, Ef);
-               LFPTOD(&r.V, rf);
-               ASSERT_NEAR(Ef, rf, 1. / MICROSECONDS);
+               ASSERT_PRED_FORMAT2(FpClose, E, r);
        }
 }
 
 TEST_F(timevalTest, ToLFPrelNeg) {
        for (int i = 0; i < COUNTOF(fdata); i++) {
-               TVAL    a(-1, fdata[i].usec);
-               LFP     E(~0, fdata[i].frac);
-               LFP     r;
-               double  Ef;
-               double  rf;
+               timeval_wrap a(-1, fdata[i].usec);
+               l_fp_wrap    E(~0, fdata[i].frac);
+               l_fp_wrap    r;
 
                timeval_reltolfp(r, a);
-               LFPTOD(&E.V, Ef);
-               LFPTOD(&r.V, rf);
-               ASSERT_NEAR(Ef, rf, 1. / MICROSECONDS);
+               ASSERT_PRED_FORMAT2(FpClose, E, r);
        }
 }
 
 TEST_F(timevalTest, ToLFPabs) {
        for (int i = 0; i < COUNTOF(fdata); i++) {
-               TVAL    a(1, fdata[i].usec);
-               LFP     E(1 + JAN_1970, fdata[i].frac);
-               LFP     r;
-               double  Ef;
-               double  rf;
+               timeval_wrap a(1, fdata[i].usec);
+               l_fp_wrap    E(1 + JAN_1970, fdata[i].frac);
+               l_fp_wrap    r;
 
                timeval_abstolfp(r, a);
-               LFPTOD(&E.V, Ef);
-               LFPTOD(&r.V, rf);
-               ASSERT_NEAR(Ef, rf, 1. / MICROSECONDS);
+               ASSERT_PRED_FORMAT2(FpClose, E, r);
+       }
+}
+
+// conversion from l_fp
+TEST_F(timevalTest, FromLFPbittest) {
+       // Not *exactly* a bittest, because 2**32 tests would take a
+       // really long time even on very fast machines! So we do test
+       // every 1000 fractional units.
+       for (u_int32 tsf = 0; tsf < ~u_int32(1000); tsf += 1000) {
+               timeval_wrap E(1, my_tsf_to_tick(tsf));
+               l_fp_wrap    a(1, tsf);
+               timeval_wrap r;
+
+               timeval_relfromlfp(r, a);
+
+               // The conversion might be off by one microsecond when
+               // comparing to calculated value; the table-driven
+               // conversion does not use all bits from the FP fraction
+               // and this truncation will affect the rounding.
+               ASSERT_PRED_FORMAT2(TimevalClose, E, r);
+       }
+}
+
+// usec -> frac -> usec roundtrip
+TEST_F(timevalTest, LFProundtrip) {
+       for (u_int32 i = 0; i < 1000000; i++) {
+               timeval_wrap E(1, i);
+               l_fp_wrap    a;
+               timeval_wrap r;
+
+               timeval_reltolfp(a, E);
+               timeval_relfromlfp(r, a);
+               ASSERT_EQ(E, r);
        }
 }
 
 TEST_F(timevalTest, ToString) {
        static const struct {
-               time_t          sec;
-               long            usec;
-               const char *    repr;
+               time_t       sec;
+               long         usec;
+               const char * repr;
        } data [] = {
                { 0, 0,  "0.000000" },
                { 2, 0,  "2.000000" },
@@ -365,9 +386,9 @@ TEST_F(timevalTest, ToString) {
                {-1, 1, "-0.999999" }
        };
        for (int i = 0; i < COUNTOF(data); ++i) {
-               TVAL a(data[i].sec, data[i].usec);
-               std::string E(data[i].repr);
-               std::string r(timeval_tostr(a));
+               timeval_wrap a(data[i].sec, data[i].usec);
+               std::string  E(data[i].repr);
+               std::string  r(timeval_tostr(a));
 
                ASSERT_EQ(E, r);
        }