timespecops.h revision 290000
1130561Sobrien/* 2130561Sobrien * timespecops.h -- calculations on 'struct timespec' values 3130561Sobrien * 4130561Sobrien * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 5130561Sobrien * The contents of 'html/copyright.html' apply. 6130561Sobrien * 7130561Sobrien * Rationale 8130561Sobrien * --------- 9130561Sobrien * 10130561Sobrien * Doing basic arithmetic on a 'struct timespec' is not exceedingly 11130561Sobrien * hard, but it requires tedious and repetitive code to keep the result 12130561Sobrien * normalised. We consider a timespec normalised when the nanosecond 13130561Sobrien * fraction is in the interval [0 .. 10^9[ ; there are multiple value 14130561Sobrien * pairs of seconds and nanoseconds that denote the same time interval, 15130561Sobrien * but the normalised representation is unique. No two different 16130561Sobrien * intervals can have the same normalised representation. 17130561Sobrien * 18130561Sobrien * Another topic is the representation of negative time intervals. 19130561Sobrien * There's more than one way to this, since both the seconds and the 20130561Sobrien * nanoseconds of a timespec are signed values. IMHO, the easiest way is 21130561Sobrien * to use a complement representation where the nanoseconds are still 22130561Sobrien * normalised, no matter what the sign of the seconds value. This makes 23130561Sobrien * normalisation easier, since the sign of the integer part is 24130561Sobrien * irrelevant, and it removes several sign decision cases during the 25130561Sobrien * calculations. 26130561Sobrien * 27130561Sobrien * As long as no signed integer overflow can occur with the nanosecond 28130561Sobrien * part of the operands, all operations work as expected and produce a 29130561Sobrien * normalised result. 30130561Sobrien * 31130561Sobrien * The exception to this are functions fix a '_fast' suffix, which do no 32130561Sobrien * normalisation on input data and therefore expect the input data to be 33130561Sobrien * normalised. 34130561Sobrien * 35130561Sobrien * Input and output operands may overlap; all input is consumed before 36130561Sobrien * the output is written to. 37130561Sobrien */ 38130561Sobrien#ifndef TIMESPECOPS_H 39130561Sobrien#define TIMESPECOPS_H 40130561Sobrien 41130561Sobrien#include <sys/types.h> 42130561Sobrien#include <stdio.h> 43130561Sobrien#include <math.h> 44130561Sobrien 45130561Sobrien#include "ntp.h" 46130561Sobrien#include "timetoa.h" 47130561Sobrien 48130561Sobrien 49130561Sobrien/* nanoseconds per second */ 50130561Sobrien#define NANOSECONDS 1000000000 51130561Sobrien 52130561Sobrien/* predicate: returns TRUE if the nanoseconds are in nominal range */ 53130561Sobrien#define timespec_isnormal(x) \ 54130561Sobrien ((x)->tv_nsec >= 0 && (x)->tv_nsec < NANOSECONDS) 55130561Sobrien 56130561Sobrien/* predicate: returns TRUE if the nanoseconds are out-of-bounds */ 57130561Sobrien#define timespec_isdenormal(x) (!timespec_isnormal(x)) 58130561Sobrien 59130561Sobrien/* conversion between l_fp fractions and nanoseconds */ 60130561Sobrien#ifdef HAVE_U_INT64 61130561Sobrien# define FTOTVN(tsf) \ 62130561Sobrien ((int32) \ 63130561Sobrien (((u_int64)(tsf) * NANOSECONDS + 0x80000000) >> 32)) 64130561Sobrien# define TVNTOF(tvu) \ 65130561Sobrien ((u_int32) \ 66130561Sobrien ((((u_int64)(tvu) << 32) + NANOSECONDS / 2) / \ 67130561Sobrien NANOSECONDS)) 68130561Sobrien#else 69130561Sobrien# define NSECFRAC (FRAC / NANOSECONDS) 70130561Sobrien# define FTOTVN(tsf) \ 71130561Sobrien ((int32)((tsf) / NSECFRAC + 0.5)) 72130561Sobrien# define TVNTOF(tvu) \ 73130561Sobrien ((u_int32)((tvu) * NSECFRAC + 0.5)) 74130561Sobrien#endif 75130561Sobrien 76130561Sobrien 77130561Sobrien 78130561Sobrien/* make sure nanoseconds are in nominal range */ 79130561Sobrienstatic inline struct timespec 80130561Sobriennormalize_tspec( 81130561Sobrien struct timespec x 82130561Sobrien ) 83130561Sobrien{ 84130561Sobrien#if SIZEOF_LONG > 4 85130561Sobrien long z; 86130561Sobrien 87130561Sobrien /* 88130561Sobrien * tv_nsec is of type 'long', and on a 64-bit machine using only 89130561Sobrien * loops becomes prohibitive once the upper 32 bits get 90130561Sobrien * involved. On the other hand, division by constant should be 91130561Sobrien * fast enough; so we do a division of the nanoseconds in that 92130561Sobrien * case. The floor adjustment step follows with the standard 93130561Sobrien * normalisation loops. And labs() is intentionally not used 94130561Sobrien * here: it has implementation-defined behaviour when applied 95130561Sobrien * to LONG_MIN. 96130561Sobrien */ 97130561Sobrien if (x.tv_nsec < -3l * NANOSECONDS || 98130561Sobrien x.tv_nsec > 3l * NANOSECONDS) { 99130561Sobrien z = x.tv_nsec / NANOSECONDS; 100130561Sobrien x.tv_nsec -= z * NANOSECONDS; 101130561Sobrien x.tv_sec += z; 102130561Sobrien } 103130561Sobrien#endif 104130561Sobrien /* since 10**9 is close to 2**32, we don't divide but do a 105130561Sobrien * normalisation in a loop; this takes 3 steps max, and should 106130561Sobrien * outperform a division even if the mul-by-inverse trick is 107130561Sobrien * employed. */ 108130561Sobrien if (x.tv_nsec < 0) 109130561Sobrien do { 110130561Sobrien x.tv_nsec += NANOSECONDS; 111130561Sobrien x.tv_sec--; 112130561Sobrien } while (x.tv_nsec < 0); 113130561Sobrien else if (x.tv_nsec >= NANOSECONDS) 114130561Sobrien do { 115130561Sobrien x.tv_nsec -= NANOSECONDS; 116130561Sobrien x.tv_sec++; 117130561Sobrien } while (x.tv_nsec >= NANOSECONDS); 118130561Sobrien 119130561Sobrien return x; 120130561Sobrien} 121130561Sobrien 122130561Sobrien/* x = a + b */ 123130561Sobrienstatic inline struct timespec 124130561Sobrienadd_tspec( 125130561Sobrien struct timespec a, 126130561Sobrien struct timespec b 127130561Sobrien ) 128130561Sobrien{ 129130561Sobrien struct timespec x; 130130561Sobrien 131130561Sobrien x = a; 132130561Sobrien x.tv_sec += b.tv_sec; 133130561Sobrien x.tv_nsec += b.tv_nsec; 134130561Sobrien 135130561Sobrien return normalize_tspec(x); 136130561Sobrien} 137130561Sobrien 138130561Sobrien/* x = a + b, b is fraction only */ 139130561Sobrienstatic inline struct timespec 140130561Sobrienadd_tspec_ns( 141130561Sobrien struct timespec a, 142130561Sobrien long b 143130561Sobrien ) 144130561Sobrien{ 145130561Sobrien struct timespec x; 146130561Sobrien 147130561Sobrien x = a; 148130561Sobrien x.tv_nsec += b; 149130561Sobrien 150130561Sobrien return normalize_tspec(x); 151130561Sobrien} 152130561Sobrien 153130561Sobrien/* x = a - b */ 154130561Sobrienstatic inline struct timespec 155130561Sobriensub_tspec( 156130561Sobrien struct timespec a, 157130561Sobrien struct timespec b 158130561Sobrien ) 159130561Sobrien{ 160130561Sobrien struct timespec x; 161130561Sobrien 162130561Sobrien x = a; 163130561Sobrien x.tv_sec -= b.tv_sec; 164130561Sobrien x.tv_nsec -= b.tv_nsec; 165130561Sobrien 166130561Sobrien return normalize_tspec(x); 167130561Sobrien} 168130561Sobrien 169130561Sobrien/* x = a - b, b is fraction only */ 170130561Sobrienstatic inline struct timespec 171130561Sobriensub_tspec_ns( 172130561Sobrien struct timespec a, 173130561Sobrien long b 174130561Sobrien ) 175130561Sobrien{ 176130561Sobrien struct timespec x; 177130561Sobrien 178130561Sobrien x = a; 179130561Sobrien x.tv_nsec -= b; 180130561Sobrien 181130561Sobrien return normalize_tspec(x); 182130561Sobrien} 183130561Sobrien 184130561Sobrien/* x = -a */ 185130561Sobrienstatic inline struct timespec 186130561Sobrienneg_tspec( 187130561Sobrien struct timespec a 188130561Sobrien ) 189130561Sobrien{ 190130561Sobrien struct timespec x; 191130561Sobrien 192130561Sobrien x.tv_sec = -a.tv_sec; 193130561Sobrien x.tv_nsec = -a.tv_nsec; 194130561Sobrien 195130561Sobrien return normalize_tspec(x); 196130561Sobrien} 197130561Sobrien 198130561Sobrien/* x = abs(a) */ 199130561Sobrienstatic inline struct timespec 200130561Sobrienabs_tspec( 201130561Sobrien struct timespec a 202130561Sobrien ) 203130561Sobrien{ 204130561Sobrien struct timespec c; 205130561Sobrien 206130561Sobrien c = normalize_tspec(a); 207130561Sobrien if (c.tv_sec < 0) { 208130561Sobrien if (c.tv_nsec != 0) { 209130561Sobrien c.tv_sec = -c.tv_sec - 1; 210130561Sobrien c.tv_nsec = NANOSECONDS - c.tv_nsec; 211130561Sobrien } else { 212130561Sobrien c.tv_sec = -c.tv_sec; 213130561Sobrien } 214130561Sobrien } 215130561Sobrien 216130561Sobrien return c; 217130561Sobrien} 218130561Sobrien 219130561Sobrien/* 220130561Sobrien * compare previously-normalised a and b 221130561Sobrien * return 1 / 0 / -1 if a < / == / > b 222130561Sobrien */ 223130561Sobrienstatic inline int 224130561Sobriencmp_tspec( 225130561Sobrien struct timespec a, 226130561Sobrien struct timespec b 227130561Sobrien ) 228130561Sobrien{ 229130561Sobrien int r; 230130561Sobrien 231130561Sobrien r = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec); 232130561Sobrien if (0 == r) 233130561Sobrien r = (a.tv_nsec > b.tv_nsec) - 234130561Sobrien (a.tv_nsec < b.tv_nsec); 235130561Sobrien 236130561Sobrien return r; 237130561Sobrien} 238130561Sobrien 239130561Sobrien/* 240130561Sobrien * compare possibly-denormal a and b 241130561Sobrien * return 1 / 0 / -1 if a < / == / > b 242130561Sobrien */ 243130561Sobrienstatic inline int 244130561Sobriencmp_tspec_denorm( 245130561Sobrien struct timespec a, 246130561Sobrien struct timespec b 247130561Sobrien ) 248130561Sobrien{ 249130561Sobrien return cmp_tspec(normalize_tspec(a), normalize_tspec(b)); 250130561Sobrien} 251130561Sobrien 252130561Sobrien/* 253130561Sobrien * test previously-normalised a 254130561Sobrien * return 1 / 0 / -1 if a < / == / > 0 255130561Sobrien */ 256130561Sobrienstatic inline int 257130561Sobrientest_tspec( 258130561Sobrien struct timespec a 259130561Sobrien ) 260130561Sobrien{ 261130561Sobrien int r; 262130561Sobrien 263130561Sobrien r = (a.tv_sec > 0) - (a.tv_sec < 0); 264130561Sobrien if (r == 0) 265130561Sobrien r = (a.tv_nsec > 0); 266130561Sobrien 267130561Sobrien return r; 268130561Sobrien} 269130561Sobrien 270130561Sobrien/* 271130561Sobrien * test possibly-denormal a 272130561Sobrien * return 1 / 0 / -1 if a < / == / > 0 273130561Sobrien */ 274130561Sobrienstatic inline int 275130561Sobrientest_tspec_denorm( 276130561Sobrien struct timespec a 277130561Sobrien ) 278130561Sobrien{ 279130561Sobrien return test_tspec(normalize_tspec(a)); 280130561Sobrien} 281130561Sobrien 282130561Sobrien/* return LIB buffer ptr to string rep */ 283130561Sobrienstatic inline const char * 284130561Sobrientspectoa( 285130561Sobrien struct timespec x 286130561Sobrien ) 287130561Sobrien{ 288130561Sobrien return format_time_fraction(x.tv_sec, x.tv_nsec, 9); 289130561Sobrien} 290130561Sobrien 291130561Sobrien/* 292130561Sobrien * convert to l_fp type, relative and absolute 293130561Sobrien */ 294130561Sobrien 295130561Sobrien/* convert from timespec duration to l_fp duration */ 296130561Sobrienstatic inline l_fp 297130561Sobrientspec_intv_to_lfp( 298130561Sobrien struct timespec x 299130561Sobrien ) 300130561Sobrien{ 301130561Sobrien struct timespec v; 302130561Sobrien l_fp y; 303130561Sobrien 304130561Sobrien v = normalize_tspec(x); 305130561Sobrien y.l_uf = TVNTOF(v.tv_nsec); 306130561Sobrien y.l_i = (int32)v.tv_sec; 307130561Sobrien 308130561Sobrien return y; 309130561Sobrien} 310130561Sobrien 311130561Sobrien/* x must be UN*X epoch, output will be in NTP epoch */ 312130561Sobrienstatic inline l_fp 313130561Sobrientspec_stamp_to_lfp( 314130561Sobrien struct timespec x 315130561Sobrien ) 316130561Sobrien{ 317130561Sobrien l_fp y; 318130561Sobrien 319130561Sobrien y = tspec_intv_to_lfp(x); 320130561Sobrien y.l_ui += JAN_1970; 321130561Sobrien 322130561Sobrien return y; 323130561Sobrien} 324130561Sobrien 325130561Sobrien/* convert from l_fp type, relative signed/unsigned and absolute */ 326130561Sobrienstatic inline struct timespec 327130561Sobrienlfp_intv_to_tspec( 328130561Sobrien l_fp x 329130561Sobrien ) 330130561Sobrien{ 331130561Sobrien struct timespec out; 332130561Sobrien l_fp absx; 333130561Sobrien int neg; 334130561Sobrien 335130561Sobrien neg = L_ISNEG(&x); 336130561Sobrien absx = x; 337130561Sobrien if (neg) { 338130561Sobrien L_NEG(&absx); 339130561Sobrien } 340130561Sobrien out.tv_nsec = FTOTVN(absx.l_uf); 341130561Sobrien out.tv_sec = absx.l_i; 342130561Sobrien if (neg) { 343130561Sobrien out.tv_sec = -out.tv_sec; 344130561Sobrien out.tv_nsec = -out.tv_nsec; 345130561Sobrien out = normalize_tspec(out); 346130561Sobrien } 347130561Sobrien 348130561Sobrien return out; 349130561Sobrien} 350130561Sobrien 351130561Sobrienstatic inline struct timespec 352130561Sobrienlfp_uintv_to_tspec( 353130561Sobrien l_fp x 354130561Sobrien ) 355130561Sobrien{ 356130561Sobrien struct timespec out; 357130561Sobrien 358130561Sobrien out.tv_nsec = FTOTVN(x.l_uf); 359130561Sobrien out.tv_sec = x.l_ui; 360130561Sobrien 361130561Sobrien return out; 362130561Sobrien} 363130561Sobrien 364130561Sobrien/* 365130561Sobrien * absolute (timestamp) conversion. Input is time in NTP epoch, output 366130561Sobrien * is in UN*X epoch. The NTP time stamp will be expanded around the 367130561Sobrien * pivot time *p or the current time, if p is NULL. 368130561Sobrien */ 369130561Sobrienstatic inline struct timespec 370130561Sobrienlfp_stamp_to_tspec( 371130561Sobrien l_fp x, 372130561Sobrien const time_t * p 373130561Sobrien ) 374130561Sobrien{ 375130561Sobrien struct timespec out; 376130561Sobrien vint64 sec; 377130561Sobrien 378130561Sobrien sec = ntpcal_ntp_to_time(x.l_ui, p); 379130561Sobrien out.tv_nsec = FTOTVN(x.l_uf); 380130561Sobrien 381130561Sobrien /* copying a vint64 to a time_t needs some care... */ 382130561Sobrien#if SIZEOF_TIME_T <= 4 383130561Sobrien out.tv_sec = (time_t)sec.d_s.lo; 384130561Sobrien#elif defined(HAVE_INT64) 385130561Sobrien out.tv_sec = (time_t)sec.q_s; 386130561Sobrien#else 387130561Sobrien out.tv_sec = ((time_t)sec.d_s.hi << 32) | sec.d_s.lo; 388130561Sobrien#endif 389130561Sobrien 390130561Sobrien return out; 391130561Sobrien} 392130561Sobrien 393130561Sobrien#endif /* TIMESPECOPS_H */ 394130561Sobrien