varnish-cache/lib/libvarnish/vnum.c
1
/*-
2
 * Copyright (c) 2008-2009 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 *
28
 * Deal with numbers with data storage suffix scaling
29
 */
30
31
#include "config.h"
32
33
#include <ctype.h>
34
#include <math.h>
35
#include <stdint.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
39
#include "vdef.h"
40
41
#include "vnum.h"
42
#include "vas.h"
43
44
static const char err_miss_num[] = "Missing number";
45
static const char err_invalid_num[] = "Invalid number";
46
static const char err_abs_req[] = "Absolute number required";
47
static const char err_invalid_suff[] = "Invalid suffix";
48
49
/**********************************************************************
50
 * Convert (all of!) a string to a floating point number, and if we can
51
 * not, return NAN.
52
 */
53
54
double
55 202049
VNUMpfx(const char *p, const char **t)
56
{
57 202049
        double m = 0., ee = 0.;
58 202049
        double ms = 1.0;
59 202049
        double es = 1.0, e = 1.0, ne = 0.0;
60
61 202049
        AN(p);
62 202049
        AN(t);
63 202049
        *t = NULL;
64 404101
        while (isspace(*p))
65 3
                p++;
66
67 202049
        if (*p == '-' || *p == '+')
68 20
                ms = (*p++ == '-' ? -1.0 : 1.0);
69
70 855042
        for (; *p != '\0'; p++) {
71 735002
                if (isdigit(*p)) {
72 569743
                        m = m * 10. + *p - '0';
73 569743
                        e = ne;
74 569743
                        if (e)
75 233664
                                ne = e - 1.0;
76 165259
                } else if (*p == '.' && ne == 0.0) {
77 83250
                        ne = -1.0;
78
                } else
79
                        break;
80
        }
81 202049
        if (e > 0.0)
82 45
                return(nan(""));                // No digits
83 202004
        if (*p == 'e' || *p == 'E') {
84 3527
                p++;
85 3527
                if (*p == '-' || *p == '+')
86 1408
                        es = (*p++ == '-' ? -1.0 : 1.0);
87 3527
                if (!isdigit(*p))
88 3
                        return (nan(""));
89 7051
                for (; isdigit(*p); p++)
90 3527
                        ee = ee * 10. + *p - '0';
91
        }
92 404008
        while (isspace(*p))
93 6
                p++;
94 202001
        if (*p != '\0')
95 78439
                *t = p;
96 202001
        return (ms * m * pow(10., e + es * ee));
97
}
98
99
double
100 118606
VNUM(const char *p)
101
{
102
        const char *t;
103
        double r;
104
105 118606
        r = VNUMpfx(p, &t);
106 118606
        if (t != NULL)
107 11
                r = nan("");
108 118606
        return (r);
109
}
110
111
/**********************************************************************/
112
113
double
114 16
VNUM_duration(const char *p)
115
{
116
        const char *t;
117 16
        double r, sc = 1.0;
118
119 16
        if (p == NULL)
120 1
                return (nan(""));
121
122 15
        r = VNUMpfx(p, &t);
123
124 15
        if (isnan(r) || t == NULL)
125 3
                return (nan(""));
126
127 24
        while (isspace(*t))
128 0
                t++;
129
130
        // keep in sync with vcc_expr.c vcc_TimeUnit()
131 12
        switch (*t++) {
132
        case 's':
133 2
                break;
134
        case 'm':
135 2
                if (*t == 's') {
136 1
                        sc = 1e-3;
137 1
                        t++;
138
                } else
139 1
                        sc = 60.0;
140 2
                break;
141
        case 'h':
142 2
                sc = 60.0 * 60.0;
143 2
                break;
144
        case 'd':
145 2
                sc = 60.0 * 60.0 * 24.0;
146 2
                break;
147
        case 'w':
148 2
                sc = 60.0 * 60.0 * 24.0 * 7.0;
149 2
                break;
150
        case 'y':
151 1
                sc = 60.0 * 60.0 * 24.0 * 365.0;
152 1
                break;
153
        default:
154 1
                return (nan(""));
155
        }
156
157 24
        while (isspace(*t))
158 2
                t++;
159
160 11
        if (*t != '\0')
161 2
                return (nan(""));
162
163 9
        return (r * sc);
164
}
165
166
/**********************************************************************/
167
168
const char *
169 83351
VNUM_2bytes(const char *p, uintmax_t *r, uintmax_t rel)
170
{
171
        double fval;
172
        const char *end;
173
174 83351
        if (p == NULL || *p == '\0')
175 1
                return (err_miss_num);
176
177 83350
        fval = VNUMpfx(p, &end);
178 83350
        if (isnan(fval))
179 2
                return (err_invalid_num);
180
181 83348
        if (end == NULL) {
182 4935
                *r = (uintmax_t)fval;
183 4935
                return (NULL);
184
        }
185
186 78413
        if (end[0] == '%' && end[1] == '\0') {
187 4
                if (rel == 0)
188 1
                        return (err_abs_req);
189 3
                fval *= rel / 100.0;
190
        } else {
191
                /* accept a space before the multiplier */
192 78409
                if (end[0] == ' ' && end[1] != '\0')
193 0
                        ++end;
194
195 78409
                switch (end[0]) {
196
                case 'k': case 'K':
197 31632
                        fval *= (uintmax_t)1 << 10;
198 31632
                        ++end;
199 31632
                        break;
200
                case 'm': case 'M':
201 13129
                        fval *= (uintmax_t)1 << 20;
202 13129
                        ++end;
203 13129
                        break;
204
                case 'g': case 'G':
205 12564
                        fval *= (uintmax_t)1 << 30;
206 12564
                        ++end;
207 12564
                        break;
208
                case 't': case 'T':
209 4
                        fval *= (uintmax_t)1 << 40;
210 4
                        ++end;
211 4
                        break;
212
                case 'p': case 'P':
213 3
                        fval *= (uintmax_t)1 << 50;
214 3
                        ++end;
215 3
                        break;
216
                default:
217 21077
                        break;
218
                }
219
220
                /* [bB] is a generic suffix of no effect */
221 78409
                if (end[0] == 'b' || end[0] == 'B')
222 21091
                        end++;
223
224 78409
                if (end[0] != '\0')
225 2
                        return (err_invalid_suff);
226
        }
227
228 78410
        *r = (uintmax_t)round(fval);
229 78410
        return (NULL);
230
}
231
232
#ifdef NUM_C_TEST
233
/* Compile with: "cc -o foo -DNUM_C_TEST -I../.. -I../../include num.c -lm" */
234
235
static struct test_case {
236
        const char *str;
237
        uintmax_t rel;
238
        uintmax_t val;
239
        const char *err;
240
} test_cases[] = {
241
        { "1",                  (uintmax_t)0,   (uintmax_t)1 },
242
        { "1B",                 (uintmax_t)0,   (uintmax_t)1<<0 },
243
        { "1 B",                (uintmax_t)0,   (uintmax_t)1<<0 },
244
        { "1.3B",               (uintmax_t)0,   (uintmax_t)1 },
245
        { "1.7B",               (uintmax_t)0,   (uintmax_t)2 },
246
247
        { "1024",               (uintmax_t)0,   (uintmax_t)1024 },
248
        { "1k",                 (uintmax_t)0,   (uintmax_t)1<<10 },
249
        { "1kB",                (uintmax_t)0,   (uintmax_t)1<<10 },
250
        { "1.3kB",              (uintmax_t)0,   (uintmax_t)1331 },
251
        { "1.7kB",              (uintmax_t)0,   (uintmax_t)1741 },
252
253
        { "1048576",            (uintmax_t)0,   (uintmax_t)1048576 },
254
        { "1M",                 (uintmax_t)0,   (uintmax_t)1<<20 },
255
        { "1MB",                (uintmax_t)0,   (uintmax_t)1<<20 },
256
        { "1.3MB",              (uintmax_t)0,   (uintmax_t)1363149 },
257
        { "1.7MB",              (uintmax_t)0,   (uintmax_t)1782579 },
258
259
        { "1073741824",         (uintmax_t)0,   (uintmax_t)1073741824 },
260
        { "1G",                 (uintmax_t)0,   (uintmax_t)1<<30 },
261
        { "1GB",                (uintmax_t)0,   (uintmax_t)1<<30 },
262
        { "1.3GB",              (uintmax_t)0,   (uintmax_t)1395864371 },
263
        { "1.7GB",              (uintmax_t)0,   (uintmax_t)1825361101 },
264
265
        { "1099511627776",      (uintmax_t)0,   (uintmax_t)1099511627776ULL },
266
        { "1T",                 (uintmax_t)0,   (uintmax_t)1<<40 },
267
        { "1TB",                (uintmax_t)0,   (uintmax_t)1<<40 },
268
        { "1.3TB",              (uintmax_t)0,   (uintmax_t)1429365116109ULL },
269
        { "1.7TB",              (uintmax_t)0,   (uintmax_t)1869169767219ULL },
270
271
        { "1125899906842624",   (uintmax_t)0,   (uintmax_t)1125899906842624ULL},
272
        { "1P",                 (uintmax_t)0,   (uintmax_t)1125899906842624ULL},
273
        { "1PB",                (uintmax_t)0,   (uintmax_t)1125899906842624ULL},
274
        { "1.3 PB",             (uintmax_t)0,   (uintmax_t)1463669878895411ULL},
275
276
        { "1%",                 (uintmax_t)1024,        (uintmax_t)10 },
277
        { "2%",                 (uintmax_t)1024,        (uintmax_t)20 },
278
        { "3%",                 (uintmax_t)1024,        (uintmax_t)31 },
279
280
        /* Check the error checks */
281
        { "",                   0,      0,      err_miss_num },
282
        { "m",                  0,      0,      err_invalid_num },
283
        { "4%",                 0,      0,      err_abs_req },
284
        { "3*",                 0,      0,      err_invalid_suff },
285
286
        /* TODO: add more */
287
288
        { 0, 0, 0 },
289
};
290
291
static const char *vec[] = {
292
        " 1",
293
        " 12",
294
        " 12.",
295
        " 12.3",
296
        " 12.34",
297
        " 12.34e-3",
298
        " 12.34e3",
299
        " 12.34e+3",
300
        " +12.34e-3",
301
        " -12.34e3",
302
        "N.",
303
        "N.12.",
304
        "N12..",
305
        "N12.,",
306
        "N12e,",
307
        "N12e+,",
308
        "N12ee,",
309
        "N1..2",
310
        "NA",
311
        "N1A",
312
        "Ne-3",
313
        NULL
314
};
315
316
int
317 1
main(int argc, char *argv[])
318
{
319 1
        int ec = 0;
320
        struct test_case *tc;
321
        uintmax_t val;
322
        const char **p;
323
        const char *e;
324
        double d1, d2;
325
326
        (void)argc;
327
328 22
        for (p = vec; *p != NULL; p++) {
329 21
                e = *p;
330 21
                d1 = VNUM(e + 1);
331 21
                if (*e == 'N') {
332 11
                        if (!isnan(d1)) {
333 0
                                ec++;
334 0
                                printf("VNUM(%s) not NAN (%g)\n", e + 1, d1);
335
                        }
336
                } else {
337 10
                        d2 = atof(e + 1);
338 10
                        if (isnan(d1)) {
339 0
                                printf("VNUM(%s) is NAN (%g)\n", e + 1, d1);
340 0
                                ec++;
341 10
                        } else if (fabs((d1 - d2) / d2) > 1e-15) {
342 0
                                printf("VNUM(%s) differs from atof() (%g)\n",
343
                                    e + 1, d1);
344 0
                                ec++;
345
                        }
346
                }
347
        }
348
349 37
        for (tc = test_cases; tc->str; ++tc) {
350 36
                e = VNUM_2bytes(tc->str, &val, tc->rel);
351 36
                if (e != tc->err) {
352 0
                        printf("%s: VNUM_2bytes(\"%s\", %ju) (%s) != (%s)\n",
353
                            *argv, tc->str, tc->rel, tc->err, e);
354 0
                        ++ec;
355 36
                } else if (e == NULL && val != tc->val) {
356 0
                        printf("%s: VNUM_2bytes(\"%s\", %ju) %ju != %ju (%s)\n",
357
                            *argv, tc->str, tc->rel, val, tc->val, e);
358 0
                        ++ec;
359
                }
360
        }
361 1
        if (!isnan(VNUM_duration(NULL))) {
362 0
                printf("%s: VNUM_Duration(NULL) fail\n", *argv);
363 0
                ++ec;
364
        }
365 1
        d1 = VNUM_duration(" 365.24219d ");
366 1
        if (d1 < 31556925.2159 || d1 > 31556925.2161) {
367 0
                printf("%s: VNUM_Duration() wrong: %g\n", *argv, d1);
368 0
                ++ec;
369
        }
370
        /* TODO: test invalid strings */
371 1
        if (!ec)
372 1
                printf("OK\n");
373 1
        return (ec > 0);
374
}
375
#endif