summaryrefslogtreecommitdiff
path: root/include/timevalops.h
blob: e873b8b8006eadd89ac5224474e0a86479b6e3a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
/*
 * timevalops.h -- calculations on 'struct timeval' values
 *
 * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
 * The contents of 'html/copyright.html' apply.
 *
 * For a rationale look at 'timespecops.h'; we do the same here, but the
 * normalisation keeps the microseconds in [0 .. 10^6[, of course.
 */
#ifndef TIMEVALOPS_H
#define TIMEVALOPS_H

#include <sys/types.h>
#include <stdio.h>

#include "ntp.h"
#include "timetoa.h"


/* microseconds per second */
#define MICROSECONDS 1000000

#ifndef HAVE_U_INT64
# define USE_TSF_USEC_TABLES
#endif

/*
 * Convert usec to a time stamp fraction.
 */
#ifdef USE_TSF_USEC_TABLES
extern const u_int32 ustotslo[];
extern const u_int32 ustotsmid[];
extern const u_int32 ustotshi[];

# define TVUTOTSF(tvu, tsf)						\
	 ((tsf) = ustotslo[(tvu) & 0xff]				\
		  + ustotsmid[((tvu) >> 8) & 0xff]			\
		  + ustotshi[((tvu) >> 16) & 0xf])
#else
# define TVUTOTSF(tvu, tsf)						\
	((tsf) = (u_int32)						\
		 ((((u_int64)(tvu) << 32) + MICROSECONDS / 2) /		\
		  MICROSECONDS))
#endif

/*
 * Convert a time stamp fraction to microseconds.  The time stamp
 * fraction is assumed to be unsigned.
 */
#ifdef USE_TSF_USEC_TABLES
extern const u_int32 tstouslo[256];
extern const u_int32 tstousmid[256];
extern const u_int32 tstoushi[128];

/*
 * TV_SHIFT is used to turn the table result into a usec value.  To
 * round, add in TV_ROUNDBIT before shifting.
 */
#define	TV_SHIFT	3
#define	TV_ROUNDBIT	0x4

# define TSFTOTVU(tsf, tvu)						\
	 ((tvu) = (tstoushi[((tsf) >> 24) & 0xff]			\
		  + tstousmid[((tsf) >> 16) & 0xff]			\
		  + tstouslo[((tsf) >> 9) & 0x7f]			\
		  + TV_ROUNDBIT) >> TV_SHIFT)
#else
# define TSFTOTVU(tsf, tvu)						\
	 ((tvu) = (int32)						\
		  (((u_int64)(tsf) * MICROSECONDS + 0x80000000) >> 32))
#endif

/*
 * Convert a struct timeval to a time stamp.
 */
#define TVTOTS(tv, ts) \
	do { \
		(ts)->l_ui = (u_long)(tv)->tv_sec; \
		TVUTOTSF((tv)->tv_usec, (ts)->l_uf); \
	} while (FALSE)

#define sTVTOTS(tv, ts) \
	do { \
		int isneg = 0; \
		long usec; \
		(ts)->l_ui = (tv)->tv_sec; \
		usec = (tv)->tv_usec; \
		if (((tv)->tv_sec < 0) || ((tv)->tv_usec < 0)) { \
			usec = -usec; \
			(ts)->l_ui = -(ts)->l_ui; \
			isneg = 1; \
		} \
		TVUTOTSF(usec, (ts)->l_uf); \
		if (isneg) { \
			L_NEG((ts)); \
		} \
	} while (FALSE)

/*
 * Convert a time stamp to a struct timeval.  The time stamp
 * has to be positive.
 */
#define	TSTOTV(ts, tv) \
	do { \
		(tv)->tv_sec = (ts)->l_ui; \
		TSFTOTVU((ts)->l_uf, (tv)->tv_usec); \
		if ((tv)->tv_usec == 1000000) { \
			(tv)->tv_sec++; \
			(tv)->tv_usec = 0; \
		} \
	} while (FALSE)


