xref: /netbsd-src/external/bsd/ntp/dist/include/timevalops.h (revision cdfa2a7ef92791ba9db70a584a1d904730e6fb46)
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