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