Home | History | Annotate | Line # | Download | only in libntp
      1 /*	$NetBSD: timespecops.c,v 1.2 2020/05/25 20:47:24 christos Exp $	*/
      2 
      3 /*
      4  * timespecops.c -- calculations on 'struct timespec' values
      5  *
      6  * Written by Juergen Perlinger (perlinger (at) ntp.org) for the NTP project.
      7  * The contents of 'html/copyright.html' apply.
      8  *
      9  */
     10 
     11 #include "config.h"
     12 
     13 #include <sys/types.h>
     14 #include <stdio.h>
     15 #include <math.h>
     16 
     17 #include "ntp.h"
     18 #include "timetoa.h"
     19 #include "timespecops.h"
     20 
     21 
     22 /* nanoseconds per second */
     23 #define NANOSECONDS 1000000000
     24 
     25 /* conversion between l_fp fractions and nanoseconds */
     26 #ifdef HAVE_U_INT64
     27 # define FTOTVN(tsf)						\
     28 	((int32)						\
     29 	 (((u_int64)(tsf) * NANOSECONDS + 0x80000000) >> 32))
     30 # define TVNTOF(tvu)						\
     31 	((u_int32)						\
     32 	 ((((u_int64)(tvu) << 32) + NANOSECONDS / 2) /		\
     33 	  NANOSECONDS))
     34 #else
     35 # define NSECFRAC	(FRAC / NANOSECONDS)
     36 # define FTOTVN(tsf)						\
     37 	((int32)((tsf) / NSECFRAC + 0.5))
     38 # define TVNTOF(tvu)						\
     39 	((u_int32)((tvu) * NSECFRAC + 0.5))
     40 #endif
     41 
     42 
     43 
     44 /* make sure nanoseconds are in nominal range */
     45 struct timespec
     46 normalize_tspec(
     47 	struct timespec x
     48 	)
     49 {
     50 #if SIZEOF_LONG > 4
     51 	long	z;
     52 
     53 	/*
     54 	 * tv_nsec is of type 'long', and on a 64-bit machine using only
     55 	 * loops becomes prohibitive once the upper 32 bits get
     56 	 * involved. On the other hand, division by constant should be
     57 	 * fast enough; so we do a division of the nanoseconds in that
     58 	 * case. The floor adjustment step follows with the standard
     59 	 * normalisation loops. And labs() is intentionally not used
     60 	 * here: it has implementation-defined behaviour when applied
     61 	 * to LONG_MIN.
     62 	 */
     63 	if (x.tv_nsec < -3l * NANOSECONDS ||
     64 	    x.tv_nsec > 3l * NANOSECONDS) {
     65 		z = x.tv_nsec / NANOSECONDS;
     66 		x.tv_nsec -= z * NANOSECONDS;
     67 		x.tv_sec += z;
     68 	}
     69 #endif
     70 	/* since 10**9 is close to 2**32, we don't divide but do a
     71 	 * normalisation in a loop; this takes 3 steps max, and should
     72 	 * outperform a division even if the mul-by-inverse trick is
     73 	 * employed. */
     74 	if (x.tv_nsec < 0)
     75 		do {
     76 			x.tv_nsec += NANOSECONDS;
     77 			x.tv_sec--;
     78 		} while (x.tv_nsec < 0);
     79 	else if (x.tv_nsec >= NANOSECONDS)
     80 		do {
     81 			x.tv_nsec -= NANOSECONDS;
     82 			x.tv_sec++;
     83 		} while (x.tv_nsec >= NANOSECONDS);
     84 
     85 	return x;
     86 }
     87 
     88 /* x = abs(a) */
     89 struct timespec
     90 abs_tspec(
     91 	struct timespec	a
     92 	)
     93 {
     94 	struct timespec	c;
     95 
     96 	c = normalize_tspec(a);
     97 	if (c.tv_sec < 0) {
     98 		if (c.tv_nsec != 0) {
     99 			c.tv_sec = -c.tv_sec - 1;
    100 			c.tv_nsec = NANOSECONDS - c.tv_nsec;
    101 		} else {
    102 			c.tv_sec = -c.tv_sec;
    103 		}
    104 	}
    105 
    106 	return c;
    107 }
    108 
    109 /*
    110  * compare previously-normalised a and b
    111  * return 1 / 0 / -1 if a < / == / > b
    112  */
    113 int
    114 cmp_tspec(
    115 	struct timespec a,
    116 	struct timespec b
    117 	)
    118 {
    119 	int r;
    120 
    121 	r = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec);
    122 	if (0 == r)
    123 		r = (a.tv_nsec > b.tv_nsec) -
    124 		    (a.tv_nsec < b.tv_nsec);
    125 
    126 	return r;
    127 }
    128 
    129 /*
    130  * test previously-normalised a
    131  * return 1 / 0 / -1 if a < / == / > 0
    132  */
    133 int
    134 test_tspec(
    135 	struct timespec	a
    136 	)
    137 {
    138 	int		r;
    139 
    140 	r = (a.tv_sec > 0) - (a.tv_sec < 0);
    141 	if (r == 0)
    142 		r = (a.tv_nsec > 0);
    143 
    144 	return r;
    145 }
    146 
    147 /*
    148  *  convert to l_fp type, relative and absolute
    149  */
    150 
    151 /* convert from timespec duration to l_fp duration */
    152 l_fp
    153 tspec_intv_to_lfp(
    154 	struct timespec	x
    155 	)
    156 {
    157 	struct timespec	v;
    158 	l_fp		y;
    159 
    160 	v = normalize_tspec(x);
    161 	y.l_uf = TVNTOF(v.tv_nsec);
    162 	y.l_i = (int32)v.tv_sec;
    163 
    164 	return y;
    165 }
    166 
    167 /* convert from l_fp type, relative signed/unsigned and absolute */
    168 struct timespec
    169 lfp_intv_to_tspec(
    170 	l_fp		x
    171 	)
    172 {
    173 	struct timespec out;
    174 	l_fp		absx;
    175 	int		neg;
    176 
    177 	neg = L_ISNEG(&x);
    178 	absx = x;
    179 	if (neg) {
    180 		L_NEG(&absx);
    181 	}
    182 	out.tv_nsec = FTOTVN(absx.l_uf);
    183 	out.tv_sec = absx.l_i;
    184 	if (neg) {
    185 		out.tv_sec = -out.tv_sec;
    186 		out.tv_nsec = -out.tv_nsec;
    187 		out = normalize_tspec(out);
    188 	}
    189 
    190 	return out;
    191 }
    192 
    193 struct timespec
    194 lfp_uintv_to_tspec(
    195 	l_fp		x
    196 	)
    197 {
    198 	struct timespec	out;
    199 
    200 	out.tv_nsec = FTOTVN(x.l_uf);
    201 	out.tv_sec = x.l_ui;
    202 
    203 	return out;
    204 }
    205 
    206 /*
    207  * absolute (timestamp) conversion. Input is time in NTP epoch, output
    208  * is in UN*X epoch. The NTP time stamp will be expanded around the
    209  * pivot time *p or the current time, if p is NULL.
    210  */
    211 struct timespec
    212 lfp_stamp_to_tspec(
    213 	l_fp		x,
    214 	const time_t *	p
    215 	)
    216 {
    217 	struct timespec	out;
    218 	vint64		sec;
    219 
    220 	sec = ntpcal_ntp_to_time(x.l_ui, p);
    221 	out.tv_nsec = FTOTVN(x.l_uf);
    222 
    223 	/* copying a vint64 to a time_t needs some care... */
    224 #if SIZEOF_TIME_T <= 4
    225 	out.tv_sec = (time_t)sec.d_s.lo;
    226 #elif defined(HAVE_INT64)
    227 	out.tv_sec = (time_t)sec.q_s;
    228 #else
    229 	out.tv_sec = ((time_t)sec.d_s.hi << 32) | sec.d_s.lo;
    230 #endif
    231 
    232 	return out;
    233 }
    234 
    235 /* -*-EOF-*- */
    236