Coverage Report

Created: 2025-07-18 06:53

/src/ntpsec/libntp/timespecops.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * timespecops.h -- calculations on 'struct timespec' values
3
 *
4
 * Copyright Juergen Perlinger (perlinger@ntp.org)
5
 * Copyright the NTPsec project contributors
6
 * SPDX-License-Identifier: NTP
7
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
 * normalization 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
 * normalization 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
 */
41
#include "config.h"
42
43
#include <stdio.h>
44
#include <math.h>
45
46
#include "timespecops.h"
47
#include "ntp.h"
48
#include "ntp_calendar.h"
49
50
/* make sure nanoseconds are in nominal range */
51
struct timespec
52
normalize_tspec(
53
  struct timespec x
54
  )
55
0
{
56
0
#if NTP_SIZEOF_LONG > 4
57
  /*
58
   * tv_nsec is of type 'long', and on a 64-bit machine using only
59
   * loops becomes prohibitive once the upper 32 bits get
60
   * involved. On the other hand, division by constant should be
61
   * fast enough; so we do a division of the nanoseconds in that
62
   * case.
63
   */
64
0
  if (x.tv_nsec < 0 || x.tv_nsec >= NS_PER_S) {
65
0
    ldiv_t  z = ldiv( x.tv_nsec, NS_PER_S);
66
0
    if (z.rem < 0) {
67
0
      z.quot--;
68
0
      z.rem  += NS_PER_S;
69
0
    }
70
0
    x.tv_sec  += z.quot;
71
0
    x.tv_nsec  = z.rem;
72
0
  }
73
#else
74
  /* since 10**9 is close to 2**32, we don't divide but do a
75
   * normalization in a loop; this takes 3 steps max, and should
76
   * outperform a division even if the mul-by-inverse trick is
77
   * employed. */
78
  if (x.tv_nsec < 0)
79
    do {
80
      x.tv_nsec += NS_PER_S;
81
      x.tv_sec--;
82
    } while (x.tv_nsec < 0);
83
  else if (x.tv_nsec >= NS_PER_S)
84
    do {
85
      x.tv_nsec -= NS_PER_S;
86
      x.tv_sec++;
87
    } while (x.tv_nsec >= NS_PER_S);
88
#endif
89
90
0
  return x;
91
0
}
92
93
/* convert a double to a rounded and normalized timespec */
94
struct timespec
95
d_to_tspec(
96
  double d
97
  )
98
0
{
99
0
  struct timespec x;
100
0
  double s = floor(d);
101
102
0
  x.tv_sec  = (time_t) s;
103
0
  x.tv_nsec = (long) (((d - s) * NS_PER_S) + 0.5);
104
0
  return x;
105
0
}
106
107
/* convert a timespec to a double
108
 * will drop low bits if integer part is big
109
 */
110
double
111
tspec_to_d(
112
  struct timespec ts
113
  )
114
0
{
115
0
  return (ts.tv_sec + ts.tv_nsec*S_PER_NS);
116
0
}
117
118
/* x = a + b */
119
struct timespec
120
add_tspec(
121
  struct timespec a,
122
  struct timespec b
123
  )
124
0
{
125
0
  struct timespec x;
126
127
0
  x = a;
128
0
  x.tv_sec += b.tv_sec;
129
0
  x.tv_nsec += b.tv_nsec;
130
131
0
  return normalize_tspec(x);
132
0
}
133
134
/* x = a + b, b is fraction only */
135
struct timespec
136
add_tspec_ns(
137
  struct timespec a,
138
  long    b
139
  )
140
0
{
141
0
  struct timespec x;
142
143
0
  x = a;
144
0
  x.tv_nsec += b;
145
146
0
  return normalize_tspec(x);
147
0
}
148
149
/* x = a - b */
150
struct timespec
151
sub_tspec(
152
  struct timespec a,
153
  struct timespec b
154
  )
155
0
{
156
0
  struct timespec x;
157
158
0
  x = a;
159
0
  x.tv_sec -= b.tv_sec;
160
0
  x.tv_nsec -= b.tv_nsec;
161
162
0
  return normalize_tspec(x);
163
0
}
164
165
/* x = a - b, b is fraction only */
166
struct timespec
167
sub_tspec_ns(
168
  struct timespec a,
169
  long    b
170
  )
171
0
{
172
0
  struct timespec x;
173
174
0
  x = a;
175
0
  x.tv_nsec -= b;
176
177
0
  return normalize_tspec(x);
178
0
}
179
180
/* x = -a */
181
struct timespec
182
neg_tspec(
183
  struct timespec a
184
  )
185
0
{
186
0
  struct timespec x;
187
188
0
  x.tv_sec = -a.tv_sec;
189
0
  x.tv_nsec = -a.tv_nsec;
190
191
0
  return normalize_tspec(x);
192
0
}
193
194
/* x = abs(a) */
195
struct timespec
196
abs_tspec(
197
  struct timespec a
198
  )
