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@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 */ 45struct timespec 46normalize_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) */ 89struct timespec 90abs_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 */ 113int 114cmp_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 */ 133int 134test_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 */ 152l_fp 153tspec_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 */ 168struct timespec 169lfp_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 193struct timespec 194lfp_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 */ 211struct timespec 212lfp_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