From aa0c96d43d933c29e0e132bdc13fd1f15168155f Mon Sep 17 00:00:00 2001 From: des Date: Wed, 13 Feb 2008 13:29:40 +0000 Subject: [PATCH] Rewrite str2bytes, add unit test git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@2454 d4fa192b-c00b-0410-8231-f00ffab90ce4 --- varnish-cache/lib/libvarnish/Makefile.am | 12 ++ varnish-cache/lib/libvarnish/num.c | 163 ++++++++++++++++++----- 2 files changed, 141 insertions(+), 34 deletions(-) diff --git a/varnish-cache/lib/libvarnish/Makefile.am b/varnish-cache/lib/libvarnish/Makefile.am index 47c8e4dd..b035203f 100644 --- a/varnish-cache/lib/libvarnish/Makefile.am +++ b/varnish-cache/lib/libvarnish/Makefile.am @@ -23,3 +23,15 @@ libvarnish_la_SOURCES = \ libvarnish_la_CFLAGS = -include config.h libvarnish_la_LIBADD = ${RT_LIBS} ${NET_LIBS} + +TESTS = num_c_test + +if ENABLE_TESTS +noinst_PROGRAMS = ${TESTS} + +num_c_test_SOURCES = num.c +num_c_test_CFLAGS = -DNUM_C_TEST -include config.h + +test: ${TESTS} + @for test in ${TESTS} ; do ./$${test} ; done +endif diff --git a/varnish-cache/lib/libvarnish/num.c b/varnish-cache/lib/libvarnish/num.c index 941aad47..bf16f0c8 100644 --- a/varnish-cache/lib/libvarnish/num.c +++ b/varnish-cache/lib/libvarnish/num.c @@ -30,8 +30,9 @@ * Deal with numbers with data storage suffix scaling */ -#include +#include #include +#include #include #include @@ -39,41 +40,135 @@ const char * str2bytes(const char *p, uintmax_t *r, uintmax_t rel) { - int i; - double l; - char suff[2]; - - i = sscanf(p, "%lg%1s", &l, suff); - - assert(i >= -1 && i <= 2); - - if (i < 1) - return ("Could not find any number"); - - if (l < 0.0) - return ("Negative numbers not allowed"); - - if (i == 2) { - switch (tolower(*suff)) { - case 'b': break; - case 'k': l *= ((uintmax_t)1 << 10); break; - case 'm': l *= ((uintmax_t)1 << 20); break; - case 'g': l *= ((uintmax_t)1 << 30); break; - case 't': l *= ((uintmax_t)1 << 40); break; - case 'p': l *= ((uintmax_t)1 << 50); break; - case 'e': l *= ((uintmax_t)1 << 60); break; - case '%': - /* Percentage of 'rel' arg */ - if (rel != 0) { - l *= 1e-2 * rel; - break; - } - /*FALLTHROUGH*/ - default: - return ("Unknown scaling suffix [bkmgtpe] allowed"); + double fval; + char *end; + + fval = strtod(p, &end); + if (end == p || !isfinite(fval)) + return ("Invalid number"); + + if (*end == '\0') { + *r = (uintmax_t)fval; + return (NULL); + } + + if (end[0] == '%' && end[1] == '\0') { + if (rel == 0) + return ("Absolute number required"); + fval *= rel / 100.0; + } else { + /* accept a space before the multiplier */ + if (end[0] == ' ' && end[1] != '\0') + ++end; + + switch (end[0]) { + case 'k': case 'K': + fval *= (uintmax_t)1 << 10; + ++end; + break; + case 'm': case 'M': + fval *= (uintmax_t)1 << 20; + ++end; + break; + case 'g': case 'G': + fval *= (uintmax_t)1 << 30; + ++end; + break; + case 't': case 'T': + fval *= (uintmax_t)1 << 40; + ++end; + break; + case 'p': case 'P': + fval *= (uintmax_t)1 << 50; + ++end; + break; + case 'e': case 'E': + fval *= (uintmax_t)1 << 60; + ++end; + break; } + + /* accept 'b' for 'bytes' */ + if (end[0] == 'b' || end[0] == 'B') + ++end; + + if (end[0] != '\0') + return ("Invalid suffix"); } - *r = (uintmax_t)(l + .5); + + /* intentionally not round(fval) to avoid need for -lm */ + *r = (uintmax_t)(fval + 0.5); return (NULL); } +#ifdef NUM_C_TEST +#include +#include +#include +#include + +struct test_case { + const char *str; + uintmax_t rel; + uintmax_t val; +} test_cases[] = { + { "1", (uintmax_t)0, (uintmax_t)1 }, + { "1B", (uintmax_t)0, (uintmax_t)1<<0 }, + { "1 B", (uintmax_t)0, (uintmax_t)1<<0 }, + { "1.3B", (uintmax_t)0, (uintmax_t)1 }, + { "1.7B", (uintmax_t)0, (uintmax_t)2 }, + + { "1024", (uintmax_t)0, (uintmax_t)1024 }, + { "1k", (uintmax_t)0, (uintmax_t)1<<10 }, + { "1kB", (uintmax_t)0, (uintmax_t)1<<10 }, + { "1.3kB", (uintmax_t)0, (uintmax_t)1331 }, + { "1.7kB", (uintmax_t)0, (uintmax_t)1741 }, + + { "1048576", (uintmax_t)0, (uintmax_t)1048576 }, + { "1M", (uintmax_t)0, (uintmax_t)1<<20 }, + { "1MB", (uintmax_t)0, (uintmax_t)1<<20 }, + { "1.3MB", (uintmax_t)0, (uintmax_t)1363149 }, + { "1.7MB", (uintmax_t)0, (uintmax_t)1782579 }, + + { "1073741824", (uintmax_t)0, (uintmax_t)1073741824 }, + { "1G", (uintmax_t)0, (uintmax_t)1<<30 }, + { "1GB", (uintmax_t)0, (uintmax_t)1<<30 }, + { "1.3GB", (uintmax_t)0, (uintmax_t)1395864371 }, + { "1.7GB", (uintmax_t)0, (uintmax_t)1825361101 }, + + { "1099511627776", (uintmax_t)0, (uintmax_t)1099511627776 }, + { "1T", (uintmax_t)0, (uintmax_t)1<<40 }, + { "1TB", (uintmax_t)0, (uintmax_t)1<<40 }, + { "1.3TB", (uintmax_t)0, (uintmax_t)1429365116109 }, + { "1.7TB", (uintmax_t)0, (uintmax_t)1869169767219 }, + + { "1%", (uintmax_t)1024, (uintmax_t)10 }, + { "2%", (uintmax_t)1024, (uintmax_t)20 }, + { "3%", (uintmax_t)1024, (uintmax_t)31 }, + /* TODO: add more */ + + { 0, 0, 0 }, +}; + +int +main(int argc, char *argv[]) +{ + struct test_case *tc; + uintmax_t val; + int ec; + + (void)argc; + for (ec = 0, tc = test_cases; tc->str; ++tc) { + str2bytes(tc->str, &val, tc->rel); + if (val != tc->val) { + printf("%s: str2bytes(\"%s\", %ju) %ju != %ju\n", + *argv, tc->str, tc->rel, val, tc->val); + ++ec; + } + } + /* TODO: test invalid strings */ + if (!ec) + printf("OK\n"); + return (ec > 0); +} +#endif -- 2.39.5