/*
 * predicate: returns TRUE if the microseconds are in nominal range
 * use like: int timeval_isnormal(const struct timeval *x)
 */
#define timeval_isnormal(x) \
	((x)->tv_usec >= 0 && (x)->tv_usec < MICROSECONDS)

/*
 * Convert milliseconds to a time stamp fraction.  Unused except for
 * refclock_leitch.c, so accompanying lookup tables were removed in
 * favor of reusing the microseconds conversion tables.
 */
#define	MSUTOTSF(msu, tsf)	TVUTOTSF((msu) * 1000, tsf)

/*
 * predicate: returns TRUE if the microseconds are out-of-bounds
 * use like: int timeval_isdenormal(const struct timeval *x)
 */
#define timeval_isdenormal(x)	(!timeval_isnormal(x))

/* make sure microseconds are in nominal range */
static inline struct timeval
normalize_tval(
	struct timeval	x
	)
{
	long		z;

	/*
	 * If the fraction becomes excessive denormal, we use division
	 * to do first partial normalisation. The normalisation loops
	 * following will do the remaining cleanup. Since the size of
	 * tv_usec has a peculiar definition by the standard the range
	 * check is coded manually. And labs() is intentionally not used
	 * here: it has implementation-defined behaviour when applied
	 * to LONG_MIN.
	 */
	if (x.tv_usec < -3l * MICROSECONDS ||
	    x.tv_usec >  3l * MICROSECONDS  ) {
		z = x.tv_usec / MICROSECONDS;
		x.tv_usec -= z * MICROSECONDS;
		x.tv_sec += z;
	}

	/*
	 * Do any remaining normalisation steps in loops. This takes 3
	 * steps max, and should outperform a division even if the
	 * mul-by-inverse trick is employed. (It also does the floor
	 * division adjustment if the above division was executed.)
	 */
	if (x.tv_usec < 0)
		do {
			x.tv_usec += MICROSECONDS;
			x.tv_sec--;
		} while (x.tv_usec < 0);
	else if (x.tv_usec >= MICROSECONDS)
		do {
			x.tv_usec -= MICROSECONDS;
			x.tv_sec++;
		} while (x.tv_usec >= MICROSECONDS);

	return x;
}

/* x = a + b */
static inline struct timeval
add_tval(
	struct timeval	a,
	struct timeval	b
	)
{
	struct timeval	x;

	x = a;
	x.tv_sec += b.tv_sec;
	x.tv_usec += b.tv_usec;

	return normalize_tval(x);
}

/* x = a + b, b is fraction only */
static inline struct timeval
add_tval_us(
	struct timeval	a,
	long		b
	)
{
	struct timeval x;

	x = a;
	x.tv_usec += b;

	return normalize_tval(x);
}

/* x = a - b */
static inline struct timeval
sub_tval(
	struct timeval	a,
	struct timeval	b
	)
{	
	struct timeval	x;

	x = a;
	x.tv_sec -= b.tv_sec;
	x.tv_usec -= b.tv_usec;

	return normalize_tval(x);
}

/* x = a - b, b is fraction only */
static inline struct timeval
sub_tval_us(
	struct timeval	a,
	long		b
	)
{
	struct timeval x;

	x = a;
	x.tv_usec -= b;

	return normalize_tval(x);
}

/* x = -a */
static inline struct timeval
neg_tval(
	struct timeval	a
	)
{	
	struct timeval	x;

	x.tv_sec = -a.tv_sec;
	x.tv_usec = -a.tv_usec;

	return normalize_tval(x);
}

/* x = abs(a) */
static inline struct timeval
abs_tval(
	struct timeval	a
	)
{
	struct timeval	c;

	c = normalize_tval(a);
	if (c.tv_sec < 0) {
		if (c.tv_usec != 0) {
			c.tv_sec = -c.tv_sec - 1;
			c.tv_usec = MICROSECONDS - c.tv_usec;
		} else {
			c.tv_sec = -c.tv_sec;
		}
	}

	return c;
}

