timevalops.h revision 290001
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
31extern const u_int32 ustotslo[];
32extern const u_int32 ustotsmid[];
33extern 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
51extern const u_int32 tstouslo[256];
52extern const u_int32 tstousmid[256];
53extern 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 */
135static inline struct timeval
136normalize_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 */
179static inline struct timeval
180add_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 */
195static inline struct timeval
196add_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 */
210static inline struct timeval
211sub_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 */
226static inline struct timeval
227sub_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 */
241static inline struct timeval
242neg_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) */
255static inline struct timeval
256abs_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 */
279static inline int
280cmp_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 */
299static inline int
300cmp_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 */
312static inline int
313test_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 */
330static inline int
331test_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 */
339static inline const char *
340tvaltoa(
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 */
348static inline l_fp
349tval_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 */
364static inline l_fp
365tval_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 */
378static inline struct timeval
379lfp_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
403static inline struct timeval
404lfp_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 */
421static inline struct timeval
422lfp_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