1/*
2 * vint64ops.c - operations on 'vint64' values
3 *
4 * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
5 * The contents of 'html/copyright.html' apply.
6 * ----------------------------------------------------------------------
7 * This is an attempt to get the vint64 calculations stuff centralised.
8 */
9
10#include <config.h>
11#include <stdlib.h>
12#include <ctype.h>
13#include <string.h>
14#include <errno.h>
15
16#include "ntp_types.h"
17#include "ntp_fp.h"
18#include "vint64ops.h"
19
20/* ---------------------------------------------------------------------
21 * GCC is rather sticky with its 'const' attribute. We have to do it more
22 * explicit than with a cast if we want to get rid of a CONST qualifier.
23 * Greetings from the PASCAL world, where casting was only possible via
24 * untagged unions...
25 */
26static inline void*
27noconst(
28	const void* ptr
29	)
30{
31	union {
32		const void * cp;
33		void *       vp;
34	} tmp;
35	tmp.cp = ptr;
36	return tmp.vp;
37}
38
39/* -------------------------------------------------------------------------*/
40
41vint64
42strtouv64(
43	const char * begp,
44	char **      endp,
45	int          base
46	)
47{
48	vint64  res;
49	u_char  digit;
50	int     sig, num;
51	const u_char *src;
52
53	num = sig = 0;
54	src = (const u_char*)begp;
55	while (isspace(*src))
56		src++;
57
58	if (*src == '-') {
59		src++;
60		sig = 1;
61	} else  if (*src == '+') {
62		src++;
63	}
64
65	if (base == 0) {
66		base = 10;
67		if (*src == '0') {
68			base = 8;
69			if (toupper(*++src) == 'X') {
70				src++;
71				base = 16;
72			}
73		}
74	} else if (base == 16) { /* remove optional leading '0x' or '0X' */
75		if (src[0] == '0' && toupper(src[1]) == 'X')
76			src += 2;
77	} else if (base <= 2 || base > 36) {
78		memset(&res, 0xFF, sizeof(res));
79		errno = ERANGE;
80		return res;
81	}
82
83	memset(&res, 0, sizeof(res));
84	while (*src) {
85		if (isdigit(*src))
86			digit = *src - '0';
87		else if (isupper(*src))
88			digit = *src - 'A' + 10;
89		else if (islower(*src))
90			digit = *src - 'a' + 10;
91		else
92			break;
93		if (digit >= base)
94			break;
95		num = 1;
96#if defined(HAVE_INT64)
97		res.Q_s = res.Q_s * base + digit;
98#else
99		/* res *= base, using 16x16->32 bit
100		 * multiplication. Slow but portable.
101		 */
102		{
103			uint32_t accu;
104			accu       = (uint32_t)res.W_s.ll * base;
105			res.W_s.ll = (uint16_t)accu;
106			accu       = (accu >> 16)
107			           + (uint32_t)res.W_s.lh * base;
108			res.W_s.lh = (uint16_t)accu;
109			/* the upper bits can be done in one step: */
110			res.D_s.hi = res.D_s.hi * base + (accu >> 16);
111		}
112		M_ADD(res.D_s.hi, res.D_s.lo, 0, digit);
113#endif
114		src++;
115	}
116	if (!num)
117		errno = EINVAL;
118	if (endp)
119		*endp = (char*)noconst(src);
120	if (sig)
121		M_NEG(res.D_s.hi, res.D_s.lo);
122	return res;
123}
124
125/* -------------------------------------------------------------------------*/
126
127int
128icmpv64(
129	const vint64 * lhs,
130	const vint64 * rhs
131	)
132{
133	int res;
134
135#if defined(HAVE_INT64)
136	res = (lhs->q_s > rhs->q_s)
137	    - (lhs->q_s < rhs->q_s);
138#else
139	res = (lhs->d_s.hi > rhs->d_s.hi)
140	    - (lhs->d_s.hi < rhs->d_s.hi);
141	if ( ! res )
142		res = (lhs->D_s.lo > rhs->D_s.lo)
143		    - (lhs->D_s.lo < rhs->D_s.lo);
144#endif
145
146	return res;
147}
148
149/* -------------------------------------------------------------------------*/
150
151int
152ucmpv64(
153	const vint64 * lhs,
154	const vint64 * rhs
155	)
156{
157	int res;
158
159#if defined(HAVE_INT64)
160	res = (lhs->Q_s > rhs->Q_s)
161	    - (lhs->Q_s < rhs->Q_s);
162#else
163	res = (lhs->D_s.hi > rhs->D_s.hi)
164	    - (lhs->D_s.hi < rhs->D_s.hi);
165	if ( ! res )
166		res = (lhs->D_s.lo > rhs->D_s.lo)
167		    - (lhs->D_s.lo < rhs->D_s.lo);
168#endif
169	return res;
170}
171
172/* -------------------------------------------------------------------------*/
173
174vint64
175addv64(
176	const vint64 *lhs,
177	const vint64 *rhs
178	)
179{
180	vint64 res;
181
182#if defined(HAVE_INT64)
183	res.Q_s = lhs->Q_s + rhs->Q_s;
184#else
185	res = *lhs;
186	M_ADD(res.D_s.hi, res.D_s.lo, rhs->D_s.hi, rhs->D_s.lo);
187#endif
188	return res;
189}
190
191/* -------------------------------------------------------------------------*/
192
193vint64
194subv64(
195	const vint64 *lhs,
196	const vint64 *rhs
197	)
198{
199	vint64 res;
200
201#if defined(HAVE_INT64)
202	res.Q_s = lhs->Q_s - rhs->Q_s;
203#else
204	res = *lhs;
205	M_SUB(res.D_s.hi, res.D_s.lo, rhs->D_s.hi, rhs->D_s.lo);
206#endif
207	return res;
208}
209
210/* -------------------------------------------------------------------------*/
211
212vint64
213addv64i32(
214	const vint64 * lhs,
215	int32_t        rhs
216	)
217{
218	vint64 res;
219
220	res = *lhs;
221#if defined(HAVE_INT64)
222	res.q_s += rhs;
223#else
224	M_ADD(res.D_s.hi, res.D_s.lo,  -(rhs < 0), rhs);
225#endif
226	return res;
227}
228
229/* -------------------------------------------------------------------------*/
230
231vint64
232subv64i32(
233	const vint64 * lhs,
234	int32_t        rhs
235	)
236{
237	vint64 res;
238
239	res = *lhs;
240#if defined(HAVE_INT64)
241	res.q_s -= rhs;
242#else
243	M_SUB(res.D_s.hi, res.D_s.lo,  -(rhs < 0), rhs);
244#endif
245	return res;
246}
247
248/* -------------------------------------------------------------------------*/
249
250vint64
251addv64u32(
252	const vint64 * lhs,
253	uint32_t       rhs
254	)
255{
256	vint64 res;
257
258	res = *lhs;
259#if defined(HAVE_INT64)
260	res.Q_s += rhs;
261#else
262	M_ADD(res.D_s.hi, res.D_s.lo, 0, rhs);
263#endif
264	return res;
265}
266
267/* -------------------------------------------------------------------------*/
268
269vint64
270subv64u32(
271	const vint64 * lhs,
272	uint32_t       rhs
273	)
274{
275	vint64 res;
276
277	res = *lhs;
278#if defined(HAVE_INT64)
279	res.Q_s -= rhs;
280#else
281	M_SUB(res.D_s.hi, res.D_s.lo, 0, rhs);
282#endif
283	return res;
284}
285