/*
 * compare previously-normalised a and b
 * return 1 / 0 / -1 if a < / == / > b
 */
static inline int
cmp_tval(
	struct timeval a,
	struct timeval b
	)
{
	int r;

	r = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec);
	if (0 == r)
		r = (a.tv_usec > b.tv_usec) -
		    (a.tv_usec < b.tv_usec);
	
	return r;
}

/*
 * compare possibly-denormal a and b
 * return 1 / 0 / -1 if a < / == / > b
 */
static inline int
cmp_tval_denorm(
	struct timeval	a,
	struct timeval	b
	)
{
	return cmp_tval(normalize_tval(a), normalize_tval(b));
}

/*
 * test previously-normalised a
 * return 1 / 0 / -1 if a < / == / > 0
 */
static inline int
test_tval(
	struct timeval	a
	)
{
	int		r;

	r = (a.tv_sec > 0) - (a.tv_sec < 0);
	if (r == 0)
		r = (a.tv_usec > 0);
	
	return r;
}

/*
 * test possibly-denormal a
 * return 1 / 0 / -1 if a < / == / > 0
 */
static inline int
test_tval_denorm(
	struct timeval	a
	)
{
	return test_tval(normalize_tval(a));
}

/* return LIB buffer ptr to string rep */
static inline const char *
tvaltoa(
	struct timeval	x
	)
{
	return format_time_fraction(x.tv_sec, x.tv_usec, 6);
}

/* convert from timeval duration to l_fp duration */
static inline l_fp
tval_intv_to_lfp(
	struct timeval	x
	)
{
	struct timeval	v;
	l_fp		y;
	
	v = normalize_tval(x);
	TVUTOTSF(v.tv_usec, y.l_uf);
	y.l_i = (int32)v.tv_sec;

	return y;
}

/* x must be UN*X epoch, output *y will be in NTP epoch */
static inline l_fp
tval_stamp_to_lfp(
	struct timeval	x
	)
{
	l_fp		y;

	y = tval_intv_to_lfp(x);
	y.l_ui += JAN_1970;

	return y;
}

/* convert to l_fp type, relative signed/unsigned and absolute */
static inline struct timeval
lfp_intv_to_tval(
	l_fp		x
	)
{
	struct timeval	out;
	l_fp		absx;
	int		neg;
	
	neg = L_ISNEG(&x);
	absx = x;
	if (neg) {
		L_NEG(&absx);	
	}
	TSFTOTVU(absx.l_uf, out.tv_usec);
	out.tv_sec = absx.l_i;
	if (neg) {
		out.tv_sec = -out.tv_sec;
		out.tv_usec = -out.tv_usec;
		out = normalize_tval(out);
	}

	return out;
}

static inline struct timeval
lfp_uintv_to_tval(
	l_fp		x
	)
{
	struct timeval	out;
	
	TSFTOTVU(x.l_uf, out.tv_usec);
	out.tv_sec = x.l_ui;

	return out;
}

/*
 * absolute (timestamp) conversion. Input is time in NTP epoch, output
 * is in UN*X epoch. The NTP time stamp will be expanded around the
 * pivot time *p or the current time, if p is NULL.
 */
static inline struct timeval
lfp_stamp_to_tval(
	l_fp		x,
	const time_t *	p
	)
{
	struct timeval	out;
	vint64		sec;

	sec = ntpcal_ntp_to_time(x.l_ui, p);
	TSFTOTVU(x.l_uf, out.tv_usec);

	/* copying a vint64 to a time_t needs some care... */
#if SIZEOF_TIME_T <= 4
	out.tv_sec = (time_t)sec.d_s.lo;
#elif defined(HAVE_INT64)
	out.tv_sec = (time_t)sec.q_s;
#else
	out.tv_sec = ((time_t)sec.d_s.hi << 32) | sec.d_s.lo;
#endif
	out = normalize_tval(out);

	return out;
}

#endif	/* TIMEVALOPS_H */