1 /* $NetBSD: timespecops.h,v 1.5 2020/05/25 20:47:20 christos Exp $ */
2
3 /*
4 * timespecops.h -- 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 * Rationale
10 * ---------
11 *
12 * Doing basic arithmetic on a 'struct timespec' is not exceedingly
13 * hard, but it requires tedious and repetitive code to keep the result
14 * normalised. We consider a timespec normalised when the nanosecond
15 * fraction is in the interval [0 .. 10^9[ ; there are multiple value
16 * pairs of seconds and nanoseconds that denote the same time interval,
17 * but the normalised representation is unique. No two different
18 * intervals can have the same normalised representation.
19 *
20 * Another topic is the representation of negative time intervals.
21 * There's more than one way to this, since both the seconds and the
22 * nanoseconds of a timespec are signed values. IMHO, the easiest way is
23 * to use a complement representation where the nanoseconds are still
24 * normalised, no matter what the sign of the seconds value. This makes
25 * normalisation easier, since the sign of the integer part is
26 * irrelevant, and it removes several sign decision cases during the
27 * calculations.
28 *
29 * As long as no signed integer overflow can occur with the nanosecond
30 * part of the operands, all operations work as expected and produce a
31 * normalised result.
32 *
33 * The exception to this are functions fix a '_fast' suffix, which do no
34 * normalisation on input data and therefore expect the input data to be
35 * normalised.
36 *
37 * Input and output operands may overlap; all input is consumed before
38 * the output is written to.
39 */
40 #ifndef TIMESPECOPS_H
41 #define TIMESPECOPS_H
42
43 #include <sys/types.h>
44 #include <stdio.h>
45 #include <math.h>
46
47 #include "ntp.h"
48 #include "timetoa.h"
49
50
51 /* nanoseconds per second */
52 #define NANOSECONDS 1000000000
53
54 /* predicate: returns TRUE if the nanoseconds are in nominal range */
55 #define timespec_isnormal(x) \
56 ((x)->tv_nsec >= 0 && (x)->tv_nsec < NANOSECONDS)
57
58 /* predicate: returns TRUE if the nanoseconds are out-of-bounds */
59 #define timespec_isdenormal(x) (!timespec_isnormal(x))
60
61
62
63
64 /* make sure nanoseconds are in nominal range */
65 extern struct timespec normalize_tspec(struct timespec x);
66
67 /* x = a + b */
68 static inline struct timespec
add_tspec(struct timespec a,struct timespec b)69 add_tspec(
70 struct timespec a,
71 struct timespec b
72 )
73 {
74 struct timespec x;
75
76 x = a;
77 x.tv_sec += b.tv_sec;
78 x.tv_nsec += b.tv_nsec;
79
80 return normalize_tspec(x);
81 }
82
83 /* x = a + b, b is fraction only */
84 static inline struct timespec
add_tspec_ns(struct timespec a,long b)85 add_tspec_ns(
86 struct timespec a,
87 long b
88 )
89 {
90 struct timespec x;
91
92 x = a;
93 x.tv_nsec += b;
94
95 return normalize_tspec(x);
96 }
97
98 /* x = a - b */
99 static inline struct timespec
sub_tspec(struct timespec a,struct timespec b)100 sub_tspec(
101 struct timespec a,
102 struct timespec b
103 )
104 {
105 struct timespec x;
106
107 x = a;
108 x.tv_sec -= b.tv_sec;
109 x.tv_nsec -= b.tv_nsec;
110
111 return normalize_tspec(x);
112 }
113
114 /* x = a - b, b is fraction only */
115 static inline struct timespec
sub_tspec_ns(struct timespec a,long b)116 sub_tspec_ns(
117 struct timespec a,
118 long b
119 )
120 {
121 struct timespec x;
122
123 x = a;
124 x.tv_nsec -= b;
125
126 return normalize_tspec(x);
127 }
128
129 /* x = -a */
130 static inline struct timespec
neg_tspec(struct timespec a)131 neg_tspec(
132 struct timespec a
133 )
134 {
135 struct timespec x;
136
137 x.tv_sec = -a.tv_sec;
138 x.tv_nsec = -a.tv_nsec;
139
140 return normalize_tspec(x);
141 }
142
143 /* x = abs(a) */
144 struct timespec abs_tspec(struct timespec a);
145
146 /*
147 * compare previously-normalised a and b
148 * return 1 / 0 / -1 if a < / == / > b
149 */
150 extern int cmp_tspec(struct timespec a, struct timespec b);
151
152 /*
153 * compare possibly-denormal a and b
154 * return 1 / 0 / -1 if a < / == / > b
155 */
156 static inline int
cmp_tspec_denorm(struct timespec a,struct timespec b)157 cmp_tspec_denorm(
158 struct timespec a,
159 struct timespec b
160 )
161 {
162 return cmp_tspec(normalize_tspec(a), normalize_tspec(b));
163 }
164
165 /*
166 * test previously-normalised a
167 * return 1 / 0 / -1 if a < / == / > 0
168 */
169 extern int test_tspec(struct timespec a);
170
171 /*
172 * test possibly-denormal a
173 * return 1 / 0 / -1 if a < / == / > 0
174 */
175 static inline int
test_tspec_denorm(struct timespec a)176 test_tspec_denorm(
177 struct timespec a
178 )
179 {
180 return test_tspec(normalize_tspec(a));
181 }
182
183 /* return LIB buffer ptr to string rep */
184 static inline const char *
tspectoa(struct timespec x)185 tspectoa(
186 struct timespec x
187 )
188 {
189 return format_time_fraction(x.tv_sec, x.tv_nsec, 9);
190 }
191
192 /*
193 * convert to l_fp type, relative and absolute
194 */
195
196 /* convert from timespec duration to l_fp duration */
197 extern l_fp tspec_intv_to_lfp(struct timespec x);
198
199 /* x must be UN*X epoch, output will be in NTP epoch */
200 static inline l_fp
tspec_stamp_to_lfp(struct timespec x)201 tspec_stamp_to_lfp(
202 struct timespec x
203 )
204 {
205 l_fp y;
206
207 y = tspec_intv_to_lfp(x);
208 y.l_ui += JAN_1970;
209
210 return y;
211 }
212
213 /* convert from l_fp type, relative signed/unsigned and absolute */
214 extern struct timespec lfp_intv_to_tspec(l_fp x);
215 extern struct timespec lfp_uintv_to_tspec(l_fp x);
216
217 /*
218 * absolute (timestamp) conversion. Input is time in NTP epoch, output
219 * is in UN*X epoch. The NTP time stamp will be expanded around the
220 * pivot time *p or the current time, if p is NULL.
221 */
222 extern struct timespec lfp_stamp_to_tspec(l_fp x, const time_t *pivot);
223
224 #endif /* TIMESPECOPS_H */
225