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
33 extern const u_int32 ustotslo[];
34 extern const u_int32 ustotsmid[];
35 extern 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
53 extern const u_int32 tstouslo[256];
54 extern const u_int32 tstousmid[256];
55 extern 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 */
137 static inline struct timeval
normalize_tval(struct timeval x)138 normalize_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 */
181 static inline struct timeval
add_tval(struct timeval a,struct timeval b)182 add_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 */
197 static inline struct timeval
add_tval_us(struct timeval a,long b)198 add_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 */
212 static inline struct timeval
sub_tval(struct timeval a,struct timeval b)213 sub_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 */
228 static inline struct timeval
sub_tval_us(struct timeval a,long b)229 sub_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 */
243 static inline struct timeval
neg_tval(struct timeval a)244 neg_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) */
257 static inline struct timeval
abs_tval(struct timeval a)258 abs_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  */
281 static inline int
cmp_tval(struct timeval a,struct timeval b)282 cmp_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  */
301 static inline int
cmp_tval_denorm(struct timeval a,struct timeval b)302 cmp_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  */
314 static inline int
test_tval(struct timeval a)315 test_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  */
332 static inline int
test_tval_denorm(struct timeval a)333 test_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 */
341 static inline const char *
tvaltoa(struct timeval x)342 tvaltoa(
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 */
350 static inline l_fp
tval_intv_to_lfp(struct timeval x)351 tval_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 */
366 static inline l_fp
tval_stamp_to_lfp(struct timeval x)367 tval_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 */
380 static inline struct timeval
lfp_intv_to_tval(l_fp x)381 lfp_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 
405 static inline struct timeval
lfp_uintv_to_tval(l_fp x)406 lfp_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  */
423 static inline struct timeval
lfp_stamp_to_tval(l_fp x,const time_t * p)424 lfp_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