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