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 33 extern const u_int32 ustotslo[]; 34 extern const u_int32 ustotsmid[]; 35 extern 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 53 extern const u_int32 tstouslo[256]; 54 extern const u_int32 tstousmid[256]; 55 extern 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 */ 137 static inline struct timeval 138 normalize_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 */ 181 static inline struct timeval 182 add_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 */ 197 static inline struct timeval 198 add_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 */ 212 static inline struct timeval 213 sub_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 */ 228 static inline struct timeval 229 sub_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 */ 243 static inline struct timeval 244 neg_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) */ 257 static inline struct timeval 258 abs_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 */ 281 static inline int 282 cmp_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 */ 301 static inline int 302 cmp_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 */ 314 static inline int 315 test_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 */ 332 static inline int 333 test_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 */ 341 static inline const char * 342 tvaltoa( 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 */ 350 static inline l_fp 351 tval_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 */ 366 static inline l_fp 367 tval_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 */ 380 static inline struct timeval 381 lfp_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 405 static inline struct timeval 406 lfp_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 */ 423 static inline struct timeval 424 lfp_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