qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 07/17] json: learn to parse uint64 numbers


From: Marc-André Lureau
Subject: [Qemu-devel] [PATCH 07/17] json: learn to parse uint64 numbers
Date: Tue, 9 May 2017 20:35:49 +0300

Switch strtoll() usage to qemu_strtoi64() helper while at it.

Replace temporarily the error in qnum_get_int() with values >INT64_MAX
until the visitor is updated.

Add a few tests for large numbers.

Signed-off-by: Marc-André Lureau <address@hidden>
---
 qobject/json-lexer.c               |  4 ++++
 qobject/json-parser.c              | 30 ++++++++++++++++++++++--------
 qobject/qnum.c                     |  4 ++--
 tests/check-qjson.c                | 28 ++++++++++++++++++++++++++++
 tests/check-qnum.c                 |  9 +++++----
 tests/test-qobject-input-visitor.c |  7 ++-----
 6 files changed, 63 insertions(+), 19 deletions(-)

diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c
index af4a75e05b..a0beb0b106 100644
--- a/qobject/json-lexer.c
+++ b/qobject/json-lexer.c
@@ -227,15 +227,18 @@ static const uint8_t json_lexer[][256] =  {
     /* escape */
     [IN_ESCAPE_LL] = {
         ['d'] = JSON_ESCAPE,
+        ['u'] = JSON_ESCAPE,
     },
 
     [IN_ESCAPE_L] = {
         ['d'] = JSON_ESCAPE,
+        ['u'] = JSON_ESCAPE,
         ['l'] = IN_ESCAPE_LL,
     },
 
     [IN_ESCAPE_I64] = {
         ['d'] = JSON_ESCAPE,
+        ['u'] = JSON_ESCAPE,
     },
 
     [IN_ESCAPE_I6] = {
@@ -247,6 +250,7 @@ static const uint8_t json_lexer[][256] =  {
     },
 
     [IN_ESCAPE] = {
+        ['u'] = JSON_ESCAPE,
         ['d'] = JSON_ESCAPE,
         ['i'] = JSON_ESCAPE,
         ['p'] = JSON_ESCAPE,
diff --git a/qobject/json-parser.c b/qobject/json-parser.c
index f431854ba1..fa15c762d3 100644
--- a/qobject/json-parser.c
+++ b/qobject/json-parser.c
@@ -12,6 +12,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/cutils.h"
 #include "qapi/error.h"
 #include "qemu-common.h"
 #include "qapi/qmp/types.h"
@@ -472,6 +473,13 @@ static QObject *parse_escape(JSONParserContext *ctxt, 
va_list *ap)
     } else if (!strcmp(token->str, "%lld") ||
                !strcmp(token->str, "%I64d")) {
         return QOBJECT(qnum_from_int(va_arg(*ap, long long)));
+    } else if (!strcmp(token->str, "%u")) {
+        return QOBJECT(qnum_from_uint(va_arg(*ap, unsigned int)));
+    } else if (!strcmp(token->str, "%lu")) {
+        return QOBJECT(qnum_from_uint(va_arg(*ap, unsigned long)));
+    } else if (!strcmp(token->str, "%llu") ||
+               !strcmp(token->str, "%I64u")) {
+        return QOBJECT(qnum_from_uint(va_arg(*ap, unsigned long long)));
     } else if (!strcmp(token->str, "%s")) {
         return QOBJECT(qstring_from_str(va_arg(*ap, const char *)));
     } else if (!strcmp(token->str, "%f")) {
@@ -494,22 +502,28 @@ static QObject *parse_literal(JSONParserContext *ctxt)
         /* A possibility exists that this is a whole-valued float where the
          * fractional part was left out due to being 0 (.0). It's not a big
          * deal to treat these as ints in the parser, so long as users of the
-         * resulting QObject know to expect a QNum in place of a QNum in
-         * cases like these.
+         * resulting QObject know to expect a QNum that will handle
+         * implicit conversions to the expected type.
          *
-         * However, in some cases these values will overflow/underflow a
-         * QNum/int64 container, thus we should assume these are to be handled
-         * as QNums/doubles rather than silently changing their values.
+         * However, in some cases these values will overflow/underflow
+         * a QNum/int64 container, thus we should assume these are to
+         * be handled as QNum/uint64 or QNums/doubles rather than
+         * silently changing their values.
          *
-         * strtoll() indicates these instances by setting errno to ERANGE
+         * qemu_strto*() indicates these instances by setting errno to ERANGE
          */
         int64_t value;
+        uint64_t uvalue;
 
-        errno = 0; /* strtoll doesn't set errno on success */
-        value = strtoll(token->str, NULL, 10);
+        qemu_strtoi64(token->str, NULL, 10, &value);
         if (errno != ERANGE) {
             return QOBJECT(qnum_from_int(value));
         }
+
+        qemu_strtou64(token->str, NULL, 10, &uvalue);
+        if (errno != ERANGE) {
+            return QOBJECT(qnum_from_uint(uvalue));
+        }
         /* fall through to JSON_FLOAT */
     }
     case JSON_FLOAT:
diff --git a/qobject/qnum.c b/qobject/qnum.c
index be6307accf..2f87952db8 100644
--- a/qobject/qnum.c
+++ b/qobject/qnum.c
@@ -76,8 +76,8 @@ int64_t qnum_get_int(const QNum *qn, Error **errp)
         return qn->u.i64;
     case QNUM_U64:
         if (qn->u.u64 > INT64_MAX) {
-            error_setg(errp, "The number is too large, use qnum_get_uint()");
-            return 0;
+            /* temporarily accepts to cast to i64 until visitor is switched */
+            error_report("The number is too large, use qnum_get_uint()");
         }
         return qn->u.u64;
     case QNUM_DOUBLE:
diff --git a/tests/check-qjson.c b/tests/check-qjson.c
index c432aebf13..57c2366dc3 100644
--- a/tests/check-qjson.c
+++ b/tests/check-qjson.c
@@ -904,6 +904,33 @@ static void simple_number(void)
     }
 }
 
