summaryrefslogtreecommitdiff
path: root/external/bsd/ntp/dist/include/timespecops.h
blob: 066579fec90ea436e289c7ca47473df153f7dd20 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*	$NetBSD: timespecops.h,v 1.5 2020/05/25 20:47:20 christos Exp $	*/

/*
 * timespecops.h -- calculations on 'struct timespec' values
 *
 * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
 * The contents of 'html/copyright.html' apply.
 *
 * Rationale
 * ---------
 *
 * Doing basic arithmetic on a 'struct timespec' is not exceedingly
 * hard, but it requires tedious and repetitive code to keep the result
 * normalised. We consider a timespec normalised when the nanosecond
 * fraction is in the interval [0 .. 10^9[ ; there are multiple value
 * pairs of seconds and nanoseconds that denote the same time interval,
 * but the normalised representation is unique. No two different
 * intervals can have the same normalised representation.
 *
 * Another topic is the representation of negative time intervals.
 * There's more than one way to this, since both the seconds and the
 * nanoseconds of a timespec are signed values. IMHO, the easiest way is
 * to use a complement representation where the nanoseconds are still
 * normalised, no matter what the sign of the seconds value. This makes
 * normalisation easier, since the sign of the integer part is
 * irrelevant, and it removes several sign decision cases during the
 * calculations.
 *
 * As long as no signed integer overflow can occur with the nanosecond
 * part of the operands, all operations work as expected and produce a
 * normalised result.
 *
 * The exception to this are functions fix a '_fast' suffix, which do no
 * normalisation on input data and therefore expect the input data to be
 * normalised.
 *
 * Input and output operands may overlap; all input is consumed before
 * the output is written to.
 */
#ifndef TIMESPECOPS_H
#define TIMESPECOPS_H

#include <sys/types.h>
#include <stdio.h>
#include <math.h>

#include "ntp.h"
#include "timetoa.h"


/* nanoseconds per second */
#define NANOSECONDS 1000000000

/* predicate: returns TRUE if the nanoseconds are in nominal range */
#define timespec_isnormal(x) \
	((x)->tv_nsec >= 0 && (x)->tv_nsec < NANOSECONDS)

/* predicate: returns TRUE if the nanoseconds are out-of-bounds */
#define timespec_isdenormal(x)	(!timespec_isnormal(x))




/* make sure nanoseconds are in nominal range */
extern struct timespec normalize_tspec(struct timespec x);

/* x = a + b */
static inline struct timespec
add_tspec(
	struct timespec	a,
	struct timespec	b
	)
{
	struct timespec	x;

	x = a;
	x.tv_sec += b.tv_sec;
	x.tv_nsec += b.tv_nsec;

	return normalize_tspec(x);
}

/* x = a + b, b is fraction only */
static inline struct timespec
add_tspec_ns(
	struct timespec	a,
	long		b
	)
{
	struct timespec x;

	x = a;
	x.tv_nsec += b;

	return normalize_tspec(x);
}

/* x = a - b */
static inline struct timespec
sub_tspec(
	struct timespec	a,
	struct timespec	b
	)
{	
	struct timespec x;

	x = a;
	x.tv_sec -= b.tv_sec;
	x.tv_nsec -= b.tv_nsec;

	return normalize_tspec(x);
}

/* x = a - b, b is fraction only */
static inline struct timespec
sub_tspec_ns(
	struct timespec	a,
	long		b
	)
{
	struct timespec	x;

	x = a;
	x.tv_nsec -= b;

	return normalize_tspec(x);
}

/* x = -a */
static inline struct timespec
neg_tspec(
	struct timespec	a
	)
{	
	struct timespec	x;

	x.tv_sec = -a.tv_sec;
	x.tv_nsec = -a.tv_nsec;

	return normalize_tspec(x);
}

/* x = abs(a) */
struct timespec abs_tspec(struct timespec a);

/*
 * compare previously-normalised a and b
 * return 1 / 0 / -1 if a < / == / > b
 */
extern int cmp_tspec(struct timespec a,	struct timespec b);

/*
 * compare possibly-denormal a and b
 * return 1 / 0 / -1 if a < / == / > b
 */
static inline int
cmp_tspec_denorm(
	struct timespec	a,
	struct timespec	b
	)
{
	return cmp_tspec(normalize_tspec(a), normalize_tspec(b));
}

/*
 * test previously-normalised a
 * return 1 / 0 / -1 if a < / == / > 0
 */
extern int test_tspec(struct timespec a);

/*
 * test possibly-denormal a
 * return 1 / 0 / -1 if a < / == / > 0
 */
static inline int
test_tspec_denorm(
	struct timespec	a
	)
{
	return test_tspec(normalize_tspec(a));
}

/* return LIB buffer ptr to string rep */
static inline const char *
tspectoa(
	struct timespec	x
	)
{
	return format_time_fraction(x.tv_sec, x.tv_nsec, 9);
}

/*
 *  convert to l_fp type, relative and absolute
 */

/* convert from timespec duration to l_fp duration */
extern l_fp tspec_intv_to_lfp(struct timespec x);

/* x must be UN*X epoch, output will be in NTP epoch */
static inline l_fp
tspec_stamp_to_lfp(
	struct timespec	x
	)
{
	l_fp		y;

	y = tspec_intv_to_lfp(x);
	y.l_ui += JAN_1970;

	return y;
}

/* convert from l_fp type, relative signed/unsigned and absolute */
extern struct timespec lfp_intv_to_tspec(l_fp x);
extern struct timespec lfp_uintv_to_tspec(l_fp x);

/*
 * absolute (timestamp) conversion. Input is time in NTP epoch, output
 * is in UN*X epoch. The NTP time stamp will be expanded around the
 * pivot time *p or the current time, if p is NULL.
 */
extern struct timespec lfp_stamp_to_tspec(l_fp x, const time_t *pivot);

#endif	/* TIMESPECOPS_H */