@@ -1970,40 +1970,25 @@ static void test_qemu_strtosz_simple(void)
g_assert_cmpint(err, ==, 0);
g_assert_cmpint(res, ==, 12345);
- /* Note: precision is 53 bits since we're parsing with strtod() */
-
- str = "9007199254740991"; /* 2^53-1 */
- err = qemu_strtosz(str, &endptr, &res);
- g_assert_cmpint(err, ==, 0);
- g_assert_cmpint(res, ==, 0x1fffffffffffff);
- g_assert(endptr == str + 16);
-
- str = "9007199254740992"; /* 2^53 */
- err = qemu_strtosz(str, &endptr, &res);
- g_assert_cmpint(err, ==, 0);
- g_assert_cmpint(res, ==, 0x20000000000000);
- g_assert(endptr == str + 16);
+ /* Note: precision is 64 bits (UINT64_MAX) */
str = "9007199254740993"; /* 2^53+1 */
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, 0);
- g_assert_cmpint(res, ==, 0x20000000000000); /* rounded to 53 bits */
+ g_assert_cmpint(res, ==, 0x20000000000001);
g_assert(endptr == str + 16);
- str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */
+ str = "18446744073709550591"; /* 0xfffffffffffffbff */
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, 0);
- g_assert_cmpint(res, ==, 0xfffffffffffff800);
+ g_assert_cmpint(res, ==, 0xfffffffffffffbff);
g_assert(endptr == str + 20);
- str = "18446744073709550591"; /* 0xfffffffffffffbff */
+ str = "18446744073709551615"; /* 2^64-1 (UINT64_MAX) */
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, 0);
- g_assert_cmpint(res, ==, 0xfffffffffffff800); /* rounded to 53 bits */
+ g_assert_cmpint(res, ==, 0xffffffffffffffff);
g_assert(endptr == str + 20);
-
- /* 0x7ffffffffffffe00..0x7fffffffffffffff get rounded to
- * 0x8000000000000000, thus -ERANGE; see test_qemu_strtosz_erange() */
}
static void test_qemu_strtosz_units(void)
@@ -2145,20 +2130,20 @@ static void test_qemu_strtosz_erange(void)
g_assert_cmpint(err, ==, -ERANGE);
g_assert(endptr == str + 2);
- str = "18446744073709550592"; /* 0xfffffffffffffc00 */
+ str = "18446744073709551616"; /* 2^64 */
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, -ERANGE);
g_assert(endptr == str + 20);
- str = "18446744073709551615"; /* 2^64-1 */
+ str = "1.7976931348623158e+308"; /* DBL_MAX */
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, -ERANGE);
- g_assert(endptr == str + 20);
+ g_assert(endptr == str + 23);
- str = "18446744073709551616"; /* 2^64 */
+ str = "2.2250738585072014e-308"; /* DBL_MIN */
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, -ERANGE);
- g_assert(endptr == str + 20);
+ g_assert(endptr == str + 23);
str = "20E";
err = qemu_strtosz(str, &endptr, &res);
@@ -383,59 +383,26 @@ static void test_keyval_visit_size(void)
visit_end_struct(v, NULL);
visit_free(v);
- /* Note: precision is 53 bits since we're parsing with strtod() */
+ /* Note: precision is 64 bits (UINT64_MAX) */
- /* Around limit of precision: 2^53-1, 2^53, 2^53+1 */
- qdict = keyval_parse("sz1=9007199254740991,"
- "sz2=9007199254740992,"
- "sz3=9007199254740993",
+ /* Around limit of precision: UINT64_MAX - 1, UINT64_MAX */
+ qdict = keyval_parse("sz1=18446744073709551614,"
+ "sz2=18446744073709551615",
NULL, &error_abort);
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
qobject_unref(qdict);
visit_start_struct(v, NULL, NULL, 0, &error_abort);
visit_type_size(v, "sz1", &sz, &error_abort);
- g_assert_cmphex(sz, ==, 0x1fffffffffffff);
+ g_assert_cmphex(sz, ==, 0xfffffffffffffffe);
visit_type_size(v, "sz2", &sz, &error_abort);
- g_assert_cmphex(sz, ==, 0x20000000000000);
- visit_type_size(v, "sz3", &sz, &error_abort);
- g_assert_cmphex(sz, ==, 0x20000000000000);
- visit_check_struct(v, &error_abort);
- visit_end_struct(v, NULL);
- visit_free(v);
-
- /* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */
- qdict = keyval_parse("sz1=9223372036854774784," /* 7ffffffffffffc00 */
- "sz2=9223372036854775295", /* 7ffffffffffffdff */
- NULL, &error_abort);
- v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
- qobject_unref(qdict);
- visit_start_struct(v, NULL, NULL, 0, &error_abort);
- visit_type_size(v, "sz1", &sz, &error_abort);
- g_assert_cmphex(sz, ==, 0x7ffffffffffffc00);
- visit_type_size(v, "sz2", &sz, &error_abort);
- g_assert_cmphex(sz, ==, 0x7ffffffffffffc00);
- visit_check_struct(v, &error_abort);
- visit_end_struct(v, NULL);
- visit_free(v);
-
- /* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */
- qdict = keyval_parse("sz1=18446744073709549568," /* fffffffffffff800 */
- "sz2=18446744073709550591", /* fffffffffffffbff */
- NULL, &error_abort);
- v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
- qobject_unref(qdict);
- visit_start_struct(v, NULL, NULL, 0, &error_abort);
- visit_type_size(v, "sz1", &sz, &error_abort);
- g_assert_cmphex(sz, ==, 0xfffffffffffff800);
- visit_type_size(v, "sz2", &sz, &error_abort);
- g_assert_cmphex(sz, ==, 0xfffffffffffff800);
+ g_assert_cmphex(sz, ==, 0xffffffffffffffff);
visit_check_struct(v, &error_abort);
visit_end_struct(v, NULL);
visit_free(v);
/* Beyond limits */
qdict = keyval_parse("sz1=-1,"
- "sz2=18446744073709550592", /* fffffffffffffc00 */
+ "sz2=18446744073709551616", /* 2^64 */
NULL, &error_abort);
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
qobject_unref(qdict);
@@ -650,50 +650,25 @@ static void test_opts_parse_size(void)
g_assert_cmpuint(opts_count(opts), ==, 1);
g_assert_cmpuint(qemu_opt_get_size(opts, "size1", 1), ==, 0);
- /* Note: precision is 53 bits since we're parsing with strtod() */
+ /* Note: precision is 64 bits (UINT64_MAX) */
- /* Around limit of precision: 2^53-1, 2^53, 2^54 */
+ /* Around limit of precision: UINT64_MAX - 1, UINT64_MAX */
opts = qemu_opts_parse(&opts_list_02,
- "size1=9007199254740991,"
- "size2=9007199254740992,"
- "size3=9007199254740993",
- false, &error_abort);
- g_assert_cmpuint(opts_count(opts), ==, 3);
- g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
- ==, 0x1fffffffffffff);
- g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
- ==, 0x20000000000000);
- g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1),
- ==, 0x20000000000000);
-
- /* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */
- opts = qemu_opts_parse(&opts_list_02,
- "size1=9223372036854774784," /* 7ffffffffffffc00 */
- "size2=9223372036854775295", /* 7ffffffffffffdff */
- false, &error_abort);
- g_assert_cmpuint(opts_count(opts), ==, 2);
- g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
- ==, 0x7ffffffffffffc00);
- g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
- ==, 0x7ffffffffffffc00);
-
- /* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */
- opts = qemu_opts_parse(&opts_list_02,
- "size1=18446744073709549568," /* fffffffffffff800 */
- "size2=18446744073709550591", /* fffffffffffffbff */
+ "size1=18446744073709551614,"
+ "size2=18446744073709551615",
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 2);
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
- ==, 0xfffffffffffff800);
+ ==, 0xfffffffffffffffe);
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
- ==, 0xfffffffffffff800);
+ ==, 0xffffffffffffffff);
/* Beyond limits */
opts = qemu_opts_parse(&opts_list_02, "size1=-1", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
opts = qemu_opts_parse(&opts_list_02,
- "size1=18446744073709550592", /* fffffffffffffc00 */
+ "size1=18446744073709551616", /* 2^64 */
false, &err);
error_free_or_abort(&err);
g_assert(!opts);
@@ -212,19 +212,39 @@ static int do_strtosz(const char *nptr, const char **end,
const char default_suffix, int64_t unit,
uint64_t *result)
{
- int retval;
- const char *endptr;
+ int retval, retd, retu;
+ const char *endptr, *suffixd, *suffixu;
unsigned char c;
int mul_required = 0;
- double val, mul, integral, fraction;
+ bool use_strtod;
+ uint64_t valu;
+ int64_t mul;
+ double vald, integral, fraction;
+
+ /*
+ * Parse @nptr both as a double and as a uint64_t, then use the method
+ * which consumes more characters.
+ */
+ retd = qemu_strtod_finite(nptr, &suffixd, &vald);
+ retu = qemu_strtou64(nptr, &suffixu, 0, &valu);
+ use_strtod = strlen(suffixd) < strlen(suffixu);
+
+ if (use_strtod) {
+ endptr = suffixd;
+ retval = retd;
+ } else {
+ endptr = suffixu;
+ retval = retu;
+ }
- retval = qemu_strtod_finite(nptr, &endptr, &val);
if (retval) {
goto out;
}
- fraction = modf(val, &integral);
- if (fraction != 0) {
- mul_required = 1;
+ if (use_strtod) {
+ fraction = modf(vald, &integral);
+ if (fraction != 0) {
+ mul_required = 1;
+ }
}
c = *endptr;
mul = suffix_mul(c, unit);
@@ -238,17 +258,30 @@ static int do_strtosz(const char *nptr, const char **end,
retval = -EINVAL;
goto out;
}
- /*
- * Values near UINT64_MAX overflow to 2**64 when converting to double
- * precision. Compare against the maximum representable double precision
- * value below 2**64, computed as "the next value after 2**64 (0x1p64) in
- * the direction of 0".
- */
- if ((val * mul > nextafter(0x1p64, 0)) || val < 0) {
- retval = -ERANGE;
- goto out;
+
+ if (use_strtod) {
+ /*
+ * Values near UINT64_MAX overflow to 2**64 when converting to double
+ * precision. Compare against the maximum representable double precision
+ * value below 2**64, computed as "the next value after 2**64 (0x1p64)
+ * in the direction of 0".
+ */
+ if ((vald * mul > nextafter(0x1p64, 0)) || vald < 0) {
+ retval = -ERANGE;
+ goto out;
+ }
+ *result = vald * mul;
+ } else {
+ /* Reject negative input and overflow output */
+ while (qemu_isspace(*nptr)) {
+ nptr++;
+ }
+ if (*nptr == '-' || UINT64_MAX / mul < valu) {
+ retval = -ERANGE;
+ goto out;
+ }
+ *result = valu * mul;
}
- *result = val * mul;
retval = 0;
out:
Parse input string both as a double and as a uint64_t, then use the method which consumes more characters. Update the related test cases. Signed-off-by: Tao Xu <tao3.xu@intel.com> --- Changes in v2: - Add more test case for double overflow and underflow. - Set mul as int64_t (Markus) - Restore endptr (Markus) --- tests/test-cutils.c | 37 +++++++---------------- tests/test-keyval.c | 47 +++++------------------------ tests/test-qemu-opts.c | 39 +++++------------------- util/cutils.c | 67 +++++++++++++++++++++++++++++++----------- 4 files changed, 75 insertions(+), 115 deletions(-)