1290001Sglebius/* 2290001Sglebius * timespecops.h -- calculations on 'struct timespec' values 3290001Sglebius * 4290001Sglebius * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 5290001Sglebius * The contents of 'html/copyright.html' apply. 6290001Sglebius * 7290001Sglebius * Rationale 8290001Sglebius * --------- 9290001Sglebius * 10290001Sglebius * Doing basic arithmetic on a 'struct timespec' is not exceedingly 11290001Sglebius * hard, but it requires tedious and repetitive code to keep the result 12290001Sglebius * normalised. We consider a timespec normalised when the nanosecond 13290001Sglebius * fraction is in the interval [0 .. 10^9[ ; there are multiple value 14290001Sglebius * pairs of seconds and nanoseconds that denote the same time interval, 15290001Sglebius * but the normalised representation is unique. No two different 16290001Sglebius * intervals can have the same normalised representation. 17290001Sglebius * 18290001Sglebius * Another topic is the representation of negative time intervals. 19290001Sglebius * There's more than one way to this, since both the seconds and the 20290001Sglebius * nanoseconds of a timespec are signed values. IMHO, the easiest way is 21290001Sglebius * to use a complement representation where the nanoseconds are still 22290001Sglebius * normalised, no matter what the sign of the seconds value. This makes 23290001Sglebius * normalisation easier, since the sign of the integer part is 24290001Sglebius * irrelevant, and it removes several sign decision cases during the 25290001Sglebius * calculations. 26290001Sglebius * 27290001Sglebius * As long as no signed integer overflow can occur with the nanosecond 28290001Sglebius * part of the operands, all operations work as expected and produce a 29290001Sglebius * normalised result. 30290001Sglebius * 31290001Sglebius * The exception to this are functions fix a '_fast' suffix, which do no 32290001Sglebius * normalisation on input data and therefore expect the input data to be 33290001Sglebius * normalised. 34290001Sglebius * 35290001Sglebius * Input and output operands may overlap; all input is consumed before 36290001Sglebius * the output is written to. 37290001Sglebius */ 38290001Sglebius#ifndef TIMESPECOPS_H 39290001Sglebius#define TIMESPECOPS_H 40290001Sglebius 41290001Sglebius#include <sys/types.h> 42290001Sglebius#include <stdio.h> 43290001Sglebius#include <math.h> 44290001Sglebius 45290001Sglebius#include "ntp.h" 46290001Sglebius#include "timetoa.h" 47290001Sglebius 48290001Sglebius 49290001Sglebius/* nanoseconds per second */ 50290001Sglebius#define NANOSECONDS 1000000000 51290001Sglebius 52290001Sglebius/* predicate: returns TRUE if the nanoseconds are in nominal range */ 53290001Sglebius#define timespec_isnormal(x) \ 54290001Sglebius ((x)->tv_nsec >= 0 && (x)->tv_nsec < NANOSECONDS) 55290001Sglebius 56290001Sglebius/* predicate: returns TRUE if the nanoseconds are out-of-bounds */ 57290001Sglebius#define timespec_isdenormal(x) (!timespec_isnormal(x)) 58290001Sglebius 59290001Sglebius/* conversion between l_fp fractions and nanoseconds */ 60290001Sglebius#ifdef HAVE_U_INT64 61290001Sglebius# define FTOTVN(tsf) \ 62290001Sglebius ((int32) \ 63290001Sglebius (((u_int64)(tsf) * NANOSECONDS + 0x80000000) >> 32)) 64290001Sglebius# define TVNTOF(tvu) \ 65290001Sglebius ((u_int32) \ 66290001Sglebius ((((u_int64)(tvu) << 32) + NANOSECONDS / 2) / \ 67290001Sglebius NANOSECONDS)) 68290001Sglebius#else 69290001Sglebius# define NSECFRAC (FRAC / NANOSECONDS) 70290001Sglebius# define FTOTVN(tsf) \ 71290001Sglebius ((int32)((tsf) / NSECFRAC + 0.5)) 72290001Sglebius# define TVNTOF(tvu) \ 73290001Sglebius ((u_int32)((tvu) * NSECFRAC + 0.5)) 74290001Sglebius#endif 75290001Sglebius 76290001Sglebius 77290001Sglebius 78290001Sglebius/* make sure nanoseconds are in nominal range */ 79290001Sglebiusstatic inline struct timespec 80290001Sglebiusnormalize_tspec( 81290001Sglebius struct timespec x 82290001Sglebius ) 83290001Sglebius{ 84290001Sglebius#if SIZEOF_LONG > 4 85290001Sglebius long z; 86290001Sglebius 87290001Sglebius /* 88290001Sglebius * tv_nsec is of type 'long', and on a 64-bit machine using only 89290001Sglebius * loops becomes prohibitive once the upper 32 bits get 90290001Sglebius * involved. On the other hand, division by constant should be 91290001Sglebius * fast enough; so we do a division of the nanoseconds in that 92290001Sglebius * case. The floor adjustment step follows with the standard 93290001Sglebius * normalisation loops. And labs() is intentionally not used 94290001Sglebius * here: it has implementation-defined behaviour when applied 95290001Sglebius * to LONG_MIN. 96290001Sglebius */ 97290001Sglebius if (x.tv_nsec < -3l * NANOSECONDS || 98290001Sglebius x.tv_nsec > 3l * NANOSECONDS) { 99290001Sglebius z = x.tv_nsec / NANOSECONDS; 100290001Sglebius x.tv_nsec -= z * NANOSECONDS; 101290001Sglebius x.tv_sec += z; 102290001Sglebius } 103290001Sglebius#endif 104290001Sglebius /* since 10**9 is close to 2**32, we don't divide but do a 105290001Sglebius * normalisation in a loop; this takes 3 steps max, and should 106290001Sglebius * outperform a division even if the mul-by-inverse trick is 107290001Sglebius * employed. */ 108290001Sglebius if (x.tv_nsec < 0) 109290001Sglebius do { 110290001Sglebius x.tv_nsec += NANOSECONDS; 111290001Sglebius x.tv_sec--; 112290001Sglebius } while (x.tv_nsec < 0); 113290001Sglebius else if (x.tv_nsec >= NANOSECONDS) 114290001Sglebius do { 115290001Sglebius x.tv_nsec -= NANOSECONDS; 116290001Sglebius x.tv_sec++; 117290001Sglebius } while (x.tv_nsec >= NANOSECONDS); 118290001Sglebius 119290001Sglebius return x; 120290001Sglebius} 121290001Sglebius 122290001Sglebius/* x = a + b */ 123290001Sglebiusstatic inline struct timespec 124290001Sglebiusadd_tspec( 125290001Sglebius struct timespec a, 126290001Sglebius struct timespec b 127290001Sglebius ) 128290001Sglebius{ 129290001Sglebius struct timespec x; 130290001Sglebius 131290001Sglebius x = a; 132290001Sglebius x.tv_sec += b.tv_sec; 133290001Sglebius x.tv_nsec += b.tv_nsec; 134290001Sglebius 135290001Sglebius return normalize_tspec(x); 136290001Sglebius} 137290001Sglebius 138290001Sglebius/* x = a + b, b is fraction only */ 139290001Sglebiusstatic inline struct timespec 140290001Sglebiusadd_tspec_ns( 141290001Sglebius struct timespec a, 142290001Sglebius long b 143290001Sglebius ) 144290001Sglebius{ 145290001Sglebius struct timespec x; 146290001Sglebius 147290001Sglebius x = a; 148290001Sglebius x.tv_nsec += b; 149290001Sglebius 150290001Sglebius return normalize_tspec(x); 151290001Sglebius} 152290001Sglebius 153290001Sglebius/* x = a - b */ 154290001Sglebiusstatic inline struct timespec 155290001Sglebiussub_tspec( 156290001Sglebius struct timespec a, 157290001Sglebius struct timespec b 158290001Sglebius ) 159290001Sglebius{ 160290001Sglebius struct timespec x; 161290001Sglebius 162290001Sglebius x = a; 163290001Sglebius x.tv_sec -= b.tv_sec; 164290001Sglebius x.tv_nsec -= b.tv_nsec; 165290001Sglebius 166290001Sglebius return normalize_tspec(x); 167290001Sglebius} 168290001Sglebius 169290001Sglebius/* x = a - b, b is fraction only */ 170290001Sglebiusstatic inline struct timespec 171290001Sglebiussub_tspec_ns( 172290001Sglebius struct timespec a, 173290001Sglebius long b 174290001Sglebius ) 175290001Sglebius{ 176290001Sglebius struct timespec x; 177290001Sglebius 178290001Sglebius x = a; 179290001Sglebius x.tv_nsec -= b; 180290001Sglebius 181290001Sglebius return normalize_tspec(x); 182290001Sglebius} 183290001Sglebius 184290001Sglebius/* x = -a */ 185290001Sglebiusstatic inline struct timespec 186290001Sglebiusneg_tspec( 187290001Sglebius struct timespec a 188290001Sglebius ) 189290001Sglebius{ 190290001Sglebius struct timespec x; 191290001Sglebius 192290001Sglebius x.tv_sec = -a.tv_sec; 193290001Sglebius x.tv_nsec = -a.tv_nsec; 194290001Sglebius 195290001Sglebius return normalize_tspec(x); 196290001Sglebius} 197290001Sglebius 198290001Sglebius/* x = abs(a) */ 199290001Sglebiusstatic inline struct timespec 200290001Sglebiusabs_tspec( 201290001Sglebius struct timespec a 202290001Sglebius ) 203290001Sglebius{ 204290001Sglebius struct timespec c; 205290001Sglebius 206290001Sglebius c = normalize_tspec(a); 207290001Sglebius if (c.tv_sec < 0) { 208290001Sglebius if (c.tv_nsec != 0) { 209290001Sglebius c.tv_sec = -c.tv_sec - 1; 210290001Sglebius c.tv_nsec = NANOSECONDS - c.tv_nsec; 211290001Sglebius } else { 212290001Sglebius c.tv_sec = -c.tv_sec; 213290001Sglebius } 214290001Sglebius } 215290001Sglebius 216290001Sglebius return c; 217290001Sglebius} 218290001Sglebius 219290001Sglebius/* 220290001Sglebius * compare previously-normalised a and b 221290001Sglebius * return 1 / 0 / -1 if a < / == / > b 222290001Sglebius */ 223290001Sglebiusstatic inline int 224290001Sglebiuscmp_tspec( 225290001Sglebius struct timespec a, 226290001Sglebius struct timespec b 227290001Sglebius ) 228290001Sglebius{ 229290001Sglebius int r; 230290001Sglebius 231290001Sglebius r = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec); 232290001Sglebius if (0 == r) 233290001Sglebius r = (a.tv_nsec > b.tv_nsec) - 234290001Sglebius (a.tv_nsec < b.tv_nsec); 235290001Sglebius 236290001Sglebius return r; 237290001Sglebius} 238290001Sglebius 239290001Sglebius/* 240290001Sglebius * compare possibly-denormal a and b 241290001Sglebius * return 1 / 0 / -1 if a < / == / > b 242290001Sglebius */ 243290001Sglebiusstatic inline int 244290001Sglebiuscmp_tspec_denorm( 245290001Sglebius struct timespec a, 246290001Sglebius struct timespec b 247290001Sglebius ) 248290001Sglebius{ 249290001Sglebius return cmp_tspec(normalize_tspec(a), normalize_tspec(b)); 250290001Sglebius} 251290001Sglebius 252290001Sglebius/* 253290001Sglebius * test previously-normalised a 254290001Sglebius * return 1 / 0 / -1 if a < / == / > 0 255290001Sglebius */ 256290001Sglebiusstatic inline int 257290001Sglebiustest_tspec( 258290001Sglebius struct timespec a 259290001Sglebius ) 260290001Sglebius{ 261290001Sglebius int r; 262290001Sglebius 263290001Sglebius r = (a.tv_sec > 0) - (a.tv_sec < 0); 264290001Sglebius if (r == 0) 265290001Sglebius r = (a.tv_nsec > 0); 266290001Sglebius 267290001Sglebius return r; 268290001Sglebius} 269290001Sglebius 270290001Sglebius/* 271290001Sglebius * test possibly-denormal a 272290001Sglebius * return 1 / 0 / -1 if a < / == / > 0 273290001Sglebius */ 274290001Sglebiusstatic inline int 275290001Sglebiustest_tspec_denorm( 276290001Sglebius struct timespec a 277290001Sglebius ) 278290001Sglebius{ 279290001Sglebius return test_tspec(normalize_tspec(a)); 280290001Sglebius} 281290001Sglebius 282290001Sglebius/* return LIB buffer ptr to string rep */ 283290001Sglebiusstatic inline const char * 284290001Sglebiustspectoa( 285290001Sglebius struct timespec x 286290001Sglebius ) 287290001Sglebius{ 288290001Sglebius return format_time_fraction(x.tv_sec, x.tv_nsec, 9); 289290001Sglebius} 290290001Sglebius 291290001Sglebius/* 292290001Sglebius * convert to l_fp type, relative and absolute 293290001Sglebius */ 294290001Sglebius 295290001Sglebius/* convert from timespec duration to l_fp duration */ 296290001Sglebiusstatic inline l_fp 297290001Sglebiustspec_intv_to_lfp( 298290001Sglebius struct timespec x 299290001Sglebius ) 300290001Sglebius{ 301290001Sglebius struct timespec v; 302290001Sglebius l_fp y; 303290001Sglebius 304290001Sglebius v = normalize_tspec(x); 305290001Sglebius y.l_uf = TVNTOF(v.tv_nsec); 306290001Sglebius y.l_i = (int32)v.tv_sec; 307290001Sglebius 308290001Sglebius return y; 309290001Sglebius} 310290001Sglebius 311290001Sglebius/* x must be UN*X epoch, output will be in NTP epoch */ 312290001Sglebiusstatic inline l_fp 313290001Sglebiustspec_stamp_to_lfp( 314290001Sglebius struct timespec x 315290001Sglebius ) 316290001Sglebius{ 317290001Sglebius l_fp y; 318290001Sglebius 319290001Sglebius y = tspec_intv_to_lfp(x); 320290001Sglebius y.l_ui += JAN_1970; 321290001Sglebius 322290001Sglebius return y; 323290001Sglebius} 324290001Sglebius 325290001Sglebius/* convert from l_fp type, relative signed/unsigned and absolute */ 326290001Sglebiusstatic inline struct timespec 327290001Sglebiuslfp_intv_to_tspec( 328290001Sglebius l_fp x 329290001Sglebius ) 330290001Sglebius{ 331290001Sglebius struct timespec out; 332290001Sglebius l_fp absx; 333290001Sglebius int neg; 334290001Sglebius 335290001Sglebius neg = L_ISNEG(&x); 336290001Sglebius absx = x; 337290001Sglebius if (neg) { 338290001Sglebius L_NEG(&absx); 339290001Sglebius } 340290001Sglebius out.tv_nsec = FTOTVN(absx.l_uf); 341290001Sglebius out.tv_sec = absx.l_i; 342290001Sglebius if (neg) { 343290001Sglebius out.tv_sec = -out.tv_sec; 344290001Sglebius out.tv_nsec = -out.tv_nsec; 345290001Sglebius out = normalize_tspec(out); 346290001Sglebius } 347290001Sglebius 348290001Sglebius return out; 349290001Sglebius} 350290001Sglebius 351290001Sglebiusstatic inline struct timespec 352290001Sglebiuslfp_uintv_to_tspec( 353290001Sglebius l_fp x 354290001Sglebius ) 355290001Sglebius{ 356290001Sglebius struct timespec out; 357290001Sglebius 358290001Sglebius out.tv_nsec = FTOTVN(x.l_uf); 359290001Sglebius out.tv_sec = x.l_ui; 360290001Sglebius 361290001Sglebius return out; 362290001Sglebius} 363290001Sglebius 364290001Sglebius/* 365290001Sglebius * absolute (timestamp) conversion. Input is time in NTP epoch, output 366290001Sglebius * is in UN*X epoch. The NTP time stamp will be expanded around the 367290001Sglebius * pivot time *p or the current time, if p is NULL. 368290001Sglebius */ 369290001Sglebiusstatic inline struct timespec 370290001Sglebiuslfp_stamp_to_tspec( 371290001Sglebius l_fp x, 372290001Sglebius const time_t * p 373290001Sglebius ) 374290001Sglebius{ 375290001Sglebius struct timespec out; 376290001Sglebius vint64 sec; 377290001Sglebius 378290001Sglebius sec = ntpcal_ntp_to_time(x.l_ui, p); 379290001Sglebius out.tv_nsec = FTOTVN(x.l_uf); 380290001Sglebius 381290001Sglebius /* copying a vint64 to a time_t needs some care... */ 382290001Sglebius#if SIZEOF_TIME_T <= 4 383290001Sglebius out.tv_sec = (time_t)sec.d_s.lo; 384290001Sglebius#elif defined(HAVE_INT64) 385290001Sglebius out.tv_sec = (time_t)sec.q_s; 386290001Sglebius#else 387290001Sglebius out.tv_sec = ((time_t)sec.d_s.hi << 32) | sec.d_s.lo; 388290001Sglebius#endif 389290001Sglebius 390290001Sglebius return out; 391290001Sglebius} 392290001Sglebius 393290001Sglebius#endif /* TIMESPECOPS_H */ 394