]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
added timespecops and timevalops for operations on struct timespec and struct timeval
authorJuergen Perlinger <perlinger@ntp.org>
Sun, 16 Jan 2011 13:56:08 +0000 (14:56 +0100)
committerJuergen Perlinger <perlinger@ntp.org>
Sun, 16 Jan 2011 13:56:08 +0000 (14:56 +0100)
bk: 4d32f8f8yT-YfT6AAJsfS6GF8rPXbw

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

index 0bcc93fd3d3907c543d9418beaf8ccdc31c43406..82fdc5c45727510e9cf080a6b971da711031538b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,5 @@
+* added timespecops.{c,h} and tievalops.{c.h} to libntp and include
+  added tspecops.cpp to tests/libntp
 (4.2.7p118) 2011/01/15 Released by Harlan Stenn <stenn@ntp.org>
 * Simplify the built-sources stuff in sntp/ .
 * Fix check for -lipv6 on HP-UX 11.
diff --git a/include/timespecops.h b/include/timespecops.h
new file mode 100644 (file)
index 0000000..e9c4736
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * timespecops.h -- calculations on 'struct timespec' values
+ *
+ * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ *
+ * Rationale
+ * ---------
+ *
+ * Doing basic arithmetic on a 'struct timespec' is not exceedingly
+ * hard, but it requires tedious and repetitive code to keep the result
+ * normalised. We consider a timespec normalised when the nanosecond
+ * fraction is in the interval [0 .. 10^9[ ; there are multiple value
+ * pairs of seconds and nanoseconds that denote the same time interval,
+ * but the normalised representation is unique. No two different
+ * intervals can have the same normalised representation.
+ *
+ * Another topic is the representation of negative time intervals.
+ * There's more than one way to this, since both the seconds and the
+ * nanoseconds of a timespec are signed values. IMHO, the easiest way is
+ * to use a complement representation where the nanoseconds are still
+ * normalised, no matter what the sign of the seconds value. This makes
+ * normalisation easier, since the sign of the integer part is
+ * irrelevant, and it removes several sign decision cases during the
+ * calculations.
+ *
+ * As long as no signed integer overflow can occur with the nanosecond
+ * part of the operands, all operations work as expected and produce a
+ * normalised result.
+ *
+ * The exception to this are functions fix a '_fast' suffix, which do no
+ * normalisation on input data and therefore expect the input data to be
+ * normalised.
+ */
+#ifndef TIMPESPECOPS_H
+#define TIMPESPECOPS_H
+
+#include <config.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include "ntp_unixtime.h"
+#include "ntp_fp.h"
+
+
+/*
+ * We avoid to use/define NANOSECONDS here, as it is also defined in
+ * some other files and we don't want to resolve the possible clashes
+ * here.
+ */
+
+/* predicate: returns TRUE if the nanoseconds are out-of-bounds */
+#define timespec_isdenormal(x) \
+       ((u_long)(x)->tv_nsec >= 1000000000)
+
+/* predicate: returns TRUE if the nanoseconds are in nominal range */
+#define timespec_isnormal(x) \
+       ((u_long)(x)->tv_nsec <  1000000000)
+
+
+/*make sure nanoseconds are in nominal range */
+extern void timespec_norm(struct timespec *x);
+
+/* x = a, normlised */
+extern void timespec_copy(struct timespec *x, const struct timespec *a);
+
+/* x = a + b */
+extern void timespec_add(struct timespec *x, const struct timespec *a,
+                        const struct timespec *b);
+
+/* x = a + b, b is fraction only */
+extern void timespec_addns(struct timespec *x, const struct timespec *a,
+                          long b);
+
+/* x = a + b */
+extern void timespec_sub(struct timespec *x, const struct timespec *a,
+                        const struct timespec *b);
+
+/* x = a - b, b is fraction only */
+extern void timespec_subns(struct timespec *x, const struct timespec *a,
+                          long b);
+
+/* x = -a */
+extern void timespec_neg(struct timespec *x, const struct timespec *a);
+
+/* x = ( a < 0) ? -a : a
+ * return if negation was needed
+ */
+extern int timespec_abs(struct timespec *x, const struct timespec *a);
+
+/* compare a <--> b
+ * return 1 / 0 / -1 if         a < / == / > b
+ */
+extern int timespec_cmp_fast(const struct timespec *a,
+                            const struct timespec *b);
+
+extern int timespec_cmp(const struct timespec *a,
+                       const struct timespec *b);
+
+/* test a
+ * return 1 / 0 / -1 if         a < / == / > 0
+ */
+extern int timespec_test_fast(const struct timespec *a);
+extern int timespec_test(const struct timespec *a);
+
+/* return LIB buffer ptr to string rep */
+extern const char* timespec_tostr(const struct timespec *x);
+
+/*
+  convert to l_fp type, relative and absolute
+*/
+
+/* convert from duration to duration */
+extern void timespec_reltolfp(l_fp *y, const struct timespec *x);
+
+/* 'x' must be UN*X epoch, output will be in NTP epoch */
+extern void timespec_abstolfp(l_fp *y, const struct timespec *x);
+
+/*
+  convert to l_fp type, relative signed/unsigned and absolute
+*/
+extern void timespec_relfromlfp(struct timespec *y, const l_fp *x);
+extern void timespec_urelfromlfp(struct timespec *y, const l_fp *x);
+
+/* absolute (timestamp) conversion. Input is time in NTP epoch, output
+ * is in UN*X epoch. The NTP time stamp will be expanded the pivot time
+ * '*' or the current time, if 'p' is NULL.
+ */
+extern void timespec_absfromlfp(struct timespec *y, const l_fp *x,
+                               const time_t *p);
+#endif
+/* -*- EOF -*- */
diff --git a/include/timevalops.h b/include/timevalops.h
new file mode 100644 (file)
index 0000000..4dc2c4e
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * timevalops.h -- calculations on 'struct timeval' values
+ *
+ * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ *
+ * For a rationale look at 'timespecops.h'; we do the same here, but the
+ * normalisation keeps the microseconds in [0 .. 10^6[, of course.
+ */
+#ifndef TIMPEVALOPS_H
+#define TIMPEVALOPS_H
+
+#include <config.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include "ntp_unixtime.h"
+#include "ntp_fp.h"
+
+/*
+ * We avoid to use/define MICROSECONDS here, as it is also possibly
+ * Defined in some other files and we don't want to resolve the possible
+ * clashes here.
+ */
+
+/* predicate: returns TRUE if the microseconds are out-of-bounds */
+/* use like: int timeval_isdenormal(const struct timeval *x) */
+#define timeval_isdenormal(x) \
+       ((u_long)(x)->tv_usec >= 1000000)
+
+/* predicate: returns TRUE if the microseconds are in nominal range */
+/* use like: int timeval_isnormal(const struct timeval *x) */
+#define timeval_isnormal(x) \
+       ((u_long)(x)->tv_usec <  1000000)
+
+
+/*make sure microseconds are in nominal range */
+extern void timeval_norm(struct timeval *x);
+
+/* x = a, normlised */
+extern void timeval_copy(struct timeval *x, const struct timeval *a);
+
+/* x = a + b */
+extern void timeval_add(struct timeval *x, const struct timeval *a,
+                       const struct timeval *b);
+
+/* x = a + b, b is fraction only */
+extern void timeval_addus(struct timeval *x, const struct timeval *a,
+                         long b);
+
+/* x = a + b */
+extern void timeval_sub(struct timeval *x, const struct timeval *a,
+                       const struct timeval *b);
+
+/* x = a - b, b is fraction only */
+extern void timeval_subus(struct timeval *x, const struct timeval *a,
+                         long  b);
+
+/* x = -a */
+extern void timeval_neg(struct timeval *x, const struct timeval *a);
+
+/* x = ( a < 0) ? -a : a
+ * return if negation was needed
+ */
+extern int timeval_abs(struct timeval *x, const struct timeval *a);
+
+/* compare a <--> b
+ * return 1 / 0 / -1 if         a < / == / > b
+ */
+extern int timeval_cmp_fast(const struct timeval *a,
+                           const struct timeval *b);
+
+extern int timeval_cmp(const struct timeval *a,
+                      const struct timeval *b);
+
+/* test a
+ * return 1 / 0 / -1 if         a < / == / > 0
+ */
+extern int timeval_test_fast(const struct timeval *a);
+extern int timeval_test(const struct timeval *a);
+
+/* return LIB buffer ptr to string rep */
+extern const char* timeval_tostr(const struct timeval *x);
+
+/*
+  convert to l_fp type, relative and absolute
+*/
+
+/* convert from duration to duration */
+extern void timeval_reltolfp(l_fp *y, const struct timeval *x);
+
+/* 'x' must be UN*X epoch, output will be in NTP epoch */
+extern void timeval_abstolfp(l_fp *y, const struct timeval *x);
+
+/*
+  convert to l_fp type, relative signed/unsigned and absolute
+*/
+extern void timeval_relfromlfp(struct timeval *y, const l_fp *x);
+extern void timeval_urelfromlfp(struct timeval *y, const l_fp *x);
+
+/* absolute (timestamp) conversion. Input is time in NTP epoch, output
+ * is in UN*X epoch. The NTP time stamp will be expanded the pivot time
+ * '*' or the current time, if 'p' is NULL.
+ */
+extern void timeval_absfromlfp(struct timeval *y, const l_fp *x,
+                              const time_t *p);
+
+#endif
+/* -*- EOF -*- */
index 79ee44d08fc38016f5dac4180aef7183d149577f..17b32266b0ea029b4825f2ee3f32df314b48d194 100644 (file)
@@ -64,6 +64,8 @@ libntp_a_SRCS =                                               \
        statestr.c                                      \
        strdup.c                                        \
        syssignal.c                                     \
+       timespecops.c                                   \
+       timevalops.c                                    \
        tsftomsu.c                                      \
        tstotv.c                                        \
        tvtots.c                                        \
diff --git a/libntp/timespecops.c b/libntp/timespecops.c
new file mode 100644 (file)
index 0000000..1db0035
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * timespecops.c -- calculations on 'struct timespec' values
+ *
+ * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ */
+
+#include <config.h>
+#include <math.h>
+
+#include "lib_strbuf.h"
+#include "ntp_calendar.h"
+
+#include "timespecops.h"
+
+#undef NANOSECONDS
+#define NANOSECONDS 1000000000
+
+#if SIZEOF_LONG >= 8
+# define MYFTOTVN(tsf,tvu) \
+       (tvu) = (int32)(((u_long)(tsf) * NANOSECONDS + 0x80000000) >> 32)
+# define MYTVNTOF(tvu,tsf) \
+       (tsf) = (u_int32)((((u_long)(tvu)<<32)+NANOSECONDS/2) / NANOSECONDS)
+#else
+# define MYFTOTVN(tsf,tvu) \
+       (tvu) = (int32)floor((tsf) / 4.294967296 + 0.5)
+# define MYTVNTOF(tvu,tsf) \
+       (tsf) = (u_int32)floor((tvu) * 4.294967296 + 0.5)
+#endif
+
+#define COPYNORM(dst,src) \
+  do {   *(dst) = *(src);              \
+         if (timespec_isdenormal((dst)))       \
+                 timespec_norm((dst)); \
+  } while (0)
+       
+
+void
+timespec_norm(
+       struct timespec *x
+       )
+{
+#if SIZEOF_LONG > 4
+       /* tv_nsec is of type 'long', and on a 64 bit machine
+        * normalisation by loop becomes prohibitive, once the upper 32
+        * bits become involved. On the other hand, division by constant
+        * should be fast enough; so we do a division of the nanoseconds
+        * if the high dword becomes involved. The floor adjustment step
+        * follows with the standard normalisation loops. */
+       long z;
+
+       z = (x->tv_nsec < 0) ? -x->tv_nsec : x->tv_nsec;
+       if (z >> 32) {
+               z = x->tv_nsec / NANOSECONDS;
+               x->tv_nsec -= z * NANOSECONDS;
+               x->tv_sec  += z;
+       }
+#endif
+
+       /* since 10**9 is close to 2**32, we don't divide but do a
+        * normalisation in a loop; this takes 3 steps max, and should
+        * outperform a division even if the mul-by-inverse trick is
+        * employed. */
+       if (x->tv_nsec < 0)
+               do {
+                       x->tv_nsec += NANOSECONDS;
+                       x->tv_sec  -= 1;
+               } while (x->tv_nsec < 0);
+       else if (x->tv_nsec >= NANOSECONDS)
+               do {
+                       x->tv_nsec -= NANOSECONDS;
+                       x->tv_sec  += 1;
+               } while (x->tv_nsec >= NANOSECONDS);
+}
+
+/* x = a, normlised */
+void
+timespec_copy(
+             struct timespec *x,
+       const struct timespec *a
+       )
+{
+       COPYNORM(x,a);
+}
+
+/* x = a + b */
+void
+timespec_add(
+             struct timespec *x,
+       const struct timespec *a,
+       const struct timespec *b
+       )
+{      
+       struct timespec c;
+
+       c.tv_sec  = a->tv_sec  + b->tv_sec;
+       c.tv_nsec = a->tv_nsec + b->tv_nsec;
+       COPYNORM(x, &c);
+}
+
+/* x = a + b, b is fraction only */
+void
+timespec_addns(
+             struct timespec *x,
+       const struct timespec *a,
+       long                   b
+       )
+{      
+       struct timespec c;
+
+       c.tv_sec  = a->tv_sec;
+       c.tv_nsec = a->tv_nsec + b;
+       COPYNORM(x, &c);
+}
+
+/* x = a + b */
+void
+timespec_sub(
+             struct timespec *x,
+       const struct timespec *a,
+       const struct timespec *b
+       )
+{      
+       struct timespec c;
+
+       c.tv_sec  = a->tv_sec  - b->tv_sec;
+       c.tv_nsec = a->tv_nsec - b->tv_nsec;
+       COPYNORM(x, &c);
+}
+
+/* x = a - b, b is fraction only */
+void
+timespec_subns(
+             struct timespec *x,
+       const struct timespec *a,
+       long                   b
+       )
+{      
+       struct timespec c;
+
+       c.tv_sec  = a->tv_sec;
+       c.tv_nsec = a->tv_nsec - b;
+       COPYNORM(x, &c);
+}
+
+/* x = -a */
+void
+timespec_neg(
+             struct timespec *x,
+       const struct timespec *a
+       )
+{      
+       struct timespec c;
+
+       c.tv_sec  = - a->tv_sec;
+       c.tv_nsec = - a->tv_nsec;
+       COPYNORM(x, &c);
+}
+
+/* x = ( a < 0) ? -a : a
+ * return if negation was needed
+ */
+int
+timespec_abs(
+             struct timespec *x,
+       const struct timespec *a
+       )
+{      
+       struct timespec c;
+       int             r;
+
+       COPYNORM(&c, a);
+       if ((r = (c.tv_sec < 0)) != 0) {
+               c.tv_sec  = - c.tv_sec;
+               c.tv_nsec = - c.tv_nsec;
+               if (c.tv_nsec < 0) {
+                       c.tv_sec  -= 1;
+                       c.tv_nsec += NANOSECONDS;
+               }
+       }
+       *x = c;
+
+       return r;
+}
+
+/* compare a <--> b
+ * return 1 / 0 / -1 if         a < / == / > b
+ */
+int
+timespec_cmp_fast(
+       const struct timespec *a,
+       const struct timespec *b
+       )
+{
+       int r;
+
+       r = (a->tv_sec > b->tv_sec)
+         - (a->tv_sec < b->tv_sec);
+       if (r == 0)
+               r = (a->tv_nsec > b->tv_nsec)
+                 - (a->tv_nsec < b->tv_nsec);
+       
+       return r;
+}
+
+int
+timespec_cmp(
+       const struct timespec *a,
+       const struct timespec *b
+       )
+{
+       struct timespec A;
+       struct timespec B;
+
+       COPYNORM(&A, a);
+       COPYNORM(&B, b);
+       return timespec_cmp_fast(&A, &B);
+}
+
+/* test a
+ * return 1 / 0 / -1 if         a < / == / > 0
+ */
+int
+timespec_test_fast(
+       const struct timespec *a
+       )
+{
+       int r;
+
+       r = (a->tv_sec > 0) - (a->tv_sec < 0);
+       if (r == 0)
+               r = (a->tv_nsec > 0);
+       
+       return r;
+}
+
+int
+timespec_test(
+       const struct timespec *a
+       )
+{
+       struct timespec A;
+
+       COPYNORM(&A, a);
+       return timespec_test_fast(&A);
+}
+
+const char*
+timespec_tostr(
+       const struct timespec *x
+       )
+{
+       struct timespec v;
+       int             s;
+       char           *cp;
+
+       LIB_GETBUF(cp);
+       s = timespec_abs(&v, x);
+       if (v.tv_sec >= 0)
+               snprintf(cp, LIB_BUFLENGTH, "%s%ld.%09ld",
+                        "-"+(s==0), (long)v.tv_sec, (long)v.tv_nsec);
+       else if (v.tv_nsec == 0)
+               snprintf(cp, LIB_BUFLENGTH, "%ld.000000000",
+                        (long)v.tv_sec);
+       else
+               cp = "#OVERFLOW#";
+
+       return cp;
+}
+
+void
+timespec_abstolfp(
+       l_fp                  *y,
+       const struct timespec *x
+       )
+{
+       struct timespec v;
+
+       COPYNORM(&v, x);
+       MYTVNTOF(v.tv_nsec, y->l_uf);
+       y->l_ui = (u_int32)v.tv_sec + JAN_1970;
+}
+
+void
+timespec_reltolfp(
+       l_fp                  *y,
+       const struct timespec *x
+       )
+{
+       struct timespec v;
+       
+       COPYNORM(&v, x);
+       MYTVNTOF(v.tv_nsec, y->l_uf);
+       y->l_i = (int32)v.tv_sec;
+
+}
+
+
+void
+timespec_relfromlfp(
+       struct timespec *y,
+       const l_fp      *x)
+{
+       struct timespec out;
+
+       MYFTOTVN(x->l_uf, out.tv_nsec);
+       out.tv_sec = x->l_i;
+       COPYNORM(y, &out);
+}
+
+void
+timespec_urelfromlfp(
+       struct timespec *y,
+       const l_fp      *x)
+{
+       struct timespec out;
+       
+       MYFTOTVN(x->l_uf, out.tv_nsec);
+       out.tv_sec = x->l_ui;
+       COPYNORM(y, &out);
+}
+
+void
+timespec_absfromlfp(
+       struct timespec *y,
+       const l_fp      *x,
+       const time_t    *p
+       )
+{
+       struct timespec out;
+       vint64          sec;
+
+       sec = ntpcal_ntp_to_time(x->l_ui, p);
+       MYFTOTVN(x->l_uf, out.tv_nsec);
+
+       /* copying a vint64 to a time_t needs some care... */
+#   ifdef HAVE_INT64
+       out.tv_sec = (time_t)sec.q_s;
+#   elif SIZEOF_TIME_T > 4
+       out.tv_sec = ((time_t)sec.d_s.hi << 32) + sec.d_s.lo;
+#   else
+       out.tv_sec = (time_t)sec.d_s.lo;
+#   endif
+       
+       COPYNORM(y, &out);
+}
+/* -*- EOF -*- */
diff --git a/libntp/timevalops.c b/libntp/timevalops.c
new file mode 100644 (file)
index 0000000..b48b2a8
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * timevalops.c -- calculations on 'struct timeval' values
+ *
+ * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
+ * The contents of 'html/copyright.html' apply.
+ */
+
+#include <config.h>
+#include <math.h>
+
+#include "lib_strbuf.h"
+#include "ntp_calendar.h"
+
+#include "timevalops.h"
+
+#undef MICROSECONDS
+#define MICROSECONDS 1000000
+
+#if SIZEOF_LONG >= 8
+# define MYFTOTVU(tsf,tvu) \
+       (tvu) = (int32)(((u_long)(tsf) * MICROSECONDS + 0x80000000) >> 32)
+# define MYTVUTOF(tvu,tsf) \
+       (tsf) = (u_int32)((((u_long)(tvu)<<32)+MICROSECONDS/2) / MICROSECONDS)
+#else
+# define MYFTOTVU(tsf,tvu) TSFTOTVU(tsf, tvu)
+# define MYTVUTOF(tvu,tsf) TVUTOTSF(tvu, tsf)
+#endif
+
+#define COPYNORM(dst,src) \
+  do {   *(dst) = *(src);              \
+         if (timeval_isdenormal((dst)))        \
+                 timeval_norm((dst));  \
+  } while (0)
+       
+
+void
+timeval_norm(
+       struct timeval *x
+       )
+{
+       /* If the fraction becomes excessive denormal, we use division
+        * to do first partial normalisation. The normalisation loops
+        * following will do the remaining cleanup.
+        */
+       if (abs(x->tv_usec) >= 4*MICROSECONDS) {
+               long z;
+               z = x->tv_usec / MICROSECONDS;
+               x->tv_usec -= z * MICROSECONDS;
+               x->tv_sec  += z;
+       }
+
+       /* since 10**9 is close to 2**32, we don't divide but do a
+        * normalisation in a loop; this takes 3 steps max, and should
+        * outperform a division even if the mul-by-inverse trick is
+        * employed. */
+       if (x->tv_usec < 0)
+               do {
+                       x->tv_usec += MICROSECONDS;
+                       x->tv_sec  -= 1;
+               } while (x->tv_usec < 0);
+       else if (x->tv_usec >= MICROSECONDS)
+               do {
+                       x->tv_usec -= MICROSECONDS;
+                       x->tv_sec  += 1;
+               } while (x->tv_usec >= MICROSECONDS);
+}
+
+/* x = a, normlised */
+void
+timeval_copy(
+             struct timeval *x,
+       const struct timeval *a
+       )
+{
+       COPYNORM(x,a);
+}
+
+/* x = a + b */
+void
+timeval_add(
+             struct timeval *x,
+       const struct timeval *a,
+       const struct timeval *b
+       )
+{      
+       struct timeval c;
+
+       c.tv_sec  = a->tv_sec  + b->tv_sec;
+       c.tv_usec = a->tv_usec + b->tv_usec;
+       COPYNORM(x, &c);
+}
+
+/* x = a + b, b is fraction only */
+void
+timeval_addus(
+             struct timeval *x,
+       const struct timeval *a,
+       long                   b
+       )
+{      
+       struct timeval c;
+
+       c.tv_sec  = a->tv_sec;
+       c.tv_usec = a->tv_usec + b;
+       COPYNORM(x, &c);
+}
+
+/* x = a + b */
+void
+timeval_sub(
+             struct timeval *x,
+       const struct timeval *a,
+       const struct timeval *b
+       )
+{      
+       struct timeval c;
+
+       c.tv_sec  = a->tv_sec  - b->tv_sec;
+       c.tv_usec = a->tv_usec - b->tv_usec;
+       COPYNORM(x, &c);
+}
+
+/* x = a - b, b is fraction only */
+void
+timeval_subus(
+             struct timeval *x,
+       const struct timeval *a,
+       long                   b
+       )
+{      
+       struct timeval c;
+
+       c.tv_sec  = a->tv_sec;
+       c.tv_usec = a->tv_usec - b;
+       COPYNORM(x, &c);
+}
+
+/* x = -a */
+void
+timeval_neg(
+             struct timeval *x,
+       const struct timeval *a
+       )
+{      
+       struct timeval c;
+
+       c.tv_sec  = - a->tv_sec;
+       c.tv_usec = - a->tv_usec;
+       COPYNORM(x, &c);
+}
+
+/* x = ( a < 0) ? -a : a
+ * return if negation was needed
+ */
+int
+timeval_abs(
+             struct timeval *x,
+       const struct timeval *a
+       )
+{      
+       struct timeval c;
+       int             r;
+
+       COPYNORM(&c, a);
+       if ((r = (c.tv_sec < 0)) != 0) {
+               c.tv_sec  = - c.tv_sec;
+               c.tv_usec = - c.tv_usec;
+               if (c.tv_usec < 0) {
+                       c.tv_sec  -= 1;
+                       c.tv_usec += MICROSECONDS;
+               }
+       }
+       *x = c;
+
+       return r;
+}
+
+/* compare a <--> b
+ * return 1 / 0 / -1 if         a < / == / > b
+ */
+int
+timeval_cmp_fast(
+       const struct timeval *a,
+       const struct timeval *b
+       )
+{
+       int r;
+
+       r = (a->tv_sec > b->tv_sec)
+         - (a->tv_sec < b->tv_sec);
+       if (r == 0)
+               r = (a->tv_usec > b->tv_usec)
+                 - (a->tv_usec < b->tv_usec);
+       
+       return r;
+}
+
+int
+timeval_cmp(
+       const struct timeval *a,
+       const struct timeval *b
+       )
+{
+       struct timeval A;
+       struct timeval B;
+
+       COPYNORM(&A, a);
+       COPYNORM(&B, b);
+       return timeval_cmp_fast(&A, &B);
+}
+
+/* test a
+ * return 1 / 0 / -1 if         a < / == / > 0
+ */
+int
+timeval_test_fast(
+       const struct timeval *a
+       )
+{
+       int r;
+
+       r = (a->tv_sec > 0) - (a->tv_sec < 0);
+       if (r == 0)
+               r = (a->tv_usec > 0);
+       
+       return r;
+}
+
+int
+timeval_test(
+       const struct timeval *a
+       )
+{
+       struct timeval A;
+       int            r;
+
+       COPYNORM(&A, a);
+       r = (A.tv_sec > 0) - (A.tv_sec < 0);
+       if (r == 0)
+               r = (A.tv_usec > 0);
+       
+       return r;
+}
+
+/* return LIB buffer ptr to string rep */
+const char*
+timeval_tostr(
+       const struct timeval *x
+       )
+{
+       struct timeval v;
+       int            s;
+       char          *cp;
+
+       LIB_GETBUF(cp);
+       s = timeval_abs(&v, x);
+       if (v.tv_sec >= 0)
+               snprintf(cp, LIB_BUFLENGTH, "%s%ld.%06ld",
+                        "-"+(s==0), (long)v.tv_sec, (long)v.tv_usec);
+       else if (v.tv_usec == 0)
+               snprintf(cp, LIB_BUFLENGTH, "%ld.000000",
+                        (long)v.tv_sec);
+       else
+               cp = "#OVERFLOW#";
+
+       return cp;
+}
+
+void
+timeval_abstolfp(
+       l_fp                 *y,
+       const struct timeval *x
+       )
+{
+       struct timeval v;
+
+       COPYNORM(&v, x);
+       MYTVUTOF(v.tv_usec, y->l_uf);
+       y->l_ui = (u_int32)v.tv_sec + JAN_1970;
+}
+
+void
+timeval_reltolfp(
+       l_fp                 *y,
+       const struct timeval *x
+       )
+{
+       struct timeval v;
+       
+       COPYNORM(&v, x);
+       MYTVUTOF(v.tv_usec, y->l_uf);
+       y->l_i = (int32)v.tv_sec;
+
+}
+
+
+void
+timeval_relfromlfp(
+       struct timeval *y,
+       const l_fp     *x)
+{
+       struct timeval out;
+
+       MYFTOTVU(x->l_uf, out.tv_usec);
+       out.tv_sec = x->l_i;
+       COPYNORM(y, &out);
+}
+
+void
+timeval_urelfromlfp(
+       struct timeval *y,
+       const l_fp     *x)
+{
+       struct timeval out;
+       
+       MYFTOTVU(x->l_uf, out.tv_usec);
+       out.tv_sec = x->l_ui;
+       COPYNORM(y, &out);
+}
+
+void
+timeval_absfromlfp(
+       struct timeval *y,
+       const l_fp     *x,
+       const time_t   *p
+       )
+{
+       struct timeval out;
+       vint64         sec;
+
+       sec = ntpcal_ntp_to_time(x->l_ui, p);
+       MYFTOTVU(x->l_uf, out.tv_usec);
+
+       /* copying a vint64 to a time_t needs some care... */
+#   ifdef HAVE_INT64
+       out.tv_sec = (time_t)sec.q_s;
+#   elif SIZEOF_TIME_T > 4
+       out.tv_sec = ((time_t)sec.d_s.hi << 32) + sec.d_s.lo;
+#   else
+       out.tv_sec = (time_t)sec.d_s.lo;
+#   endif
+       
+       COPYNORM(y, &out);
+}
+
+/* -*- EOF -*- */
index ff49c0ef9c2d8457486d1a53d7dcd2f80ec15cd8..47c2ba27f8835cb59e7399b45088169648a023d8 100644 (file)
@@ -8,7 +8,7 @@ LDADD =                                 \
        @GTEST_LDFLAGS@                 \
        @GTEST_LIBS@                    \
        $(NULL)
-       
+
 AM_CFLAGS   = @CFLAGS_NTP@
 AM_CXXFLAGS = @GTEST_CXXFLAGS@
 AM_CPPFLAGS = @GTEST_CPPFLAGS@ @CPPFLAGS_NTP@
@@ -45,6 +45,7 @@ tests_SOURCES = $(top_srcdir)/sntp/tests_main.cpp     \
                ssl_init.cpp            \
                statestr.cpp            \
                strtolfp.cpp            \
+               tspecops.cpp            \
                tsftomsu.cpp            \
                tstotv.cpp              \
                tvtots.cpp              \
diff --git a/tests/libntp/tspecops.cpp b/tests/libntp/tspecops.cpp
new file mode 100644 (file)
index 0000000..c13b131
--- /dev/null
@@ -0,0 +1,327 @@
+#include "libntptest.h"
+
+extern "C" {
+#include "timespecops.h"
+}
+
+#include <string>
+#include <sstream>
+
+class timespecTest : public libntptest {
+protected:
+       static const long NANOSECONDS;
+       // that's it...
+};
+const long timespecTest::NANOSECONDS = 1000000000;
+
+
+struct TSPEC{
+       struct timespec V;
+
+       TSPEC()
+               { ZERO(V); }
+       TSPEC(time_t hi, long lo)
+               { V.tv_sec = hi; V.tv_nsec = lo; }
+       bool operator == (const TSPEC &rhs) const
+               { return timespec_cmp(&V, &rhs.V) == 0; }
+       bool valid() const
+               { return timespec_isnormal(&V); }
+       operator struct timespec* () 
+               { return &V; }
+       operator struct timespec& ()
+               { return V;     }
+       TSPEC &operator=(const TSPEC &rhs)
+               { V = rhs.V; return *this; }
+       TSPEC &operator=(const struct timespec &rhs)
+               { V = rhs; return *this; }
+};
+
+std::ostream&
+operator << (std::ostream& os, const TSPEC &val)
+{
+       os << timespec_tostr(&val.V);
+       return os;
+}
+
+
+struct LFP {
+       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; }
+};
+
+std::ostream&
+operator << (std::ostream& os, const LFP &val)
+{
+       os << ulfptoa(&val.V, 10);
+       return os;
+}
+
+
+// ---------------------------------------------------------------------
+// test support stuff
+// ---------------------------------------------------------------------
+
+TEST_F(timespecTest, Normalise) {
+       for (long ns = -2000000000; ns <= 2000000000; ns += 10000000) {
+               TSPEC x(0, ns);
+               timespec_norm(x);
+               ASSERT_TRUE(x.valid());
+       }
+}
+
+TEST_F(timespecTest, SignNoFrac) {
+       // sign test, no fraction
+       for (int i = -4; i <= 4; ++i) {
+               TSPEC a(i, 0);
+               int E = (i>0) - (i<0);
+               int r = timespec_test(a);
+               ASSERT_EQ(E, r);
+       }
+}
+
+TEST_F(timespecTest, SignWithFrac) {
+       // sign test, with fraction
+       for (int i = -4; i <= 4; ++i) {
+               TSPEC a(i, 10);
+               int E = (i>=0) - (i<0);
+               int r = timespec_test(a);
+               ASSERT_EQ(E, r);
+       }
+}
+
+// test compare
+TEST_F(timespecTest, CmpFracEQ) {
+       // fractions are equal
+       for (int i = -4; i <= 4; ++i)
+               for (int j = -4; j <= 4; ++j) {
+                       TSPEC a( i , 200);
+                       TSPEC b( j , 200);
+                       int   E = (i > j) - (i < j);
+                       int   r = timespec_cmp(a, b);
+                       ASSERT_EQ(E, r);
+               }
+}
+
+TEST_F(timespecTest, CmpFracGT) {
+       // fraction a bigger fraction b
+       for (int i = -4; i <= 4; ++i)
+               for (int j = -4; j <= 4; ++j) {
+                       TSPEC a( i , 999999800);
+                       TSPEC b( j , 200);
+                       int   E = (i >= j) - (i < j);
+                       int   r = timespec_cmp(a, b);
+                       ASSERT_EQ(E, r);
+               }
+}
+
+TEST_F(timespecTest, CmpFracLT) {
+       // fraction a less fraction b
+       for (int i = -4; i <= 4; ++i)
+               for (int j = -4; j <= 4; ++j) {
+                       TSPEC a( i , 200);
+                       TSPEC b( j , 999999800);
+                       int   E = (i > j) - (i <= j);
+                       int   r = timespec_cmp(a, b);
+                       ASSERT_EQ(E, r);
+               }
+}
+
+// Test addition (sum)
+TEST_F(timespecTest, AddFullNorm) {
+       for (int i = -4; i <= 4; ++i)
+               for (int j = -4; j <= 4; ++j) {
+                       TSPEC a( i , 200);
+                       TSPEC b( j , 400);
+                       TSPEC E(i+j, 600);
+                       TSPEC c;
+                       timespec_add(c, a, b);
+                       ASSERT_EQ(E, c);
+               }
+}
+
+TEST_F(timespecTest, AddFullOflow1) {
+       for (int i = -4; i <= 4; ++i)
+               for (int j = -4; j <= 4; ++j) {
+                       TSPEC a(  i  , 200);
+                       TSPEC b(  j  , 999999900);
+                       TSPEC E(i+j+1, 100);
+                       TSPEC c;
+                       timespec_add(c, a, b);
+                       ASSERT_EQ(E, c);
+               }
+}
+
+TEST_F(timespecTest, AddNsecNorm) {
+       for (int i = -4; i <= 4; ++i) {
+               TSPEC a(i, 200);
+               TSPEC E(i, 600);
+               TSPEC c;
+               timespec_addns(c, a, 400);
+               ASSERT_EQ(E, c);
+       }
+}
+
+TEST_F(timespecTest, AddNsecOflow1) {
+       for (int i = -4; i <= 4; ++i) {
+               TSPEC a( i , 200);
+               TSPEC E(i+1, 100);
+               TSPEC c;
+               timespec_addns(c, a, NANOSECONDS - 100);
+               ASSERT_EQ(E, c);
+       }
+}
+
+// test subtraction (difference)
+TEST_F(timespecTest, SubFullNorm) {
+       for (int i = -4; i <= 4; ++i)
+               for (int j = -4; j <= 4; ++j) {
+                       TSPEC a( i , 600);
+                       TSPEC b( j , 400);
+                       TSPEC E(i-j, 200);
+                       TSPEC c;
+                       timespec_sub(c, a, b);
+                       ASSERT_EQ(E, c);
+               }
+}
+
+TEST_F(timespecTest, SubFullOflow) {
+       for (int i = -4; i <= 4; ++i)
+               for (int j = -4; j <= 4; ++j) {
+                       TSPEC a(  i  , 100);
+                       TSPEC b(  j  , 999999900);
+                       TSPEC E(i-j-1, 200);
+                       TSPEC c;
+                       timespec_sub(c, a, b);
+                       ASSERT_EQ(E, c);
+               }
+}
+
+TEST_F(timespecTest, SubNsecNorm) {
+       for (int i = -4; i <= 4; ++i) {
+               TSPEC a(i, 600);
+               TSPEC E(i, 200);
+               TSPEC c;
+               timespec_subns(c, a, 400);
+               ASSERT_EQ(E, c);
+       }
+}
+
+TEST_F(timespecTest, SubNsecOflow) {
+       for (int i = -4; i <= 4; ++i) {
+               TSPEC a( i , 100);
+               TSPEC E(i-1, 200);
+               TSPEC c;
+               timespec_subns(c, a, NANOSECONDS - 100);
+               ASSERT_EQ(E, c);
+       }
+}
+
+// test negation
+TEST_F(timespecTest, Neg) {
+       for (int i = -4; i <= 4; ++i) {
+               TSPEC a( i , 100);
+               TSPEC b;
+               TSPEC c;
+               timespec_neg(b, a);
+               timespec_add(c, a, b);
+               ASSERT_EQ(0, timespec_test(c));
+       }
+}
+
+// test abs value
+TEST_F(timespecTest, AbsNoFrac) {
+       for (int i = -4; i <= 4; ++i) {
+               TSPEC a(i , 0);
+               TSPEC b;
+               int   c;
+               c = timespec_abs(b, a);
+               ASSERT_EQ((i < 0), c);
+               ASSERT_EQ((i != 0), timespec_test(b));
+       }
+}
+
+TEST_F(timespecTest, AbsWithFrac) {
+       for (int i = -4; i <= 4; ++i) {
+               TSPEC a(i , 100);
+               TSPEC b;
+               int   c;
+               c = timespec_abs(b, a);
+               ASSERT_EQ((i < 0), c);
+               ASSERT_EQ(1, timespec_test(b));
+       }
+}
+
+// conversion to l_fp
+TEST_F(timespecTest, ToLFPrel) {
+       static const struct {
+               long    nsec;
+               u_int32 frac;
+       } data [] = {
+               {         0, 0x00000000 }, {   2218896, 0x00916ae6 },
+               {  16408100, 0x0433523d }, { 125000000, 0x20000000 },
+               { 250000000, 0x40000000 }, { 287455871, 0x4996b53d },
+               { 375000000, 0x60000000 }, { 500000000, 0x80000000 },
+               { 518978897, 0x84dbcd0e }, { 563730222, 0x90509fb3 },
+               { 563788007, 0x9054692c }, { 583289882, 0x95527c57 },
+               { 607074509, 0x9b693c2a }, { 625000000, 0xa0000000 },
+               { 645184059, 0xa52ac851 }, { 676497788, 0xad2ef583 },
+               { 678910895, 0xadcd1abb }, { 679569625, 0xadf84663 },
+               { 690926741, 0xb0e0932d }, { 705656483, 0xb4a5e73d },
+               { 723553854, 0xb93ad34c }, { 750000000, 0xc0000000 },
+               { 763550253, 0xc3780785 }, { 775284917, 0xc6791284 },
+               { 826190764, 0xd3813ce8 }, { 875000000, 0xe0000000 },
+               { 956805507, 0xf4f134a9 }, { 982570733, 0xfb89c16c }
+       };
+       for (int i = 0; i < sizeof(data)/sizeof(*data); ++i) {
+               TSPEC a(1, data[i].nsec);
+               LFP   E(1, data[i].frac);
+               LFP   r;
+               timespec_reltolfp(r, a);
+               ASSERT_EQ(E, r);
+       }
+}
+
+TEST_F(timespecTest, ToLFPabs) {
+       static const struct {
+               long    nsec;
+               u_int32 frac;
+       } data [] = {
+               {         0, 0x00000000 }, {   2218896, 0x00916ae6 },
+               {  16408100, 0x0433523d }, { 125000000, 0x20000000 },
+               { 250000000, 0x40000000 }, { 287455871, 0x4996b53d },
+               { 375000000, 0x60000000 }, { 500000000, 0x80000000 },
+               { 518978897, 0x84dbcd0e }, { 563730222, 0x90509fb3 },
+               { 563788007, 0x9054692c }, { 583289882, 0x95527c57 },
+               { 607074509, 0x9b693c2a }, { 625000000, 0xa0000000 },
+               { 645184059, 0xa52ac851 }, { 676497788, 0xad2ef583 },
+               { 678910895, 0xadcd1abb }, { 679569625, 0xadf84663 },
+               { 690926741, 0xb0e0932d }, { 705656483, 0xb4a5e73d },
+               { 723553854, 0xb93ad34c }, { 750000000, 0xc0000000 },
+               { 763550253, 0xc3780785 }, { 775284917, 0xc6791284 },
+               { 826190764, 0xd3813ce8 }, { 875000000, 0xe0000000 },
+               { 956805507, 0xf4f134a9 }, { 982570733, 0xfb89c16c }
+       };
+       for (int i = 0; i < sizeof(data)/sizeof(*data); ++i) {
+               TSPEC a(1         , data[i].nsec);
+               LFP   E(1+JAN_1970, data[i].frac);
+               LFP   r;
+               timespec_abstolfp(r, a);
+               ASSERT_EQ(E, r);
+       }
+}
+
+// -*- EOF -*-