[master] 05e10eee3 Fully implement and test non-strict mode SF_Parse()
Poul-Henning Kamp
phk at FreeBSD.org
Wed Jun 2 11:46:06 UTC 2021
commit 05e10eee3076e4c416ebd460cfa70f4fa8fcdb67
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date: Wed Jun 2 11:44:48 2021 +0000
Fully implement and test non-strict mode SF_Parse()
diff --git a/lib/libvarnish/vnum.c b/lib/libvarnish/vnum.c
index cf532eaf0..31b2edb7f 100644
--- a/lib/libvarnish/vnum.c
+++ b/lib/libvarnish/vnum.c
@@ -44,11 +44,15 @@
#include "vas.h"
#include "vct.h"
-static const char err_miss_num[] = "Missing number";
-static const char err_fatnum[] = "Too many digits";
+/* The distinction between these two is used internally */
static const char err_invalid_num[] = "Invalid number";
+static const char err_no_digits[] = "Invalid number";
+
+static const char err_fatnum[] = "Too many digits";
+
static const char err_unknown_bytes[] =
"Unknown BYTES unit of measurement ([KMGTP][B])";
+
static const char err_fractional_bytes[] = "Fractional BYTES not allowed";
#define BAIL(txt) \
@@ -84,7 +88,7 @@ sf_parse_int(const char **ipp, const char **errtxt, int *sign, int maxdig)
retval += *(*ipp)++ - 0x30;
}
if (ndig == 0)
- BAIL(*sign < 0 ? err_invalid_num : err_miss_num);
+ BAIL(err_no_digits);
while (vct_isows(*(*ipp)))
(*ipp)++;
return (retval);
@@ -109,11 +113,15 @@ SF_Parse_Number(const char **ipp, int strict, const char **errtxt)
retval = (double)sf_parse_int(ipp, errtxt, &sign, 15);
if (strict && errno)
return (0);
+ if (*(*ipp) != '.')
+ return (retval * sign);
+ if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
+ BAIL(err_fatnum);
+ if (*errtxt == err_no_digits && (!vct_isdigit((*ipp)[1])))
+ BAIL(err_no_digits);
+ *errtxt = NULL;
+ errno = 0;
do {
- if (*(*ipp) != '.')
- break;
- if (retval < VRT_DECIMAL_MIN || retval > VRT_DECIMAL_MAX)
- BAIL(err_fatnum);
(*ipp)++;
for(ndig = 0; ndig < 3; ndig++) {
scale *= .1;
@@ -121,6 +129,8 @@ SF_Parse_Number(const char **ipp, int strict, const char **errtxt)
break;
retval += scale * (*(*ipp)++ - 0x30);
}
+ if (strict && ndig == 0)
+ BAIL(err_invalid_num);
if (strict && vct_isdigit(*(*ipp)))
BAIL(err_fatnum);
while (vct_isdigit(*(*ipp)))
@@ -332,7 +342,7 @@ VNUM_2bytes(const char *p, uintmax_t *r, uintmax_t rel)
const char *errtxt;
if (p == NULL || *p == '\0')
- return (err_miss_num);
+ return (err_invalid_num);
fval = SF_Parse_Number(&p, 1, &errtxt);
if (errno)
@@ -364,14 +374,50 @@ static const struct test_sf_parse_int {
{ "1234", 3, 123, 3, 1, err_fatnum },
{ "1234", 4, 1234, 4, 1, NULL },
{ "1234", 5, 1234, 4, 1, NULL },
- { "-", 5, 0, 1, -1, err_invalid_num },
+ { "-", 5, 0, 1, -1, err_no_digits },
+ { " ", 5, 0, 2, 1, err_no_digits },
{ "-1234", 3, 123, 4, -1, err_fatnum },
{ "-1234", 4, 1234, 5, -1, NULL },
{ "-1234", 5, 1234, 5, -1, NULL },
{ " -1234", 5, 1234, 6, -1, NULL },
{ " -1234 ", 5, 1234, 7, -1, NULL },
{ " -12 34 ", 5, 12, 5, -1, NULL },
- { " - 12 34 ", 5, 0, 2, -1, err_invalid_num },
+ { " - 12 34 ", 5, 0, 2, -1, err_no_digits },
+ { NULL},
+};
+
+static const struct test_sf_parse_number {
+ const char *input;
+ int strict;
+ double retval;
+ int consumed;
+ const char *errtxt;
+} test_sf_parse_number[] = {
+ { "1234", 1, 1234.000, 4, NULL },
+ { " 1234", 1, 1234.000, 5, NULL },
+ { " 1234 ", 1, 1234.000, 6, NULL },
+ { " 1234. ", 1, 1234.000, 6, err_invalid_num },
+ { " 123456789012.0 ", 1, 123456789012.000, 16, NULL },
+ { " 1234567890123.0 ", 1, 1234567890123.000, 14, err_fatnum },
+ { " 123456789012.123 ", 1, 123456789012.123, 18, NULL },
+ { " 123456789012.1234 ",1, 123456789012.123, 17, err_fatnum },
+ { " -0.123456 ", 1, .123, 7, err_fatnum },
+ { " -.123456 ", 1, 0., 2, err_no_digits },
+ { " .123456 ", 1, 0., 1, err_no_digits },
+ { " 0. ", 1, 0., 3, err_invalid_num },
+ { " .0 ", 1, 0., 1, err_no_digits },
+
+ { " 123456789012.1234 ",0, 123456789012.123, 19, NULL },
+ { " -0.123456 ", 0, -.123, 11, NULL },
+ { " -.123456 ", 0, -.123, 10, NULL },
+ { " .123456 ", 0, .123, 9, NULL },
+ { " 0. ", 0, 0., 4, NULL },
+ { " .0 ", 0, 0., 4, NULL },
+ { " -0. ", 0, -0., 5, NULL },
+ { " -.0 ", 0, -0., 5, NULL },
+ { " - ", 0, -0., 2, err_no_digits },
+ { " -. ", 0, 0., 2, err_no_digits },
+ { " . ", 0, 0., 1, err_no_digits },
{ NULL},
};
@@ -425,12 +471,12 @@ static struct test_case {
{ "3%", (uintmax_t)1024, (uintmax_t)30 },
/* Check the error checks */
- { "", 0, 0, err_miss_num },
+ { "", 0, 0, err_invalid_num },
{ "-1", 0, 0, err_invalid_num },
{ "1.3", 0, 0, err_fractional_bytes},
{ "1.5011%", 0, 0, err_fatnum },
- { "-", 0, 0, err_invalid_num },
- { "m", 0, 0, err_miss_num },
+ { "-", 0, 0, err_no_digits },
+ { "m", 0, 0, err_no_digits },
{ "4%", 0, 0, err_unknown_bytes },
{ "3*", 0, 0, err_unknown_bytes },
@@ -474,10 +520,14 @@ main(int argc, char *argv[])
const char *e;
double d1, d2;
const struct test_sf_parse_int *tspi;
+ const struct test_sf_parse_number *tspn;
int64_t i64;
+ volatile double dbl;
int sign, consumed;
const char *errtxt;
const char *input;
+ char buf1[30];
+ char buf2[30];
(void)argc;
@@ -502,11 +552,43 @@ main(int argc, char *argv[])
tspi->sign, sign);
printf(" consumed\texpected %d\tgot %d\n",
tspi->consumed, consumed);
+ printf(" errtxt\texpected %p\tgot %p\n",
+ tspi->errtxt, errtxt);
printf(" errtxt\texpected %s\tgot %s\n",
tspi->errtxt, errtxt);
}
}
+ for (tspn = test_sf_parse_number; tspn->input != NULL; tspn++) {
+ errtxt = "(unset)";
+ input = tspn->input;
+ dbl = SF_Parse_Number(&input, tspn->strict, &errtxt);
+ consumed = input - tspn->input;
+ bprintf(buf1, "%.4f", dbl);
+ bprintf(buf2, "%.4f", tspn->retval);
+ if (strcmp(buf1, buf2) ||
+ consumed != tspn->consumed ||
+ errtxt != tspn->errtxt) {
+ ec++;
+ printf("sf_parse_number(%s, strict=%d) failed\n",
+ tspn->input, tspn->strict);
+ printf(" retval\texpected %.4f\tgot %.4f\t(%e)\n",
+ tspn->retval, dbl, dbl - tspn->retval);
+ printf(" retval\texpected %a\tgot %a\n",
+ tspn->retval, dbl);
+ printf(" retval\texpected %s\tgot %s\n",
+ buf2, buf1);
+ printf(" retval\tdelta %e\n",
+ dbl - tspn->retval);
+ printf(" consumed\texpected %d\tgot %d\n",
+ tspn->consumed, consumed);
+ printf(" errtxt\texpected %p\tgot %p\n",
+ tspn->errtxt, errtxt);
+ printf(" errtxt\texpected %s\tgot %s\n",
+ tspn->errtxt, errtxt);
+ }
+ }
+
for (p = vec; *p != NULL; p++) {
e = *p;
d1 = VNUM(e + 1);
diff --git a/lib/libvarnishapi/vsl_query.c b/lib/libvarnishapi/vsl_query.c
index d6a30d0bb..9a8fa8283 100644
--- a/lib/libvarnishapi/vsl_query.c
+++ b/lib/libvarnishapi/vsl_query.c
@@ -122,8 +122,7 @@ vslq_test_rec(const struct vex *vex, const struct VSLC_ptr *rec)
const struct vex_rhs *rhs;
long long lhs_int = 0;
double lhs_float = 0.;
- const char *b, *e, *q, *t;
- char *p;
+ const char *b, *e, *q;
int i, dq;
AN(vex);
@@ -208,25 +207,13 @@ vslq_test_rec(const struct vex *vex, const struct VSLC_ptr *rec)
return (0);
switch (rhs->type) {
case VEX_INT:
- lhs_int = strtoll(b, &p, 0);
- if (*p == '.' || *p == 'e') {
- t = ""; /* assume valid float */
- lhs_float = VNUMpfx(b, &q);
- if (isnan(lhs_float))
- return (0);
- if (q != NULL)
- t = (q > p) ? q - 1 : q;
- p = TRUST_ME(t);
- lhs_int = (long long)lhs_float;
- }
- if (*p != '\0' && !isspace(*p))
- return (0); /* Can't parse - no match */
+ lhs_int = (long long)SF_Parse_Number(&b, 0, &q);
+ if (errno)
+ return (0);
break;
case VEX_FLOAT:
- lhs_float = VNUMpfx(b, &q);
- if (isnan(lhs_float))
- return (0);
- if (q != NULL && q > b && !isspace(q[-1]))
+ lhs_float = SF_Parse_Decimal(&b, 0, &q);
+ if (errno)
return (0);
break;
default:
diff --git a/vmod/vmod_std_conversions.c b/vmod/vmod_std_conversions.c
index cbf9c929a..bcdfac438 100644
--- a/vmod/vmod_std_conversions.c
+++ b/vmod/vmod_std_conversions.c
@@ -168,7 +168,7 @@ vmod_integer(VRT_CTX, struct VARGS(integer) *a)
if (a->valid_s && a->s != NULL) {
p = a->s;
- r = SF_Parse_Number(&p, 1, &errtxt);
+ r = SF_Parse_Number(&p, 0, &errtxt);
if (!errno && *p == '\0' && modf(r, &tmp) == 0.0)
return (r);
r = NAN;
@@ -267,7 +267,7 @@ vmod_real(VRT_CTX, struct VARGS(real) *a)
if (a->valid_s && a->s != NULL) {
p = a->s;
- r = SF_Parse_Decimal(&p, 1, &errtxt);
+ r = SF_Parse_Decimal(&p, 0, &errtxt);
if (!errno && *p == '\0')
return (r);
}
More information about the varnish-commit
mailing list