1/* $NetBSD: timevalops.h,v 1.5 2020/05/25 20:47:20 christos Exp $ */ 2 3/* 4 * timevalops.h -- calculations on 'struct timeval' values 5 * 6 * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 7 * The contents of 'html/copyright.html' apply. 8 * 9 * For a rationale look at 'timespecops.h'; we do the same here, but the 10 * normalisation keeps the microseconds in [0 .. 10^6[, of course. 11 */ 12#ifndef TIMEVALOPS_H 13#define TIMEVALOPS_H 14 15#include <sys/types.h> 16#include <stdio.h> 17 18#include "ntp.h" 19#include "timetoa.h" 20 21 22/* microseconds per second */ 23#define MICROSECONDS 1000000 24 25#ifndef HAVE_U_INT64 26# define USE_TSF_USEC_TABLES 27#endif 28 29/* 30 * Convert usec to a time stamp fraction. 31 */ 32#ifdef USE_TSF_USEC_TABLES 33extern const u_int32 ustotslo[]; 34extern const u_int32 ustotsmid[]; 35extern const u_int32 ustotshi[]; 36 37# define TVUTOTSF(tvu, tsf) \ 38 ((tsf) = ustotslo[(tvu) & 0xff] \ 39 + ustotsmid[((tvu) >> 8) & 0xff] \ 40 + ustotshi[((tvu) >> 16) & 0xf]) 41#else 42# define TVUTOTSF(tvu, tsf) \ 43 ((tsf) = (u_int32) \ 44 ((((u_int64)(tvu) << 32) + MICROSECONDS / 2) / \ 45 MICROSECONDS)) 46#endif 47 48/* 49 * Convert a time stamp fraction to microseconds. The time stamp 50 * fraction is assumed to be unsigned. 51 */ 52#ifdef USE_TSF_USEC_TABLES 53extern const u_int32 tstouslo[256]; 54extern const u_int32 tstousmid[256]; 55extern const u_int32 tstoushi[128]; 56 57/* 58 * TV_SHIFT is used to turn the table result into a usec value. To 59 * round, add in TV_ROUNDBIT before shifting. 60 */ 61#define TV_SHIFT 3 62#define TV_ROUNDBIT 0x4 63 64# define TSFTOTVU(tsf, tvu) \ 65 ((tvu) = (tstoushi[((tsf) >> 24) & 0xff] \ 66 + tstousmid[((tsf) >> 16) & 0xff] \ 67 + tstouslo[((tsf) >> 9) & 0x7f] \ 68 + TV_ROUNDBIT) >> TV_SHIFT) 69#else 70# define TSFTOTVU(tsf, tvu) \ 71 ((tvu) = (int32) \ 72 (((u_int64)(tsf) * MICROSECONDS + 0x80000000) >> 32)) 73#endif 74 75/* 76 * Convert a struct timeval to a time stamp. 77 */ 78#define TVTOTS(tv, ts) \ 79 do { \ 80 (ts)->l_ui = (u_long)(tv)->tv_sec; \ 81 TVUTOTSF((tv)->tv_usec, (ts)->l_uf); \ 82 } while (FALSE) 83 84#define sTVTOTS(tv, ts) \ 85 do { \ 86 int isneg = 0; \ 87 long usec; \ 88 (ts)->l_ui = (tv)->tv_sec; \ 89 usec = (tv)->tv_usec; \ 90 if (((tv)->tv_sec < 0) || ((tv)->tv_usec < 0)) { \ 91 usec = -usec; \ 92 (ts)->l_ui = -(ts)->l_ui; \ 93 isneg = 1; \ 94 } \ 95 TVUTOTSF(usec, (ts)->l_uf); \ 96 if (isneg) { \ 97 L_NEG((ts)); \ 98 } \ 99 } while (FALSE) 100 101/* 102 * Convert a time stamp to a struct timeval. The time stamp 103 * has to be positive. 104 */ 105#define TSTOTV(ts, tv) \ 106 do { \ 107 (tv)->tv_sec = (ts)->l_ui; \ 108 TSFTOTVU((ts)->l_uf, (tv)->tv_usec); \ 109 if ((tv)->tv_usec == 1000000) { \ 110 (tv)->tv_sec++; \ 111 (tv)->tv_usec = 0; \ 112 } \ 113 } while (FALSE) 114 115 116/* 117 * predicate: returns TRUE if the microseconds are in nominal range 118 * use like: int timeval_isnormal(const struct timeval *x) 119 */ 120#define timeval_isnormal(x) \ 121 ((x)->tv_usec >= 0 && (x)->tv_usec < MICROSECONDS) 122 123/* 124 * Convert milliseconds to a time stamp fraction. Unused except for 125 * refclock_leitch.c, so accompanying lookup tables were removed in 126 * favor of reusing the microseconds conversion tables. 127 */ 128#define MSUTOTSF(msu, tsf) TVUTOTSF((msu) * 1000, tsf) 129 130/* 131 * predicate: returns TRUE if the microseconds are out-of-bounds 132 * use like: int timeval_isdenormal(const struct timeval *x) 133 */ 134#define timeval_isdenormal(x) (!timeval_isnormal(x)) 135 136/* make sure microseconds are in nominal range */ 137static inline struct timeval 138normalize_tval( 139 struct timeval x 140 ) 141{ 142 long z; 143 144 /* 145 * If the fraction becomes excessive denormal, we use division 146 * to do first partial normalisation. The normalisation loops 147 * following will do the remaining cleanup. Since the size of 148 * tv_usec has a peculiar definition by the standard the range 149 * check is coded manually. And labs() is intentionally not used 150 * here: it has implementation-defined behaviour when applied 151 * to LONG_MIN. 152 */ 153 if (x.tv_usec < -3l * MICROSECONDS || 154 x.tv_usec > 3l * MICROSECONDS ) { 155 z = x.tv_usec / MICROSECONDS; 156 x.tv_usec -= z * MICROSECONDS; 157 x.tv_sec += z; 158 } 159 160 /* 161 * Do any remaining normalisation steps in loops. This takes 3 162 * steps max, and should outperform a division even if the 163 * mul-by-inverse trick is employed. (It also does the floor 164 * division adjustment if the above division was executed.) 165 */ 166 if (x.tv_usec < 0) 167 do { 168 x.tv_usec += MICROSECONDS; 169 x.tv_sec--; 170 } while (x.tv_usec < 0); 171 else if (x.tv_usec >= MICROSECONDS) 172 do { 173 x.tv_usec -= MICROSECONDS; 174 x.tv_sec++; 175 } while (x.tv_usec >= MICROSECONDS); 176 177 return x; 178} 179 180/* x = a + b */ 181static inline struct timeval 182add_tval( 183 struct timeval a, 184 struct timeval b 185 ) 186{ 187 struct timeval x; 188 189 x = a; 190 x.tv_sec += b.tv_sec; 191 x.tv_usec += b.tv_usec; 192 193 return normalize_tval(x); 194} 195 196/* x = a + b, b is fraction only */ 197static inline struct timeval 198add_tval_us( 199 struct timeval a, 200 long b 201 ) 202{ 203 struct timeval x; 204 205 x = a; 206 x.tv_usec += b; 207 208 return normalize_tval(x); 209} 210 211/* x = a - b */ 212static inline struct timeval 213sub_tval( 214 struct timeval a, 215 struct timeval b 216 ) 217{ 218 struct timeval x; 219 220 x = a; 221 x.tv_sec -= b.tv_sec; 222 x.tv_usec -= b.tv_usec; 223 224 return normalize_tval(x); 225} 226 227/* x = a - b, b is fraction only */ 228static inline struct timeval 229sub_tval_us( 230 struct timeval a, 231 long b 232 ) 233{ 234 struct timeval x; 235 236 x = a; 237 x.tv_usec -= b; 238 239 return normalize_tval(x); 240} 241 242/* x = -a */ 243static inline struct timeval 244neg_tval( 245 struct timeval a 246 ) 247{ 248 struct timeval x; 249 250 x.tv_sec = -a.tv_sec; 251 x.tv_usec = -a.tv_usec; 252 253 return normalize_tval(x); 254} 255 256/* x = abs(a) */ 257static inline struct timeval 258abs_tval( 259 struct timeval a 260 ) 261{ 262 struct timeval c; 263 264 c = normalize_tval(a); 265 if (c.tv_sec < 0) { 266 if (c.tv_usec != 0) { 267 c.tv_sec = -c.tv_sec - 1; 268 c.tv_usec = MICROSECONDS - c.tv_usec; 269 } else { 270 c.tv_sec = -c.tv_sec; 271 } 272 } 273 274 return c; 275} 276 277/* 278 * compare previously-normalised a and b 279 * return 1 / 0 / -1 if a < / == / > b 280 */ 281static inline int 282cmp_tval( 283 struct timeval a, 284 struct timeval b 285 ) 286{ 287 int r; 288 289 r = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec); 290 if (0 == r) 291 r = (a.tv_usec > b.tv_usec) - 292 (a.tv_usec < b.tv_usec); 293 294 return r; 295} 296 297/* 298 * compare possibly-denormal a and b 299 * return 1 / 0 / -1 if a < / == / > b 300 */ 301static inline int 302cmp_tval_denorm( 303 struct timeval a, 304 struct timeval b 305 ) 306{ 307 return cmp_tval(normalize_tval(a), normalize_tval(b)); 308} 309 310/* 311 * test previously-normalised a 312 * return 1 / 0 / -1 if a < / == / > 0 313 */ 314static inline int 315test_tval( 316 struct timeval a 317 ) 318{ 319 int r; 320 321 r = (a.tv_sec > 0) - (a.tv_sec < 0); 322 if (r == 0) 323 r = (a.tv_usec > 0); 324 325 return r; 326} 327 328/* 329 * test possibly-denormal a 330 * return 1 / 0 / -1 if a < / == / > 0 331 */ 332static inline int 333test_tval_denorm( 334 struct timeval a 335 ) 336{ 337 return test_tval(normalize_tval(a)); 338} 339 340/* return LIB buffer ptr to string rep */ 341static inline const char * 342tvaltoa( 343 struct timeval x 344 ) 345{ 346 return format_time_fraction(x.tv_sec, x.tv_usec, 6); 347} 348 349/* convert from timeval duration to l_fp duration */ 350static inline l_fp 351tval_intv_to_lfp( 352 struct timeval x 353 ) 354{ 355 struct timeval v; 356 l_fp y; 357 358 v = normalize_tval(x); 359 TVUTOTSF(v.tv_usec, y.l_uf); 360 y.l_i = (int32)v.tv_sec; 361 362 return y; 363} 364 365/* x must be UN*X epoch, output *y will be in NTP epoch */ 366static inline l_fp 367tval_stamp_to_lfp( 368 struct timeval x 369 ) 370{ 371 l_fp y; 372 373 y = tval_intv_to_lfp(x); 374 y.l_ui += JAN_1970; 375 376 return y; 377} 378 379/* convert to l_fp type, relative signed/unsigned and absolute */ 380static inline struct timeval 381lfp_intv_to_tval( 382 l_fp x 383 ) 384{ 385 struct timeval out; 386 l_fp absx; 387 int neg; 388 389 neg = L_ISNEG(&x); 390 absx = x; 391 if (neg) { 392 L_NEG(&absx); 393 } 394 TSFTOTVU(absx.l_uf, out.tv_usec); 395 out.tv_sec = absx.l_i; 396 if (neg) { 397 out.tv_sec = -out.tv_sec; 398 out.tv_usec = -out.tv_usec; 399 out = normalize_tval(out); 400 } 401 402 return out; 403} 404 405static inline struct timeval 406lfp_uintv_to_tval( 407 l_fp x 408 ) 409{ 410 struct timeval out; 411 412 TSFTOTVU(x.l_uf, out.tv_usec); 413 out.tv_sec = x.l_ui; 414 415 return out; 416} 417 418/* 419 * absolute (timestamp) conversion. Input is time in NTP epoch, output 420 * is in UN*X epoch. The NTP time stamp will be expanded around the 421 * pivot time *p or the current time, if p is NULL. 422 */ 423static inline struct timeval 424lfp_stamp_to_tval( 425 l_fp x, 426 const time_t * p 427 ) 428{ 429 struct timeval out; 430 vint64 sec; 431 432 sec = ntpcal_ntp_to_time(x.l_ui, p); 433 TSFTOTVU(x.l_uf, out.tv_usec); 434 435 /* copying a vint64 to a time_t needs some care... */ 436#if SIZEOF_TIME_T <= 4 437 out.tv_sec = (time_t)sec.d_s.lo; 438#elif defined(HAVE_INT64) 439 out.tv_sec = (time_t)sec.q_s; 440#else 441 out.tv_sec = ((time_t)sec.d_s.hi << 32) | sec.d_s.lo; 442#endif 443 out = normalize_tval(out); 444 445 return out; 446} 447 448#endif /* TIMEVALOPS_H */ 449