1 /*
2  * This file is Copyright (c) 2015 by the GPSD project
3  * SPDX-License-Identifier: BSD-2-clause
4  */
5 
6 #ifndef GPSD_TIMESPEC_H
7 #define GPSD_TIMESPEC_H
8 
9 #include <math.h>          /* for modf() */
10 #include <stdbool.h>       /* for bool */
11 
12 /* normalize a timespec
13  *
14  * three cases to note
15  * if tv_sec is positve, then tv_nsec must be positive
16  * if tv_sec is negative, then tv_nsec must be negative
17  * if tv_sec is zero, then tv_nsec may be positive or negative.
18  *
19  * this only handles the case where two normalized timespecs
20  * are added or subracted.  (e.g. only a one needs to be borrowed/carried
21  *
22  * NOTE: this normalization is not the same as ntpd uses
23  */
24 #define NS_IN_SEC	1000000000LL     /* nanoseconds in a second */
25 #define US_IN_SEC	1000000LL        /* microseconds in a second */
26 #define MS_IN_SEC	1000LL           /* milliseconds in a second */
27 
28 /* return the difference between timespecs in nanoseconds
29  * int may be too small, 32 bit long is too small, floats are too imprecise,
30  * doubles are not quite precise enough
31  * MUST be at least int64_t to maintain precision on 32 bit code */
32 #define timespec_diff_ns(x, y) \
33     (int64_t)((((x).tv_sec-(y).tv_sec)*NS_IN_SEC)+(x).tv_nsec-(y).tv_nsec)
34 
TS_NORM(struct timespec * ts)35 static inline void TS_NORM( struct timespec *ts)
36 {
37     if ( (  1 <= ts->tv_sec ) ||
38          ( (0 == ts->tv_sec ) && (0 <= ts->tv_nsec ) ) ) {
39         /* result is positive */
40 	if ( NS_IN_SEC <= ts->tv_nsec ) {
41             /* borrow from tv_sec */
42 	    ts->tv_nsec -= NS_IN_SEC;
43 	    ts->tv_sec++;
44 	} else if ( 0 > (ts)->tv_nsec ) {
45             /* carry to tv_sec */
46 	    ts->tv_nsec += NS_IN_SEC;
47 	    ts->tv_sec--;
48 	}
49     }  else {
50         /* result is negative */
51 	if ( -NS_IN_SEC >= ts->tv_nsec ) {
52             /* carry to tv_sec */
53 	    ts->tv_nsec += NS_IN_SEC;
54 	    ts->tv_sec--;
55 	} else if ( 0 < ts->tv_nsec ) {
56             /* borrow from tv_sec */
57 	    ts->tv_nsec -= NS_IN_SEC;
58 	    ts->tv_sec++;
59 	}
60     }
61 }
62 
63 /* normalize a timeval */
64 #define TV_NORM(tv)  \
65     do { \
66 	if ( US_IN_SEC <= (tv)->tv_usec ) { \
67 	    (tv)->tv_usec -= US_IN_SEC; \
68 	    (tv)->tv_sec++; \
69 	} else if ( 0 > (tv)->tv_usec ) { \
70 	    (tv)->tv_usec += US_IN_SEC; \
71 	    (tv)->tv_sec--; \
72 	} \
73     } while (0)
74 
75 /* convert timespec to timeval, with rounding */
76 #define TSTOTV(tv, ts) \
77     do { \
78 	(tv)->tv_sec = (ts)->tv_sec; \
79 	(tv)->tv_usec = ((ts)->tv_nsec + 500)/1000; \
80         TV_NORM( tv ); \
81     } while (0)
82 
83 /* convert timeval to timespec */
84 #define TVTOTS(ts, tv) \
85     do { \
86 	(ts)->tv_sec = (tv)->tv_sec; \
87 	(ts)->tv_nsec = (tv)->tv_usec*1000; \
88         TS_NORM( ts ); \
89     } while (0)
90 
91 /* subtract two timespec */
92 #define TS_SUB(r, ts1, ts2) \
93     do { \
94 	(r)->tv_sec = (ts1)->tv_sec - (ts2)->tv_sec; \
95 	(r)->tv_nsec = (ts1)->tv_nsec - (ts2)->tv_nsec; \
96         TS_NORM( r ); \
97     } while (0)
98 
99 /* subtract two timespec, return a double */
100 #define TS_SUB_D(ts1, ts2) \
101     ((double)((ts1)->tv_sec - (ts2)->tv_sec) + \
102       ((double)((ts1)->tv_nsec - (ts2)->tv_nsec) * 1e-9))
103 
104 // true if normalized timespec is non zero
105 #define TS_NZ(ts) (0 != (ts)->tv_sec || 0 != (ts)->tv_nsec)
106 
107 // true if normalized timespec equal or greater than zero
108 #define TS_GEZ(ts) (0 <= (ts)->tv_sec && 0 <= (ts)->tv_nsec)
109 
110 // true if normalized timespec greater than zero
111 #define TS_GZ(ts) (0 < (ts)->tv_sec || 0 < (ts)->tv_nsec)
112 
113 // true if normalized timespec1 greater than timespec2
114 #define TS_GT(ts1, ts2) ((ts1)->tv_sec > (ts2)->tv_sec || \
115                          ((ts1)->tv_sec == (ts2)->tv_sec && \
116                           (ts1)->tv_nsec > (ts2)->tv_nsec))
117 
118 // true if normalized timespec1 greater or equal to timespec2
119 #define TS_GE(ts1, ts2) ((ts1)->tv_sec > (ts2)->tv_sec || \
120                          ((ts1)->tv_sec == (ts2)->tv_sec && \
121                           (ts1)->tv_nsec >= (ts2)->tv_nsec))
122 
123 // true if normalized timespec1 equal to timespec2
124 #define TS_EQ(ts1, ts2) ((ts1)->tv_sec == (ts2)->tv_sec && \
125                          (ts1)->tv_nsec == (ts2)->tv_nsec)
126 
127 /* convert a timespec to a double.
128  * if tv_sec > 2, then inevitable loss of precision in tv_nsec
129  * so best to NEVER use TSTONS()
130  * WARNING replacing 1e9 with NS_IN_SEC causes loss of precision */
131 #define TSTONS(ts) ((double)((ts)->tv_sec + ((ts)->tv_nsec / 1e9)))
132 
133 /* convert a double to a timespec_t
134  * if D > 2, then inevitable loss of precision in nanoseconds
135  */
136 #define DTOTS(ts, d) \
137     do { \
138         double int_part; \
139 	(ts)->tv_nsec = (long)(modf(d, &int_part) * 1e9); \
140 	(ts)->tv_sec = (time_t)int_part; \
141     } while (0)
142 
143 /* convert integer (64 bit for full range) ms to a timespec_t */
144 #define MSTOTS(ts, ms) \
145     do { \
146 	(ts)->tv_sec = (time_t)(ms / 1000); \
147 	(ts)->tv_nsec = (long)((ms % 1000) * 1000000L); \
148     } while (0)
149 
150 #define TIMESPEC_LEN	22	/* required length of a timespec buffer */
151 
152 extern const char *timespec_str(const struct timespec *, char *, size_t);
153 
154 bool nanowait(int, int);
155 
156 #endif /* GPSD_TIMESPEC_H */
157 
158 /* end */
159