| | varnish-cache/lib/libvarnish/vnum.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2008-2009 Varnish Software AS |
2 |
|
* All rights reserved. |
3 |
|
* |
4 |
|
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk> |
5 |
|
* |
6 |
|
* SPDX-License-Identifier: BSD-2-Clause |
7 |
|
* |
8 |
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
* modification, are permitted provided that the following conditions |
10 |
|
* are met: |
11 |
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
* documentation and/or other materials provided with the distribution. |
16 |
|
* |
17 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
18 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
19 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
20 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
21 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
22 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
23 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
24 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
25 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
26 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
27 |
|
* SUCH DAMAGE. |
28 |
|
* |
29 |
|
* Deal with numbers. |
30 |
|
* |
31 |
|
*/ |
32 |
|
|
33 |
|
#include "config.h" |
34 |
|
|
35 |
|
#include <sys/types.h> |
36 |
|
|
37 |
|
#include <limits.h> |
38 |
|
#include <math.h> |
39 |
|
#include <stdint.h> |
40 |
|
#include <stdio.h> |
41 |
|
#include <stdlib.h> |
42 |
|
#include <string.h> |
43 |
|
|
44 |
|
#include "vdef.h" |
45 |
|
|
46 |
|
#include "vnum.h" |
47 |
|
#include "vas.h" |
48 |
|
#include "vct.h" |
49 |
|
|
50 |
|
/* The distinction between these two is used internally */ |
51 |
|
static const char err_invalid_num[] = "Invalid number"; |
52 |
|
static const char err_no_digits[] = "Invalid number"; |
53 |
|
|
54 |
|
static const char err_fatnum[] = "Too many digits"; |
55 |
|
|
56 |
|
static const char err_unknown_bytes[] = |
57 |
|
"Unknown BYTES unit of measurement ([KMGTP][B])"; |
58 |
|
|
59 |
|
static const char err_fractional_bytes[] = "Fractional BYTES not allowed"; |
60 |
|
|
61 |
|
#define BAIL(txt) \ |
62 |
|
do { \ |
63 |
|
if (errtxt != NULL) \ |
64 |
|
*errtxt = (txt); \ |
65 |
|
errno = EINVAL; \ |
66 |
|
return (retval); \ |
67 |
|
} while (0) |
68 |
|
|
69 |
|
/* |
70 |
|
* Internal function for parsing an integer with a limited |
71 |
|
* number of digits. |
72 |
|
*/ |
73 |
|
|
74 |
|
static int64_t |
75 |
15130320 |
sf_parse_int(const char **ipp, const char **errtxt, int *sign, int maxdig) |
76 |
|
{ |
77 |
15130320 |
int64_t retval = 0; |
78 |
15130320 |
int ndig = 0; |
79 |
|
|
80 |
15130320 |
AN(ipp); |
81 |
15130320 |
AN(*ipp); |
82 |
15130320 |
if (errtxt != NULL) |
83 |
15130309 |
*errtxt = NULL; |
84 |
15130320 |
*sign = 1; |
85 |
15130320 |
errno = 0; |
86 |
15131600 |
while (vct_isows(*(*ipp))) |
87 |
1280 |
(*ipp)++; |
88 |
15130300 |
if(*(*ipp) == '-') { |
89 |
2360 |
*sign = -1; |
90 |
2360 |
(*ipp)++; |
91 |
2360 |
} |
92 |
45208801 |
while (vct_isdigit(*(*ipp))) { |
93 |
30078941 |
ndig++; |
94 |
30078941 |
if (ndig > maxdig) |
95 |
440 |
BAIL(err_fatnum); |
96 |
30078501 |
retval *= 10; |
97 |
30078501 |
retval += *(*ipp)++ - 0x30; |
98 |
|
} |
99 |
15129860 |
if (ndig == 0) |
100 |
95439 |
BAIL(err_no_digits); |
101 |
15034701 |
while (vct_isows(*(*ipp))) |
102 |
280 |
(*ipp)++; |
103 |
15034421 |
return (retval); |
104 |
15130300 |
} |
105 |
|
|
106 |
|
/********************************************************************** |
107 |
|
* Parse a RFC8941 `sf-integer`. |
108 |
|
* |
109 |
|
* If `errno` is non-zero the conversion failed. |
110 |
|
* If `errtxt` is provided it summarily tells why. |
111 |
|
* The input argument points to the first character not consumed. |
112 |
|
*/ |
113 |
|
|
114 |
|
int64_t |
115 |
1560 |
SF_Parse_Integer(const char **ipp, const char **errtxt) |
116 |
|
{ |
117 |
|
int64_t retval; |
118 |
|
int sign; |
119 |
|
|
120 |
1560 |
retval = sf_parse_int(ipp, errtxt, &sign, 15); |
121 |
1560 |
return (retval * sign); |
122 |
|
} |
123 |
|
|
124 |
|
/********************************************************************** |
125 |
|
* Parse either a RFC8941 `sf-integer` or `sf-decimal`. |
126 |
|
* |
127 |
|
* If `errno` is non-zero the conversion failed. |
128 |
|
* If `errtxt` is provided it summarily tells why. |
129 |
|
* The input argument points to the first character not consumed. |
130 |
|
*/ |
131 |
|
|
132 |
|
double |
133 |
15128264 |
SF_Parse_Number(const char **ipp, int strict, const char **errtxt) |
134 |
|
{ |
135 |
15128264 |
double retval, scale = 1; |
136 |
|
int sign, ndig; |
137 |
|
|
138 |
15128264 |
retval = (double)sf_parse_int(ipp, errtxt, &sign, 15); |
139 |
15128264 |
if (strict && errno) |
140 |
400 |
return (0); |
141 |
15127864 |
if (*(*ipp) != '.') |
142 |
9264350 |
return (retval * sign); |
143 |
5863514 |
if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX) |
144 |
44 |
BAIL(err_fatnum); |
145 |
5863470 |
if (*errtxt == err_no_digits && (!vct_isdigit((*ipp)[1]))) |
146 |
120 |
BAIL(err_no_digits); |
147 |
5863350 |
*errtxt = NULL; |
148 |
5863350 |
errno = 0; |
149 |
5863350 |
do { |
150 |
5863350 |
(*ipp)++; |
151 |
22396259 |
for(ndig = 0; ndig < 3; ndig++) { |
152 |
17408898 |
scale *= .1; |
153 |
17408898 |
if (!vct_isdigit(*(*ipp))) |
154 |
875989 |
break; |
155 |
16532909 |
retval += scale * (*(*ipp)++ - 0x30); |
156 |
16532909 |
} |
157 |
5863350 |
if (strict && ndig == 0) |
158 |
80 |
BAIL(err_invalid_num); |
159 |
5863270 |
if (strict && vct_isdigit(*(*ipp))) |
160 |
120 |
BAIL(err_fatnum); |
161 |
5863630 |
while (vct_isdigit(*(*ipp))) |
162 |
480 |
(*ipp)++; |
163 |
5863150 |
} while (0); |
164 |
5863870 |
while (vct_isows(*(*ipp))) |
165 |
720 |
(*ipp)++; |
166 |
5863150 |
return (retval * sign); |
167 |
15128260 |
} |
168 |
|
|
169 |
|
/********************************************************************** |
170 |
|
* Parse a RFC8941 `sf-decimal`. |
171 |
|
* |
172 |
|
* If `errno` is non-zero the conversion failed. |
173 |
|
* If `errtxt` is provided it summarily tells why. |
174 |
|
* The input argument points to the first character not consumed. |
175 |
|
*/ |
176 |
|
|
177 |
|
double |
178 |
8745320 |
SF_Parse_Decimal(const char **ipp, int strict, const char **errtxt) |
179 |
|
{ |
180 |
|
double retval; |
181 |
|
|
182 |
8745320 |
retval = SF_Parse_Number(ipp, strict, errtxt); |
183 |
8745320 |
if (errno) |
184 |
160 |
return (retval); |
185 |
8745160 |
if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX) |
186 |
0 |
BAIL(err_fatnum); |
187 |
8745160 |
return (retval); |
188 |
8745320 |
} |
189 |
|
|
190 |
|
/********************************************************************** |
191 |
|
* Parse a "Varnish number". |
192 |
|
* |
193 |
|
* Varnish numbers are the union of RFC8941 sf-integer and sf-decimal. |
194 |
|
* If `errno` is non-zero the conversion failed and NAN is returned. |
195 |
|
*/ |
196 |
|
|
197 |
|
double |
198 |
35315 |
VNUM(const char *p) |
199 |
|
{ |
200 |
|
const char *t; |
201 |
|
double r; |
202 |
|
|
203 |
35315 |
r = SF_Parse_Number(&p, 0, &t); |
204 |
35315 |
if (errno || *p != '\0') |
205 |
2560 |
r = nan(""); |
206 |
35315 |
return (r); |
207 |
|
} |
208 |
|
|
209 |
|
/**********************************************************************/ |
210 |
|
|
211 |
|
vtim_dur |
212 |
888796 |
VNUM_duration_unit(vtim_dur r, const char *b, const char *e) |
213 |
|
{ |
214 |
|
double sc; |
215 |
|
|
216 |
888796 |
if (e == NULL) |
217 |
656476 |
e = strchr(b, '\0'); |
218 |
|
|
219 |
888796 |
while (b < e && vct_issp(*b)) |
220 |
0 |
b++; |
221 |
888796 |
if (b == e) |
222 |
160 |
return (nan("")); |
223 |
|
|
224 |
888636 |
switch (*b++) { |
225 |
|
case 's': |
226 |
598076 |
sc = 1.0; |
227 |
598076 |
break; |
228 |
|
case 'm': |
229 |
43240 |
if (b < e && *b == 's') { |
230 |
840 |
sc = 1e-3; |
231 |
840 |
b++; |
232 |
840 |
} else |
233 |
42400 |
sc = 60.0; |
234 |
43240 |
break; |
235 |
|
case 'h': |
236 |
245680 |
sc = 60.0 * 60.0; |
237 |
245680 |
break; |
238 |
|
case 'd': |
239 |
720 |
sc = 60.0 * 60.0 * 24.0; |
240 |
720 |
break; |
241 |
|
case 'w': |
242 |
320 |
sc = 60.0 * 60.0 * 24.0 * 7.0; |
243 |
320 |
break; |
244 |
|
case 'y': |
245 |
320 |
sc = 60.0 * 60.0 * 24.0 * 365.0; |
246 |
320 |
break; |
247 |
|
default: |
248 |
280 |
return (nan("")); |
249 |
|
} |
250 |
|
|
251 |
888436 |
while (b < e && vct_issp(*b)) |
252 |
80 |
b++; |
253 |
|
|
254 |
888356 |
if (b < e) |
255 |
80 |
return (nan("")); |
256 |
|
|
257 |
888276 |
return (r * sc); |
258 |
888796 |
} |
259 |
|
|
260 |
|
vtim_dur |
261 |
3396 |
VNUM_duration(const char *p) |
262 |
|
{ |
263 |
|
const char *t; |
264 |
|
vtim_dur r; |
265 |
|
|
266 |
3396 |
if (p == NULL) |
267 |
560 |
return (nan("")); |
268 |
|
|
269 |
2836 |
r = SF_Parse_Number(&p, 0, &t); |
270 |
|
|
271 |
2836 |
if (errno) |
272 |
80 |
return (nan("")); |
273 |
|
|
274 |
2756 |
return (VNUM_duration_unit(r, p, NULL)); |
275 |
3396 |
} |
276 |
|
|
277 |
|
/**********************************************************************/ |
278 |
|
|
279 |
|
int64_t |
280 |
6341080 |
VNUM_bytes_unit(double r, const char *b, const char *e, uintmax_t rel, |
281 |
|
const char **errtxt) |
282 |
|
{ |
283 |
6341080 |
double sc = 1.0, tmp; |
284 |
|
|
285 |
6341080 |
AN(b); |
286 |
6341080 |
AN(errtxt); |
287 |
6341080 |
errno = 0; |
288 |
6341080 |
if (e == NULL) |
289 |
6339200 |
e = strchr(b, '\0'); |
290 |
|
|
291 |
6341080 |
while (b < e && vct_issp(*b)) |
292 |
0 |
b++; |
293 |
6341080 |
if (b == e) { |
294 |
286880 |
if (modf(r, &tmp) != 0.0) { |
295 |
40 |
*errtxt = err_fractional_bytes; |
296 |
40 |
errno = EINVAL; |
297 |
40 |
} |
298 |
286880 |
return ((int64_t)trunc(sc * r)); |
299 |
|
} |
300 |
|
|
301 |
6054200 |
if (rel != 0 && *b == '%') { |
302 |
160 |
r *= rel * 0.01; |
303 |
160 |
b++; |
304 |
160 |
} else { |
305 |
6054040 |
switch (*b) { |
306 |
2326680 |
case 'k': case 'K': sc = exp2(10); b++; break; |
307 |
761680 |
case 'm': case 'M': sc = exp2(20); b++; break; |
308 |
605480 |
case 'g': case 'G': sc = exp2(30); b++; break; |
309 |
240 |
case 't': case 'T': sc = exp2(40); b++; break; |
310 |
160 |
case 'p': case 'P': sc = exp2(50); b++; break; |
311 |
|
case 'b': case 'B': |
312 |
2359560 |
if (modf(r, &tmp) != 0.0) { |
313 |
80 |
*errtxt = err_fractional_bytes; |
314 |
80 |
errno = EINVAL; |
315 |
80 |
return (0); |
316 |
|
} |
317 |
2359480 |
break; |
318 |
|
default: |
319 |
240 |
*errtxt = err_unknown_bytes; |
320 |
240 |
errno = EINVAL; |
321 |
240 |
return (0); |
322 |
|
} |
323 |
6053720 |
if (b < e && (*b == 'b' || *b == 'B')) |
324 |
2360800 |
b++; |
325 |
|
} |
326 |
6053960 |
while (b < e && vct_issp(*b)) |
327 |
80 |
b++; |
328 |
6053880 |
if (b < e) { |
329 |
0 |
*errtxt = err_unknown_bytes; |
330 |
0 |
errno = EINVAL; |
331 |
0 |
return (0); |
332 |
|
} |
333 |
6053880 |
return ((int64_t)trunc(sc * r)); |
334 |
6341080 |
} |
335 |
|
|
336 |
|
const char * |
337 |
6340000 |
VNUM_2bytes(const char *p, uintmax_t *r, uintmax_t rel) |
338 |
|
{ |
339 |
|
double fval; |
340 |
|
const char *errtxt; |
341 |
|
|
342 |
6340000 |
if (p == NULL || *p == '\0') |
343 |
440 |
return (err_invalid_num); |
344 |
|
|
345 |
6339560 |
fval = SF_Parse_Number(&p, 1, &errtxt); |
346 |
6339560 |
if (errno) |
347 |
320 |
return (errtxt); |
348 |
6339240 |
if (fval < 0) |
349 |
40 |
return (err_invalid_num); |
350 |
|
|
351 |
6339200 |
fval = VNUM_bytes_unit(fval, p, NULL, rel, &errtxt); |
352 |
6339200 |
if (errno) |
353 |
320 |
return (errtxt); |
354 |
6338880 |
*r = (uintmax_t)round(fval); |
355 |
6338880 |
return (NULL); |
356 |
6340000 |
} |
357 |
|
|
358 |
|
/**********************************************************************/ |
359 |
|
|
360 |
|
static const uint8_t hex_table[] = { |
361 |
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, |
362 |
|
127, 127, 127, 127, 127, 127, 127, 10, 11, 12, |
363 |
|
13, 14, 15, 127, 127, 127, 127, 127, 127, 127, |
364 |
|
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, |
365 |
|
127, 127, 127, 127, 127, 127, 127, 127, 127, 10, |
366 |
|
11, 12, 13, 14, 15 |
367 |
|
}; |
368 |
|
|
369 |
|
static ssize_t |
370 |
276088 |
vnum_uint(const char *b, const char *e, const char **p, unsigned base) |
371 |
|
{ |
372 |
|
ssize_t u; |
373 |
|
unsigned n; |
374 |
|
|
375 |
276088 |
AN(b); |
376 |
276088 |
AN(p); |
377 |
276088 |
if (e == NULL) |
378 |
266648 |
e = strchr(b, '\0'); |
379 |
|
|
380 |
276088 |
u = 0; |
381 |
276088 |
if (!vct_ishex(*b) || hex_table[*b - '0'] >= base) { |
382 |
644 |
*p = b; |
383 |
644 |
return (-1); |
384 |
|
} |
385 |
|
|
386 |
726800 |
for (; b < e && vct_ishex(*b) && hex_table[*b - '0'] < base; b++) { |
387 |
451396 |
if (u > (SSIZE_MAX / base)) { |
388 |
0 |
u = -2; |
389 |
0 |
break; |
390 |
|
} |
391 |
451396 |
u *= base; |
392 |
451396 |
n = hex_table[*b - '0']; |
393 |
451396 |
if (u > (SSIZE_MAX - n)) { |
394 |
40 |
u = -2; |
395 |
40 |
break; |
396 |
|
} |
397 |
451356 |
u += n; |
398 |
451356 |
} |
399 |
|
|
400 |
275444 |
*p = b; |
401 |
275444 |
return (u); |
402 |
276088 |
} |
403 |
|
|
404 |
|
ssize_t |
405 |
266648 |
VNUM_uint(const char *b, const char *e, const char **p) |
406 |
|
{ |
407 |
|
|
408 |
266648 |
return (vnum_uint(b, e, p, 10)); |
409 |
|
} |
410 |
|
|
411 |
|
ssize_t |
412 |
9440 |
VNUM_hex(const char *b, const char *e, const char **p) |
413 |
|
{ |
414 |
|
|
415 |
9440 |
return (vnum_uint(b, e, p, 16)); |
416 |
|
} |
417 |
|
|
418 |
|
#ifdef NUM_C_TEST |
419 |
|
/* |
420 |
|
* Compile with: |
421 |
|
* cc -o foo -DNUM_C_TEST -DTEST_VERBOSE \ |
422 |
|
* -I../.. -I../../include vnum.c vas.c vct.c -lm |
423 |
|
*/ |
424 |
|
|
425 |
|
static const struct test_sf_parse_int { |
426 |
|
const char *input; |
427 |
|
int maxdig; |
428 |
|
int64_t retval; |
429 |
|
int consumed; |
430 |
|
int sign; |
431 |
|
const char *errtxt; |
432 |
|
} test_sf_parse_int[] = { |
433 |
|
{ "1234", 3, 123, 3, 1, err_fatnum }, |
434 |
|
{ "1234", 4, 1234, 4, 1, NULL }, |
435 |
|
{ "1234", 5, 1234, 4, 1, NULL }, |
436 |
|
{ "-", 5, 0, 1, -1, err_no_digits }, |
437 |
|
{ " ", 5, 0, 2, 1, err_no_digits }, |
438 |
|
{ "-1234", 3, 123, 4, -1, err_fatnum }, |
439 |
|
{ "-1234", 4, 1234, 5, -1, NULL }, |
440 |
|
{ "-1234", 5, 1234, 5, -1, NULL }, |
441 |
|
{ " -1234", 5, 1234, 6, -1, NULL }, |
442 |
|
{ " -1234 ", 5, 1234, 7, -1, NULL }, |
443 |
|
{ " -12 34 ", 5, 12, 5, -1, NULL }, |
444 |
|
{ " - 12 34 ", 5, 0, 2, -1, err_no_digits }, |
445 |
|
{ NULL}, |
446 |
|
}; |
447 |
|
|
448 |
|
static const struct test_sf_parse_number { |
449 |
|
const char *input; |
450 |
|
int strict; |
451 |
|
double retval; |
452 |
|
int consumed; |
453 |
|
const char *errtxt; |
454 |
|
} test_sf_parse_number[] = { |
455 |
|
{ "1234", 1, 1234.000, 4, NULL }, |
456 |
|
{ " 1234", 1, 1234.000, 5, NULL }, |
457 |
|
{ " 1234 ", 1, 1234.000, 6, NULL }, |
458 |
|
{ " 1234. ", 1, 1234.000, 6, err_invalid_num }, |
459 |
|
{ " 123456789012.0 ", 1, 123456789012.000, 16, NULL }, |
460 |
|
{ " 1234567890123.0 ", 1, 1234567890123.000, 14, err_fatnum }, |
461 |
|
{ " 123456789012.123 ", 1, 123456789012.123, 18, NULL }, |
462 |
|
{ " 123456789012.1234 ",1, 123456789012.123, 17, err_fatnum }, |
463 |
|
{ " -0.123456 ", 1, .123, 7, err_fatnum }, |
464 |
|
{ " -.123456 ", 1, 0., 2, err_no_digits }, |
465 |
|
{ " .123456 ", 1, 0., 1, err_no_digits }, |
466 |
|
{ " 0. ", 1, 0., 3, err_invalid_num }, |
467 |
|
{ " .0 ", 1, 0., 1, err_no_digits }, |
468 |
|
|
469 |
|
{ " 123456789012.1234 ",0, 123456789012.123, 19, NULL }, |
470 |
|
{ " -0.123456 ", 0, -.123, 11, NULL }, |
471 |
|
{ " -.123456 ", 0, -.123, 10, NULL }, |
472 |
|
{ " .123456 ", 0, .123, 9, NULL }, |
473 |
|
{ " 0. ", 0, 0., 4, NULL }, |
474 |
|
{ " .0 ", 0, 0., 4, NULL }, |
475 |
|
{ " -0. ", 0, -0., 5, NULL }, |
476 |
|
{ " -.0 ", 0, -0., 5, NULL }, |
477 |
|
{ " - ", 0, -0., 2, err_no_digits }, |
478 |
|
{ " -. ", 0, 0., 2, err_no_digits }, |
479 |
|
{ " . ", 0, 0., 1, err_no_digits }, |
480 |
|
{ NULL}, |
481 |
|
}; |
482 |
|
|
483 |
|
static struct test_case { |
484 |
|
const char *str; |
485 |
|
uintmax_t rel; |
486 |
|
uintmax_t val; |
487 |
|
const char *err; |
488 |
|
} test_vnum_2bytes[] = { |
489 |
|
{ "1", (uintmax_t)0, (uintmax_t)1 }, |
490 |
|
{ "1B", (uintmax_t)0, (uintmax_t)1<<0 }, |
491 |
|
{ "1 B", (uintmax_t)0, (uintmax_t)1<<0 }, |
492 |
|
{ "1.3B", 0, 0, err_fractional_bytes }, |
493 |
|
{ "1.7B", 0, 0, err_fractional_bytes }, |
494 |
|
|
495 |
|
{ "1024", (uintmax_t)0, (uintmax_t)1024 }, |
496 |
|
{ "1k", (uintmax_t)0, (uintmax_t)1<<10 }, |
497 |
|
{ "1kB", (uintmax_t)0, (uintmax_t)1<<10 }, |
498 |
|
{ "0.75kB", (uintmax_t)0, (uintmax_t)768 }, |
499 |
|
{ "1.3kB", (uintmax_t)0, (uintmax_t)1331 }, |
500 |
|
{ "1.70kB", (uintmax_t)0, (uintmax_t)1740 }, |
501 |
|
|
502 |
|
{ "1048576", (uintmax_t)0, (uintmax_t)1048576 }, |
503 |
|
{ "1M", (uintmax_t)0, (uintmax_t)1<<20 }, |
504 |
|
{ "1MB", (uintmax_t)0, (uintmax_t)1<<20 }, |
505 |
|
{ "1.3MB", (uintmax_t)0, (uintmax_t)1363148 }, |
506 |
|
{ "1.700MB", (uintmax_t)0, (uintmax_t)1782579 }, |
507 |
|
|
508 |
|
{ "1073741824", (uintmax_t)0, (uintmax_t)1073741824 }, |
509 |
|
{ "1G", (uintmax_t)0, (uintmax_t)1<<30 }, |
510 |
|
{ "1GB", (uintmax_t)0, (uintmax_t)1<<30 }, |
511 |
|
{ "1.3GB", (uintmax_t)0, (uintmax_t)1395864371 }, |
512 |
|
{ "1.7GB", (uintmax_t)0, (uintmax_t)1825361100 }, |
513 |
|
|
514 |
|
{ "1099511627776", (uintmax_t)0, (uintmax_t)1099511627776ULL }, |
515 |
|
{ "1T", (uintmax_t)0, (uintmax_t)1<<40 }, |
516 |
|
{ "1TB", (uintmax_t)0, (uintmax_t)1<<40 }, |
517 |
|
{ "1.3TB", (uintmax_t)0, (uintmax_t)1429365116108ULL }, |
518 |
|
{ "1.7\tTB", (uintmax_t)0, (uintmax_t)1869169767219ULL }, |
519 |
|
|
520 |
|
{ "999999999999999", (uintmax_t)0, (uintmax_t)999999999999999ULL}, |
521 |
|
|
522 |
|
{ "1125899906842624", 0, 0, err_fatnum }, |
523 |
|
{ "1P\t", (uintmax_t)0, (uintmax_t)1125899906842624ULL}, |
524 |
|
{ "1PB ", (uintmax_t)0, (uintmax_t)1125899906842624ULL}, |
525 |
|
{ "1.3 PB", (uintmax_t)0, (uintmax_t)1463669878895411ULL}, |
526 |
|
|
527 |
|
{ "1.5%", (uintmax_t)1024, (uintmax_t)15 }, |
528 |
|
{ "1.501%", (uintmax_t)1024, (uintmax_t)15 }, |
529 |
|
{ "2%", (uintmax_t)1024, (uintmax_t)20 }, |
530 |
|
{ "3%", (uintmax_t)1024, (uintmax_t)30 }, |
531 |
|
|
532 |
|
/* 32bit limits */ |
533 |
|
{ "4294967295b", (uintmax_t)0, (uintmax_t)4294967295ULL}, |
534 |
|
{ "4294967294b", (uintmax_t)0, (uintmax_t)4294967294ULL}, |
535 |
|
|
536 |
|
/* Check the error checks */ |
537 |
|
{ "", 0, 0, err_invalid_num }, |
538 |
|
{ "-1", 0, 0, err_invalid_num }, |
539 |
|
{ "1.3", 0, 0, err_fractional_bytes}, |
540 |
|
{ "1.5011%", 0, 0, err_fatnum }, |
541 |
|
{ "-", 0, 0, err_no_digits }, |
542 |
|
{ "m", 0, 0, err_no_digits }, |
543 |
|
{ "4%", 0, 0, err_unknown_bytes }, |
544 |
|
{ "3*", 0, 0, err_unknown_bytes }, |
545 |
|
|
546 |
|
/* TODO: add more */ |
547 |
|
|
548 |
|
{ 0, 0, 0 }, |
549 |
|
}; |
550 |
|
|
551 |
|
static const char *vec[] = { |
552 |
|
" 1", |
553 |
|
" 12", |
554 |
|
" 12.", |
555 |
|
" 12.3", |
556 |
|
" 12.34", |
557 |
|
"N12.34e-3", |
558 |
|
"N12.34e3", |
559 |
|
"N12.34e+3", |
560 |
|
"N+12.34e-3", |
561 |
|
"N-12.34e3", |
562 |
|
"N.", |
563 |
|
"N.12.", |
564 |
|
"N12..", |
565 |
|
"N12.,", |
566 |
|
"N12e,", |
567 |
|
"N12e+,", |
568 |
|
"N12ee,", |
569 |
|
"N1..2", |
570 |
|
"NA", |
571 |
|
"N1A", |
572 |
|
"Ne-3", |
573 |
|
NULL |
574 |
|
}; |
575 |
|
|
576 |
|
int |
577 |
40 |
main(int argc, char *argv[]) |
578 |
|
{ |
579 |
40 |
int ec = 0; |
580 |
|
struct test_case *tc; |
581 |
40 |
uintmax_t val = 0; |
582 |
|
const char **p; |
583 |
|
const char *e; |
584 |
|
double d1, d2; |
585 |
|
const struct test_sf_parse_int *tspi; |
586 |
|
const struct test_sf_parse_number *tspn; |
587 |
|
int64_t i64; |
588 |
|
volatile double dbl; |
589 |
|
int sign, consumed; |
590 |
|
const char *errtxt; |
591 |
|
const char *input; |
592 |
|
char buf1[30]; |
593 |
|
char buf2[30]; |
594 |
|
|
595 |
40 |
(void)argc; |
596 |
|
|
597 |
40 |
setbuf(stdout, NULL); |
598 |
40 |
setbuf(stderr, NULL); |
599 |
|
|
600 |
520 |
for (tspi = test_sf_parse_int; tspi->input != NULL; tspi++) { |
601 |
480 |
errtxt = "(unset)"; |
602 |
480 |
input = tspi->input; |
603 |
480 |
i64 = sf_parse_int(&input, &errtxt, &sign, tspi->maxdig); |
604 |
480 |
consumed = input - tspi->input; |
605 |
960 |
if (i64 != tspi->retval || |
606 |
480 |
sign != tspi->sign || |
607 |
480 |
consumed != tspi->consumed || |
608 |
480 |
errtxt != tspi->errtxt) { |
609 |
0 |
ec++; |
610 |
0 |
printf("sf_parse_int(%s, maxdig=%d) failed\n", |
611 |
0 |
tspi->input, tspi->maxdig); |
612 |
|
#ifdef TEST_VERBOSE |
613 |
|
printf(" retval\texpected %jd\tgot %jd\n", |
614 |
|
(intmax_t)tspi->retval, (intmax_t)i64); |
615 |
|
printf(" sign\texpected %d\tgot %d\n", |
616 |
|
tspi->sign, sign); |
617 |
|
printf(" consumed\texpected %d\tgot %d\n", |
618 |
|
tspi->consumed, consumed); |
619 |
|
printf(" errtxt\texpected %p\tgot %p\n", |
620 |
|
tspi->errtxt, errtxt); |
621 |
|
printf(" errtxt\texpected %s\tgot %s\n", |
622 |
|
tspi->errtxt, errtxt); |
623 |
|
#endif |
624 |
0 |
} |
625 |
480 |
} |
626 |
|
|
627 |
1000 |
for (tspn = test_sf_parse_number; tspn->input != NULL; tspn++) { |
628 |
960 |
errtxt = "(unset)"; |
629 |
960 |
input = tspn->input; |
630 |
960 |
dbl = SF_Parse_Number(&input, tspn->strict, &errtxt); |
631 |
960 |
consumed = input - tspn->input; |
632 |
960 |
bprintf(buf1, "%.4f", dbl); |
633 |
960 |
bprintf(buf2, "%.4f", tspn->retval); |
634 |
1920 |
if (strcmp(buf1, buf2) || |
635 |
960 |
consumed != tspn->consumed || |
636 |
960 |
errtxt != tspn->errtxt) { |
637 |
0 |
ec++; |
638 |
0 |
printf("sf_parse_number(%s, strict=%d) failed\n", |
639 |
0 |
tspn->input, tspn->strict); |
640 |
|
#ifdef TEST_VERBOSE |
641 |
|
printf(" retval\texpected %.4f\tgot %.4f\t(%e)\n", |
642 |
|
tspn->retval, dbl, dbl - tspn->retval); |
643 |
|
printf(" retval\texpected %a\tgot %a\n", |
644 |
|
tspn->retval, dbl); |
645 |
|
printf(" retval\texpected %s\tgot %s\n", |
646 |
|
buf2, buf1); |
647 |
|
printf(" retval\tdelta %e\n", |
648 |
|
dbl - tspn->retval); |
649 |
|
printf(" consumed\texpected %d\tgot %d\n", |
650 |
|
tspn->consumed, consumed); |
651 |
|
printf(" errtxt\texpected %p\tgot %p\n", |
652 |
|
tspn->errtxt, errtxt); |
653 |
|
printf(" errtxt\texpected %s\tgot %s\n", |
654 |
|
tspn->errtxt, errtxt); |
655 |
|
#endif |
656 |
0 |
} |
657 |
960 |
} |
658 |
|
|
659 |
880 |
for (p = vec; *p != NULL; p++) { |
660 |
840 |
e = *p; |
661 |
840 |
d1 = VNUM(e + 1); |
662 |
840 |
if (*e == 'N') { |
663 |
640 |
if (!isnan(d1)) { |
664 |
0 |
ec++; |
665 |
0 |
printf("VNUM(%s) not NAN (%g)\n", e + 1, d1); |
666 |
0 |
} |
667 |
640 |
} else { |
668 |
200 |
d2 = atof(e + 1); |
669 |
200 |
if (isnan(d1)) { |
670 |
0 |
printf("VNUM(%s) is NAN (%g)\n", e + 1, d1); |
671 |
0 |
ec++; |
672 |
200 |
} else if (fabs((d1 - d2) / d2) > 1e-15) { |
673 |
0 |
printf("VNUM(%s) differs from atof() (%g)\n", |
674 |
0 |
e + 1, d1); |
675 |
0 |
ec++; |
676 |
0 |
} |
677 |
|
} |
678 |
840 |
} |
679 |
|
|
680 |
1840 |
for (tc = test_vnum_2bytes; tc->str; ++tc) { |
681 |
1800 |
e = VNUM_2bytes(tc->str, &val, tc->rel); |
682 |
1800 |
if (e != NULL) |
683 |
440 |
val = 0; |
684 |
1800 |
if (e == tc->err && val == tc->val) |
685 |
1800 |
continue; |
686 |
0 |
++ec; |
687 |
0 |
printf("%s: VNUM_2bytes(\"%s\", %ju)\n", |
688 |
0 |
*argv, tc->str, tc->rel); |
689 |
0 |
printf("\tExpected:\tstatus %s - value %ju\n", |
690 |
0 |
tc->err ? tc->err : "Success", tc->val); |
691 |
0 |
printf("\tGot:\t\tstatus %s - value %ju\n", |
692 |
0 |
e ? e : "Success", val); |
693 |
0 |
} |
694 |
40 |
if (!isnan(VNUM_duration(NULL))) { |
695 |
0 |
printf("%s: VNUM_Duration(NULL) fail\n", *argv); |
696 |
0 |
++ec; |
697 |
0 |
} |
698 |
40 |
d1 = VNUM_duration(" 365.24219d "); |
699 |
40 |
d2 = 31556908.8; |
700 |
40 |
if (fabs(d1 - d2) > VNUM_EPSILON) { |
701 |
0 |
printf("%s: VNUM_Duration() wrong, %.3f delta = %e\n", |
702 |
0 |
*argv, d1, d1 - d2); |
703 |
0 |
++ec; |
704 |
0 |
} |
705 |
|
/* TODO: test invalid strings */ |
706 |
40 |
if (!ec) |
707 |
40 |
printf("OK\n"); |
708 |
40 |
return (ec > 0); |
709 |
|
} |
710 |
|
#endif |