1/*	$NetBSD: timespecops.c,v 1.2 2020/05/25 20:47:24 christos Exp $	*/
2
3/*
4 * timespecops.c -- calculations on 'struct timespec' values
5 *
6 * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
7 * The contents of 'html/copyright.html' apply.
8 *
9 */
10
11#include "config.h"
12
13#include <sys/types.h>
14#include <stdio.h>
15#include <math.h>
16
17#include "ntp.h"
18#include "timetoa.h"
19#include "timespecops.h"
20
21
22/* nanoseconds per second */
23#define NANOSECONDS 1000000000
24
25/* conversion between l_fp fractions and nanoseconds */
26#ifdef HAVE_U_INT64
27# define FTOTVN(tsf)						\
28	((int32)						\
29	 (((u_int64)(tsf) * NANOSECONDS + 0x80000000) >> 32))
30# define TVNTOF(tvu)						\
31	((u_int32)						\
32	 ((((u_int64)(tvu) << 32) + NANOSECONDS / 2) /		\
33	  NANOSECONDS))
34#else
35# define NSECFRAC	(FRAC / NANOSECONDS)
36# define FTOTVN(tsf)						\
37	((int32)((tsf) / NSECFRAC + 0.5))
38# define TVNTOF(tvu)						\
39	((u_int32)((tvu) * NSECFRAC + 0.5))
40#endif
41
42
43
44/* make sure nanoseconds are in nominal range */
45struct timespec
46normalize_tspec(
47	struct timespec x
48	)
49{
50#if SIZEOF_LONG > 4
51	long	z;
52
53	/*
54	 * tv_nsec is of type 'long', and on a 64-bit machine using only
55	 * loops becomes prohibitive once the upper 32 bits get
56	 * involved. On the other hand, division by constant should be
57	 * fast enough; so we do a division of the nanoseconds in that
58	 * case. The floor adjustment step follows with the standard
59	 * normalisation loops. And labs() is intentionally not used
60	 * here: it has implementation-defined behaviour when applied
61	 * to LONG_MIN.
62	 */
63	if (x.tv_nsec < -3l * NANOSECONDS ||
64	    x.tv_nsec > 3l * NANOSECONDS) {
65		z = x.tv_nsec / NANOSECONDS;
66		x.tv_nsec -= z * NANOSECONDS;
67		x.tv_sec += z;
68	}
69#endif
70	/* since 10**9 is close to 2**32, we don't divide but do a
71	 * normalisation in a loop; this takes 3 steps max, and should
72	 * outperform a division even if the mul-by-inverse trick is
73	 * employed. */
74	if (x.tv_nsec < 0)
75		do {
76			x.tv_nsec += NANOSECONDS;
77			x.tv_sec--;
78		} while (x.tv_nsec < 0);
79	else if (x.tv_nsec >= NANOSECONDS)
80		do {
81			x.tv_nsec -= NANOSECONDS;
82			x.tv_sec++;
83		} while (x.tv_nsec >= NANOSECONDS);
84
85	return x;
86}
87
88/* x = abs(a) */
89struct timespec
90abs_tspec(
91	struct timespec	a
92	)
93{
94	struct timespec	c;
95
96	c = normalize_tspec(a);
97	if (c.tv_sec < 0) {
98		if (c.tv_nsec != 0) {
99			c.tv_sec = -c.tv_sec - 1;
100			c.tv_nsec = NANOSECONDS - c.tv_nsec;
101		} else {
102			c.tv_sec = -c.tv_sec;
103		}
104	}
105
106	return c;
107}
108
109/*
110 * compare previously-normalised a and b
111 * return 1 / 0 / -1 if a < / == / > b
112 */
113int
114cmp_tspec(
115	struct timespec a,
116	struct timespec b
117	)
118{
119	int r;
120
121	r = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec);
122	if (0 == r)
123		r = (a.tv_nsec > b.tv_nsec) -
124		    (a.tv_nsec < b.tv_nsec);
125
126	return r;
127}
128
129/*
130 * test previously-normalised a
131 * return 1 / 0 / -1 if a < / == / > 0
132 */
133int
134test_tspec(
135	struct timespec	a
136	)
137{
138	int		r;
139
140	r = (a.tv_sec > 0) - (a.tv_sec < 0);
141	if (r == 0)
142		r = (a.tv_nsec > 0);
143
144	return r;
145}
146
147/*
148 *  convert to l_fp type, relative and absolute
149 */
150
151/* convert from timespec duration to l_fp duration */
152l_fp
153tspec_intv_to_lfp(
154	struct timespec	x
155	)
156{
157	struct timespec	v;
158	l_fp		y;
159
160	v = normalize_tspec(x);
161	y.l_uf = TVNTOF(v.tv_nsec);
162	y.l_i = (int32)v.tv_sec;
163
164	return y;
165}
166
167/* convert from l_fp type, relative signed/unsigned and absolute */
168struct timespec
169lfp_intv_to_tspec(
170	l_fp		x
171	)
172{
173	struct timespec out;
174	l_fp		absx;
175	int		neg;
176
177	neg = L_ISNEG(&x);
178	absx = x;
179	if (neg) {
180		L_NEG(&absx);
181	}
182	out.tv_nsec = FTOTVN(absx.l_uf);
183	out.tv_sec = absx.l_i;
184	if (neg) {
185		out.tv_sec = -out.tv_sec;
186		out.tv_nsec = -out.tv_nsec;
187		out = normalize_tspec(out);
188	}
189
190	return out;
191}
192
193struct timespec
194lfp_uintv_to_tspec(
195	l_fp		x
196	)
197{
198	struct timespec	out;
199
200	out.tv_nsec = FTOTVN(x.l_uf);
201	out.tv_sec = x.l_ui;
202
203	return out;
204}
205
206/*
207 * absolute (timestamp) conversion. Input is time in NTP epoch, output
208 * is in UN*X epoch. The NTP time stamp will be expanded around the
209 * pivot time *p or the current time, if p is NULL.
210 */
211struct timespec
212lfp_stamp_to_tspec(
213	l_fp		x,
214	const time_t *	p
215	)
216{
217	struct timespec	out;
218	vint64		sec;
219
220	sec = ntpcal_ntp_to_time(x.l_ui, p);
221	out.tv_nsec = FTOTVN(x.l_uf);
222
223	/* copying a vint64 to a time_t needs some care... */
224#if SIZEOF_TIME_T <= 4
225	out.tv_sec = (time_t)sec.d_s.lo;
226#elif defined(HAVE_INT64)
227	out.tv_sec = (time_t)sec.q_s;
228#else
229	out.tv_sec = ((time_t)sec.d_s.hi << 32) | sec.d_s.lo;
230#endif
231
232	return out;
233}
234
235/* -*-EOF-*- */
236