+static void large_number(void)
+{
+    const char *maxu64 = "18446744073709551615"; /* 2^64-1 */
+    const char *gtu64 = "18446744073709551616"; /* 2^64 */
+    QNum *qnum;
+    QString *str;
+
+    qnum = qobject_to_qnum(qobject_from_json(maxu64, &error_abort));
+    g_assert(qnum);
+    g_assert_cmpuint(qnum_get_uint(qnum, &error_abort),
+                     ==, 18446744073709551615U);
+
+    str = qobject_to_json(QOBJECT(qnum));
+    g_assert_cmpstr(qstring_get_str(str), ==, maxu64);
+    QDECREF(str);
+    QDECREF(qnum);
+
+    qnum = qobject_to_qnum(qobject_from_json(gtu64, &error_abort));
+    g_assert(qnum);
+    g_assert_cmpfloat(qnum_get_double(qnum), ==, 18446744073709551616.0);
+
+    str = qobject_to_json(QOBJECT(qnum));
+    g_assert_cmpstr(qstring_get_str(str), ==, gtu64);
+    QDECREF(str);
+    QDECREF(qnum);
+}
+
 static void float_number(void)
 {
     int i;
@@ -1468,6 +1495,7 @@ int main(int argc, char **argv)
     g_test_add_func("/literals/string/vararg", vararg_string);
 
     g_test_add_func("/literals/number/simple", simple_number);
+    g_test_add_func("/literals/number/large", large_number);
     g_test_add_func("/literals/number/float", float_number);
     g_test_add_func("/literals/number/vararg", vararg_number);
 
diff --git a/tests/check-qnum.c b/tests/check-qnum.c
index 8199546f99..9a22af3d0e 100644
--- a/tests/check-qnum.c
+++ b/tests/check-qnum.c
@@ -107,10 +107,11 @@ static void qnum_get_uint_test(void)
     error_free_or_abort(&err);
     QDECREF(qn);
 
-    qn = qnum_from_uint(-1ULL);
-    qnum_get_int(qn, &err);
-    error_free_or_abort(&err);
-    QDECREF(qn);
+    /* temporarily disabled until visitor is switched */
+    /* qn = qnum_from_uint(-1ULL); */
+    /* qnum_get_int(qn, &err); */
+    /* error_free_or_abort(&err); */
+    /* QDECREF(qn); */
 
     /* invalid case */
     qn = qnum_from_double(0.42);
diff --git a/tests/test-qobject-input-visitor.c 
b/tests/test-qobject-input-visitor.c
index 5df62c4f9e..276a6b4427 100644
--- a/tests/test-qobject-input-visitor.c
+++ b/tests/test-qobject-input-visitor.c
@@ -119,7 +119,6 @@ static void test_visitor_in_int(TestInputVisitorData *data,
 static void test_visitor_in_uint(TestInputVisitorData *data,
                                 const void *unused)
 {
-    Error *err = NULL;
     uint64_t res = 0;
     int value = 42;
     Visitor *v;
@@ -136,12 +135,10 @@ static void test_visitor_in_uint(TestInputVisitorData 
*data,
     visit_type_uint64(v, NULL, &res, &error_abort);
     g_assert_cmpuint(res, ==, (uint64_t)-value);
 
-    /* BUG: value between INT64_MAX+1 and UINT64_MAX rejected */
-
     v = visitor_input_test_init(data, "18446744073709551574");
 
-    visit_type_uint64(v, NULL, &res, &err);
-    error_free_or_abort(&err);
+    visit_type_uint64(v, NULL, &res, &error_abort);
+    g_assert_cmpuint(res, ==, 18446744073709551574U);
 }
 
 static void test_visitor_in_int_overflow(TestInputVisitorData *data,
-- 
2.13.0.rc1.16.gd80b50c3f




reply via email to

[Prev in Thread] Current Thread [Next in Thread]