timespecops.h revision 290000
1130561Sobrien/*
2130561Sobrien * timespecops.h -- calculations on 'struct timespec' values
3130561Sobrien *
4130561Sobrien * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
5130561Sobrien * The contents of 'html/copyright.html' apply.
6130561Sobrien *
7130561Sobrien * Rationale
8130561Sobrien * ---------
9130561Sobrien *
10130561Sobrien * Doing basic arithmetic on a 'struct timespec' is not exceedingly
11130561Sobrien * hard, but it requires tedious and repetitive code to keep the result
12130561Sobrien * normalised. We consider a timespec normalised when the nanosecond
13130561Sobrien * fraction is in the interval [0 .. 10^9[ ; there are multiple value
14130561Sobrien * pairs of seconds and nanoseconds that denote the same time interval,
15130561Sobrien * but the normalised representation is unique. No two different
16130561Sobrien * intervals can have the same normalised representation.
17130561Sobrien *
18130561Sobrien * Another topic is the representation of negative time intervals.
19130561Sobrien * There's more than one way to this, since both the seconds and the
20130561Sobrien * nanoseconds of a timespec are signed values. IMHO, the easiest way is
21130561Sobrien * to use a complement representation where the nanoseconds are still
22130561Sobrien * normalised, no matter what the sign of the seconds value. This makes
23130561Sobrien * normalisation easier, since the sign of the integer part is
24130561Sobrien * irrelevant, and it removes several sign decision cases during the
25130561Sobrien * calculations.
26130561Sobrien *
27130561Sobrien * As long as no signed integer overflow can occur with the nanosecond
28130561Sobrien * part of the operands, all operations work as expected and produce a
29130561Sobrien * normalised result.
30130561Sobrien *
31130561Sobrien * The exception to this are functions fix a '_fast' suffix, which do no
32130561Sobrien * normalisation on input data and therefore expect the input data to be
33130561Sobrien * normalised.
34130561Sobrien *
35130561Sobrien * Input and output operands may overlap; all input is consumed before
36130561Sobrien * the output is written to.
37130561Sobrien */
38130561Sobrien#ifndef TIMESPECOPS_H
39130561Sobrien#define TIMESPECOPS_H
40130561Sobrien
41130561Sobrien#include <sys/types.h>
42130561Sobrien#include <stdio.h>
43130561Sobrien#include <math.h>
44130561Sobrien
45130561Sobrien#include "ntp.h"
46130561Sobrien#include "timetoa.h"
47130561Sobrien
48130561Sobrien
49130561Sobrien/* nanoseconds per second */
50130561Sobrien#define NANOSECONDS 1000000000
51130561Sobrien
52130561Sobrien/* predicate: returns TRUE if the nanoseconds are in nominal range */
53130561Sobrien#define timespec_isnormal(x) \
54130561Sobrien	((x)->tv_nsec >= 0 && (x)->tv_nsec < NANOSECONDS)
55130561Sobrien
56130561Sobrien/* predicate: returns TRUE if the nanoseconds are out-of-bounds */
57130561Sobrien#define timespec_isdenormal(x)	(!timespec_isnormal(x))
58130561Sobrien
59130561Sobrien/* conversion between l_fp fractions and nanoseconds */
60130561Sobrien#ifdef HAVE_U_INT64
61130561Sobrien# define FTOTVN(tsf)						\
62130561Sobrien	((int32)						\
63130561Sobrien	 (((u_int64)(tsf) * NANOSECONDS + 0x80000000) >> 32))
64130561Sobrien# define TVNTOF(tvu)						\
65130561Sobrien	((u_int32)						\
66130561Sobrien	 ((((u_int64)(tvu) << 32) + NANOSECONDS / 2) /		\
67130561Sobrien	  NANOSECONDS))
68130561Sobrien#else
69130561Sobrien# define NSECFRAC	(FRAC / NANOSECONDS)
70130561Sobrien# define FTOTVN(tsf)						\
71130561Sobrien	((int32)((tsf) / NSECFRAC + 0.5))
72130561Sobrien# define TVNTOF(tvu)						\
73130561Sobrien	((u_int32)((tvu) * NSECFRAC + 0.5))
74130561Sobrien#endif
75130561Sobrien
76130561Sobrien
77130561Sobrien
78130561Sobrien/* make sure nanoseconds are in nominal range */
79130561Sobrienstatic inline struct timespec
80130561Sobriennormalize_tspec(
81130561Sobrien	struct timespec x
82130561Sobrien	)
83130561Sobrien{
84130561Sobrien#if SIZEOF_LONG > 4
85130561Sobrien	long	z;
86130561Sobrien
87130561Sobrien	/*
88130561Sobrien	 * tv_nsec is of type 'long', and on a 64-bit machine using only
89130561Sobrien	 * loops becomes prohibitive once the upper 32 bits get
90130561Sobrien	 * involved. On the other hand, division by constant should be
91130561Sobrien	 * fast enough; so we do a division of the nanoseconds in that
92130561Sobrien	 * case. The floor adjustment step follows with the standard
93130561Sobrien	 * normalisation loops. And labs() is intentionally not used
94130561Sobrien	 * here: it has implementation-defined behaviour when applied
95130561Sobrien	 * to LONG_MIN.
96130561Sobrien	 */
97130561Sobrien	if (x.tv_nsec < -3l * NANOSECONDS ||
98130561Sobrien	    x.tv_nsec > 3l * NANOSECONDS) {
99130561Sobrien		z = x.tv_nsec / NANOSECONDS;
100130561Sobrien		x.tv_nsec -= z * NANOSECONDS;
101130561Sobrien		x.tv_sec += z;
102130561Sobrien	}
103130561Sobrien#endif
104130561Sobrien	/* since 10**9 is close to 2**32, we don't divide but do a
105130561Sobrien	 * normalisation in a loop; this takes 3 steps max, and should
106130561Sobrien	 * outperform a division even if the mul-by-inverse trick is
107130561Sobrien	 * employed. */
108130561Sobrien	if (x.tv_nsec < 0)
109130561Sobrien		do {
110130561Sobrien			x.tv_nsec += NANOSECONDS;
111130561Sobrien			x.tv_sec--;
112130561Sobrien		} while (x.tv_nsec < 0);
113130561Sobrien	else if (x.tv_nsec >= NANOSECONDS)
114130561Sobrien		do {
115130561Sobrien			x.tv_nsec -= NANOSECONDS;
116130561Sobrien			x.tv_sec++;
117130561Sobrien		} while (x.tv_nsec >= NANOSECONDS);
118130561Sobrien
119130561Sobrien	return x;
120130561Sobrien}
121130561Sobrien
122130561Sobrien/* x = a + b */
123130561Sobrienstatic inline struct timespec
124130561Sobrienadd_tspec(
125130561Sobrien	struct timespec	a,
126130561Sobrien	struct timespec	b
127130561Sobrien	)
128130561Sobrien{
129130561Sobrien	struct timespec	x;
130130561Sobrien
131130561Sobrien	x = a;
132130561Sobrien	x.tv_sec += b.tv_sec;
133130561Sobrien	x.tv_nsec += b.tv_nsec;
134130561Sobrien
135130561Sobrien	return normalize_tspec(x);
136130561Sobrien}
137130561Sobrien
138130561Sobrien/* x = a + b, b is fraction only */
139130561Sobrienstatic inline struct timespec
140130561Sobrienadd_tspec_ns(
141130561Sobrien	struct timespec	a,
142130561Sobrien	long		b
143130561Sobrien	)
144130561Sobrien{
145130561Sobrien	struct timespec x;
146130561Sobrien
147130561Sobrien	x = a;
148130561Sobrien	x.tv_nsec += b;
149130561Sobrien
150130561Sobrien	return normalize_tspec(x);
151130561Sobrien}
152130561Sobrien
153130561Sobrien/* x = a - b */
154130561Sobrienstatic inline struct timespec
155130561Sobriensub_tspec(
156130561Sobrien	struct timespec	a,
157130561Sobrien	struct timespec	b
158130561Sobrien	)
159130561Sobrien{
160130561Sobrien	struct timespec x;
161130561Sobrien
162130561Sobrien	x = a;
163130561Sobrien	x.tv_sec -= b.tv_sec;
164130561Sobrien	x.tv_nsec -= b.tv_nsec;
165130561Sobrien
166130561Sobrien	return normalize_tspec(x);
167130561Sobrien}
168130561Sobrien
169130561Sobrien/* x = a - b, b is fraction only */
170130561Sobrienstatic inline struct timespec
171130561Sobriensub_tspec_ns(
172130561Sobrien	struct timespec	a,
173130561Sobrien	long		b
174130561Sobrien	)
175130561Sobrien{
176130561Sobrien	struct timespec	x;
177130561Sobrien
178130561Sobrien	x = a;
179130561Sobrien	x.tv_nsec -= b;
180130561Sobrien
181130561Sobrien	return normalize_tspec(x);
182130561Sobrien}
183130561Sobrien
184130561Sobrien/* x = -a */
185130561Sobrienstatic inline struct timespec
186130561Sobrienneg_tspec(
187130561Sobrien	struct timespec	a
188130561Sobrien	)
189130561Sobrien{
190130561Sobrien	struct timespec	x;
191130561Sobrien
192130561Sobrien	x.tv_sec = -a.tv_sec;
193130561Sobrien	x.tv_nsec = -a.tv_nsec;
194130561Sobrien
195130561Sobrien	return normalize_tspec(x);
196130561Sobrien}
197130561Sobrien
198130561Sobrien/* x = abs(a) */
199130561Sobrienstatic inline struct timespec
200130561Sobrienabs_tspec(
201130561Sobrien	struct timespec	a
202130561Sobrien	)
203130561Sobrien{
204130561Sobrien	struct timespec	c;
205130561Sobrien
206130561Sobrien	c = normalize_tspec(a);
207130561Sobrien	if (c.tv_sec < 0) {
208130561Sobrien		if (c.tv_nsec != 0) {
209130561Sobrien			c.tv_sec = -c.tv_sec - 1;
210130561Sobrien			c.tv_nsec = NANOSECONDS - c.tv_nsec;
211130561Sobrien		} else {
212130561Sobrien			c.tv_sec = -c.tv_sec;
213130561Sobrien		}
214130561Sobrien	}
215130561Sobrien
216130561Sobrien	return c;
217130561Sobrien}
218130561Sobrien
219130561Sobrien/*
220130561Sobrien * compare previously-normalised a and b
221130561Sobrien * return 1 / 0 / -1 if a < / == / > b
222130561Sobrien */
223130561Sobrienstatic inline int
224130561Sobriencmp_tspec(
225130561Sobrien	struct timespec a,
226130561Sobrien	struct timespec b
227130561Sobrien	)
228130561Sobrien{
229130561Sobrien	int r;
230130561Sobrien
231130561Sobrien	r = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec);
232130561Sobrien	if (0 == r)
233130561Sobrien		r = (a.tv_nsec > b.tv_nsec) -
234130561Sobrien		    (a.tv_nsec < b.tv_nsec);
235130561Sobrien
236130561Sobrien	return r;
237130561Sobrien}
238130561Sobrien
239130561Sobrien/*
240130561Sobrien * compare possibly-denormal a and b
241130561Sobrien * return 1 / 0 / -1 if a < / == / > b
242130561Sobrien */
243130561Sobrienstatic inline int
244130561Sobriencmp_tspec_denorm(
245130561Sobrien	struct timespec	a,
246130561Sobrien	struct timespec	b
247130561Sobrien	)
248130561Sobrien{
249130561Sobrien	return cmp_tspec(normalize_tspec(a), normalize_tspec(b));
250130561Sobrien}
251130561Sobrien
252130561Sobrien/*
253130561Sobrien * test previously-normalised a
254130561Sobrien * return 1 / 0 / -1 if a < / == / > 0
255130561Sobrien */
256130561Sobrienstatic inline int
257130561Sobrientest_tspec(
258130561Sobrien	struct timespec	a
259130561Sobrien	)
260130561Sobrien{
261130561Sobrien	int		r;
262130561Sobrien
263130561Sobrien	r = (a.tv_sec > 0) - (a.tv_sec < 0);
264130561Sobrien	if (r == 0)
265130561Sobrien		r = (a.tv_nsec > 0);
266130561Sobrien
267130561Sobrien	return r;
268130561Sobrien}
269130561Sobrien
270130561Sobrien/*
271130561Sobrien * test possibly-denormal a
272130561Sobrien * return 1 / 0 / -1 if a < / == / > 0
273130561Sobrien */
274130561Sobrienstatic inline int
275130561Sobrientest_tspec_denorm(
276130561Sobrien	struct timespec	a
277130561Sobrien	)
278130561Sobrien{
279130561Sobrien	return test_tspec(normalize_tspec(a));
280130561Sobrien}
281130561Sobrien
282130561Sobrien/* return LIB buffer ptr to string rep */
283130561Sobrienstatic inline const char *
284130561Sobrientspectoa(
285130561Sobrien	struct timespec	x
286130561Sobrien	)
287130561Sobrien{
288130561Sobrien	return format_time_fraction(x.tv_sec, x.tv_nsec, 9);
289130561Sobrien}
290130561Sobrien
291130561Sobrien/*
292130561Sobrien *  convert to l_fp type, relative and absolute
293130561Sobrien */
294130561Sobrien
295130561Sobrien/* convert from timespec duration to l_fp duration */
296130561Sobrienstatic inline l_fp
297130561Sobrientspec_intv_to_lfp(
298130561Sobrien	struct timespec	x
299130561Sobrien	)
300130561Sobrien{
301130561Sobrien	struct timespec	v;
302130561Sobrien	l_fp		y;
303130561Sobrien
304130561Sobrien	v = normalize_tspec(x);
305130561Sobrien	y.l_uf = TVNTOF(v.tv_nsec);
306130561Sobrien	y.l_i = (int32)v.tv_sec;
307130561Sobrien
308130561Sobrien	return y;
309130561Sobrien}
310130561Sobrien
311130561Sobrien/* x must be UN*X epoch, output will be in NTP epoch */
312130561Sobrienstatic inline l_fp
313130561Sobrientspec_stamp_to_lfp(
314130561Sobrien	struct timespec	x
315130561Sobrien	)
316130561Sobrien{
317130561Sobrien	l_fp		y;
318130561Sobrien
319130561Sobrien	y = tspec_intv_to_lfp(x);
320130561Sobrien	y.l_ui += JAN_1970;
321130561Sobrien
322130561Sobrien	return y;
323130561Sobrien}
324130561Sobrien
325130561Sobrien/* convert from l_fp type, relative signed/unsigned and absolute */
326130561Sobrienstatic inline struct timespec
327130561Sobrienlfp_intv_to_tspec(
328130561Sobrien	l_fp		x
329130561Sobrien	)
330130561Sobrien{
331130561Sobrien	struct timespec out;
332130561Sobrien	l_fp		absx;
333130561Sobrien	int		neg;
334130561Sobrien
335130561Sobrien	neg = L_ISNEG(&x);
336130561Sobrien	absx = x;
337130561Sobrien	if (neg) {
338130561Sobrien		L_NEG(&absx);
339130561Sobrien	}
340130561Sobrien	out.tv_nsec = FTOTVN(absx.l_uf);
341130561Sobrien	out.tv_sec = absx.l_i;
342130561Sobrien	if (neg) {
343130561Sobrien		out.tv_sec = -out.tv_sec;
344130561Sobrien		out.tv_nsec = -out.tv_nsec;
345130561Sobrien		out = normalize_tspec(out);
346130561Sobrien	}
347130561Sobrien
348130561Sobrien	return out;
349130561Sobrien}
350130561Sobrien
351130561Sobrienstatic inline struct timespec
352130561Sobrienlfp_uintv_to_tspec(
353130561Sobrien	l_fp		x
354130561Sobrien	)
355130561Sobrien{
356130561Sobrien	struct timespec	out;
357130561Sobrien
358130561Sobrien	out.tv_nsec = FTOTVN(x.l_uf);
359130561Sobrien	out.tv_sec = x.l_ui;
360130561Sobrien
361130561Sobrien	return out;
362130561Sobrien}
363130561Sobrien
364130561Sobrien/*
365130561Sobrien * absolute (timestamp) conversion. Input is time in NTP epoch, output
366130561Sobrien * is in UN*X epoch. The NTP time stamp will be expanded around the
367130561Sobrien * pivot time *p or the current time, if p is NULL.
368130561Sobrien */
369130561Sobrienstatic inline struct timespec
370130561Sobrienlfp_stamp_to_tspec(
371130561Sobrien	l_fp		x,
372130561Sobrien	const time_t *	p
373130561Sobrien	)
374130561Sobrien{
375130561Sobrien	struct timespec	out;
376130561Sobrien	vint64		sec;
377130561Sobrien
378130561Sobrien	sec = ntpcal_ntp_to_time(x.l_ui, p);
379130561Sobrien	out.tv_nsec = FTOTVN(x.l_uf);
380130561Sobrien
381130561Sobrien	/* copying a vint64 to a time_t needs some care... */
382130561Sobrien#if SIZEOF_TIME_T <= 4
383130561Sobrien	out.tv_sec = (time_t)sec.d_s.lo;
384130561Sobrien#elif defined(HAVE_INT64)
385130561Sobrien	out.tv_sec = (time_t)sec.q_s;
386130561Sobrien#else
387130561Sobrien	out.tv_sec = ((time_t)sec.d_s.hi << 32) | sec.d_s.lo;
388130561Sobrien#endif
389130561Sobrien
390130561Sobrien	return out;
391130561Sobrien}
392130561Sobrien
393130561Sobrien#endif	/* TIMESPECOPS_H */
394130561Sobrien