From: Juergen Perlinger Date: Sun, 16 Jan 2011 13:56:08 +0000 (+0100) Subject: added timespecops and timevalops for operations on struct timespec and struct timeval X-Git-Tag: NTP_4_2_7P119~1^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e156478155d66176e23776f1934d649e45bfa2b5;p=thirdparty%2Fntp.git added timespecops and timevalops for operations on struct timespec and struct timeval bk: 4d32f8f8yT-YfT6AAJsfS6GF8rPXbw --- diff --git a/ChangeLog b/ChangeLog index 0bcc93fd3..82fdc5c45 100644 --- 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 * 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 index 000000000..e9c47362e --- /dev/null +++ b/include/timespecops.h @@ -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 +#include +#include +#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 index 000000000..4dc2c4e2b --- /dev/null +++ b/include/timevalops.h @@ -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 +#include +#include +#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 -*- */ diff --git a/libntp/Makefile.am b/libntp/Makefile.am index 79ee44d08..17b32266b 100644 --- a/libntp/Makefile.am +++ b/libntp/Makefile.am @@ -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 index 000000000..1db0035a2 --- /dev/null +++ b/libntp/timespecops.c @@ -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 +#include + +#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 index 000000000..b48b2a874 --- /dev/null +++ b/libntp/timevalops.c @@ -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 +#include + +#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 -*- */ diff --git a/tests/libntp/Makefile.am b/tests/libntp/Makefile.am index ff49c0ef9..47c2ba27f 100644 --- a/tests/libntp/Makefile.am +++ b/tests/libntp/Makefile.am @@ -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 index 000000000..c13b13194 --- /dev/null +++ b/tests/libntp/tspecops.cpp @@ -0,0 +1,327 @@ +#include "libntptest.h" + +extern "C" { +#include "timespecops.h" +} + +#include +#include + +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 -*-