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
normalize_tval(struct timeval x)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
add_tval(struct timeval a,struct timeval b)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
add_tval_us(struct timeval a,long b)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
sub_tval(struct timeval a,struct timeval b)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
sub_tval_us(struct timeval a,long b)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
neg_tval(struct timeval a)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
abs_tval(struct timeval a)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
cmp_tval(struct timeval a,struct timeval b)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
cmp_tval_denorm(struct timeval a,struct timeval b)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
test_tval(struct timeval a)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
test_tval_denorm(struct timeval a)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 *
tvaltoa(struct timeval x)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
tval_intv_to_lfp(struct timeval x)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
tval_stamp_to_lfp(struct timeval x)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
lfp_intv_to_tval(l_fp x)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
lfp_uintv_to_tval(l_fp x)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
lfp_stamp_to_tval(l_fp x,const time_t * p)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