199
0
{
200
0
  struct timespec c;
201
202
0
  c = normalize_tspec(a);
203
0
  if (c.tv_sec < 0) {
204
0
    if (c.tv_nsec != 0) {
205
0
      c.tv_sec = -c.tv_sec - 1;
206
0
      c.tv_nsec = NS_PER_S - c.tv_nsec;
207
0
    } else {
208
0
      c.tv_sec = -c.tv_sec;
209
0
    }
210
0
  }
211
212
0
  return c;
213
0
}
214
215
/*
216
 * compare previously-normalised a and b
217
 * return -1 / 0 / 1 if a < / == / > b
218
 */
219
220
int
221
cmp_tspec(
222
  struct timespec a,
223
  struct timespec b
224
  )
225
0
{
226
0
  int r;
227
228
0
  r = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec);
229
0
  if (0 == r)
230
0
    r = (a.tv_nsec > b.tv_nsec) -
231
0
        (a.tv_nsec < b.tv_nsec);
232
233
0
  return r;
234
0
}
235
236
/*
237
 * compare possibly-denormal a and b
238
 * return -1 / 0 / 1 if a < / == / > b
239
 */
240
int
241
cmp_tspec_denorm(
242
  struct timespec a,
243
  struct timespec b
244
  )
245
0
{
246
0
  return cmp_tspec(normalize_tspec(a), normalize_tspec(b));
247
0
}
248
249
/*
250
 * test previously-normalised a
251
 * return -1 / 0 / 1 if a < / == / > 0
252
 */
253
int
254
test_tspec(
255
  struct timespec a
256
  )
257
0
{
258
0
  int   r;
259
260
0
  r = (a.tv_sec > 0) - (a.tv_sec < 0);
261
0
  if (r == 0)
262
0
    r = (a.tv_nsec > 0);
263
264
0
  return r;
265
0
}
266
267
/*
268
 * test possibly-denormal a
269
 * return -1 / 0 / 1 if a < / == / > 0
270
 */
271
int
272
test_tspec_denorm(
273
  struct timespec a
274
  )
275
0
{
276
0
  return test_tspec(normalize_tspec(a));
277
0
}
278
279
/*
280
 *  convert to l_fp type, relative and absolute
281
 */
282
283
/* convert from timespec duration to l_fp duration */
284
l_fp
285
tspec_intv_to_lfp(
286
  struct timespec x
287
  )
288
0
{
289
0
  struct timespec v = normalize_tspec(x);
290
0
  return lfpinit((int32_t)v.tv_sec, TVNTOF(v.tv_nsec));
291
0
}
292
293
/* x must be UN*X epoch, output will be in NTP epoch */
294
l_fp
295
tspec_stamp_to_lfp(
296
  struct timespec x
297
  )
298
0
{
299
0
  l_fp    y;
300
301
0
  y = tspec_intv_to_lfp(x);
302
0
  bumplfpuint(y, JAN_1970);
303
304
0
  return y;
305
0
}
306
307
/* convert from l_fp type, relative signed/unsigned and absolute */
308
struct timespec
309
lfp_intv_to_tspec(
310
  l_fp    x
311
  )
312
0
{
313
0
  struct timespec out;
314
0
  l_fp    absx;
315
0
  int   neg;
316
317
0
  neg = L_ISNEG(x);
318
0
  absx = x;
319
0
  if (neg) {
320
0
    L_NEG(absx);
321
0
  }
322
0
  out.tv_nsec = FTOTVN(lfpfrac(absx));
323
0
  out.tv_sec = lfpsint(absx);
324
0
  if (neg) {
325
0
    out.tv_sec = -out.tv_sec;
326
0
    out.tv_nsec = -out.tv_nsec;
327
0
    out = normalize_tspec(out);
328
0
  }
329
330
0
  return out;
331
0
}
332
333
struct timespec
334
lfp_uintv_to_tspec(
335
  l_fp    x
336
  )
337
0
{
338
0
  struct timespec out;
339
340
0
  out.tv_nsec = FTOTVN(lfpfrac(x));
341
0
  out.tv_sec  = lfpsint(x);
342
343
0
  return out;
344
0
}
345
346
/*
347
 * absolute (timestamp) conversion. Input is time in NTP epoch, output
348
 * is in UN*X epoch. The NTP time stamp will be expanded around the
349
 * pivot time p.
350
 */
351
struct timespec
352
lfp_stamp_to_tspec(
353
  l_fp    x,
354
  time_t    p
355
  )
356
0
{
357
0
  struct timespec out;
358
0
  time64_t    sec;
359
360
0
  sec = ntpcal_ntp_to_time(lfpuint(x), p);
361
0
  out.tv_nsec = FTOTVN(lfpfrac(x));
362
363
        // copying a time64_t to a time_t needs some care...
364
0
        if (4 >= sizeof(time_t)) {
365
0
            out.tv_sec = (time_t)time64lo(sec);
366
0
        } else {
367
0
            out.tv_sec = (time_t)time64s(sec);
368
0
        }
369
370
0
  return out;
371
0
}
372
373
struct timespec
374
tval_to_tspec(
375
    struct timeval x
376
    )
377
0
{
378
0
  struct timespec y;
379
0
  y.tv_sec = x.tv_sec;
380
0
  y.tv_nsec = x.tv_usec * 1000;
381
0
  return y;
382
0
}
383
384
/* end */