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