json: learn to parse uint64 numbers
Switch strtoll() usage to qemu_strtoi64() helper while at it. Add a few tests for large numbers. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20170607163635.17635-11-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
parent
61a8f418b2
commit
2bc7cfea09
@ -227,15 +227,18 @@ static const uint8_t json_lexer[][256] = {
|
|||||||
/* escape */
|
/* escape */
|
||||||
[IN_ESCAPE_LL] = {
|
[IN_ESCAPE_LL] = {
|
||||||
['d'] = JSON_ESCAPE,
|
['d'] = JSON_ESCAPE,
|
||||||
|
['u'] = JSON_ESCAPE,
|
||||||
},
|
},
|
||||||
|
|
||||||
[IN_ESCAPE_L] = {
|
[IN_ESCAPE_L] = {
|
||||||
['d'] = JSON_ESCAPE,
|
['d'] = JSON_ESCAPE,
|
||||||
['l'] = IN_ESCAPE_LL,
|
['l'] = IN_ESCAPE_LL,
|
||||||
|
['u'] = JSON_ESCAPE,
|
||||||
},
|
},
|
||||||
|
|
||||||
[IN_ESCAPE_I64] = {
|
[IN_ESCAPE_I64] = {
|
||||||
['d'] = JSON_ESCAPE,
|
['d'] = JSON_ESCAPE,
|
||||||
|
['u'] = JSON_ESCAPE,
|
||||||
},
|
},
|
||||||
|
|
||||||
[IN_ESCAPE_I6] = {
|
[IN_ESCAPE_I6] = {
|
||||||
@ -251,6 +254,7 @@ static const uint8_t json_lexer[][256] = {
|
|||||||
['i'] = JSON_ESCAPE,
|
['i'] = JSON_ESCAPE,
|
||||||
['p'] = JSON_ESCAPE,
|
['p'] = JSON_ESCAPE,
|
||||||
['s'] = JSON_ESCAPE,
|
['s'] = JSON_ESCAPE,
|
||||||
|
['u'] = JSON_ESCAPE,
|
||||||
['f'] = JSON_ESCAPE,
|
['f'] = JSON_ESCAPE,
|
||||||
['l'] = IN_ESCAPE_L,
|
['l'] = IN_ESCAPE_L,
|
||||||
['I'] = IN_ESCAPE_I,
|
['I'] = IN_ESCAPE_I,
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/cutils.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qapi/qmp/types.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") ||
|
} else if (!strcmp(token->str, "%lld") ||
|
||||||
!strcmp(token->str, "%I64d")) {
|
!strcmp(token->str, "%I64d")) {
|
||||||
return QOBJECT(qnum_from_int(va_arg(*ap, long long)));
|
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")) {
|
} else if (!strcmp(token->str, "%s")) {
|
||||||
return QOBJECT(qstring_from_str(va_arg(*ap, const char *)));
|
return QOBJECT(qstring_from_str(va_arg(*ap, const char *)));
|
||||||
} else if (!strcmp(token->str, "%f")) {
|
} else if (!strcmp(token->str, "%f")) {
|
||||||
@ -493,21 +501,33 @@ static QObject *parse_literal(JSONParserContext *ctxt)
|
|||||||
case JSON_INTEGER: {
|
case JSON_INTEGER: {
|
||||||
/*
|
/*
|
||||||
* Represent JSON_INTEGER as QNUM_I64 if possible, else as
|
* Represent JSON_INTEGER as QNUM_I64 if possible, else as
|
||||||
* QNUM_DOUBLE. Note that strtoll() fails with ERANGE when
|
* QNUM_U64, else as QNUM_DOUBLE. Note that qemu_strtoi64()
|
||||||
* it's not possible.
|
* and qemu_strtou64() fail with ERANGE when it's not
|
||||||
|
* possible.
|
||||||
*
|
*
|
||||||
* qnum_get_int() will then work for any signed 64-bit
|
* qnum_get_int() will then work for any signed 64-bit
|
||||||
* JSON_INTEGER, and qnum_get_double() both for any
|
* JSON_INTEGER, qnum_get_uint() for any unsigned 64-bit
|
||||||
* JSON_INTEGER and any JSON_FLOAT (with precision loss for
|
* integer, and qnum_get_double() both for any JSON_INTEGER
|
||||||
* integers beyond 53 bits)
|
* and any JSON_FLOAT (with precision loss for integers beyond
|
||||||
|
* 53 bits)
|
||||||
*/
|
*/
|
||||||
|
int ret;
|
||||||
int64_t value;
|
int64_t value;
|
||||||
|
uint64_t uvalue;
|
||||||
|
|
||||||
errno = 0; /* strtoll doesn't set errno on success */
|
ret = qemu_strtoi64(token->str, NULL, 10, &value);
|
||||||
value = strtoll(token->str, NULL, 10);
|
if (!ret) {
|
||||||
if (errno != ERANGE) {
|
|
||||||
return QOBJECT(qnum_from_int(value));
|
return QOBJECT(qnum_from_int(value));
|
||||||
}
|
}
|
||||||
|
assert(ret == -ERANGE);
|
||||||
|
|
||||||
|
if (token->str[0] != '-') {
|
||||||
|
ret = qemu_strtou64(token->str, NULL, 10, &uvalue);
|
||||||
|
if (!ret) {
|
||||||
|
return QOBJECT(qnum_from_uint(uvalue));
|
||||||
|
}
|
||||||
|
assert(ret == -ERANGE);
|
||||||
|
}
|
||||||
/* fall through to JSON_FLOAT */
|
/* fall through to JSON_FLOAT */
|
||||||
}
|
}
|
||||||
case JSON_FLOAT:
|
case JSON_FLOAT:
|
||||||
|
@ -906,6 +906,49 @@ static void simple_number(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void large_number(void)
|
||||||
|
{
|
||||||
|
const char *maxu64 = "18446744073709551615"; /* 2^64-1 */
|
||||||
|
const char *gtu64 = "18446744073709551616"; /* 2^64 */
|
||||||
|
const char *lti64 = "-9223372036854775809"; /* -2^63 - 1 */
|
||||||
|
QNum *qnum;
|
||||||
|
QString *str;
|
||||||
|
uint64_t val;
|
||||||
|
int64_t ival;
|
||||||
|
|
||||||
|
qnum = qobject_to_qnum(qobject_from_json(maxu64, &error_abort));
|
||||||
|
g_assert(qnum);
|
||||||
|
g_assert_cmpuint(qnum_get_uint(qnum), ==, 18446744073709551615U);
|
||||||
|
g_assert(!qnum_get_try_int(qnum, &ival));
|
||||||
|
|
||||||
|
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), ==, 18446744073709552e3);
|
||||||
|
g_assert(!qnum_get_try_uint(qnum, &val));
|
||||||
|
g_assert(!qnum_get_try_int(qnum, &ival));
|
||||||
|
|
||||||
|
str = qobject_to_json(QOBJECT(qnum));
|
||||||
|
g_assert_cmpstr(qstring_get_str(str), ==, gtu64);
|
||||||
|
QDECREF(str);
|
||||||
|
QDECREF(qnum);
|
||||||
|
|
||||||
|
qnum = qobject_to_qnum(qobject_from_json(lti64, &error_abort));
|
||||||
|
g_assert(qnum);
|
||||||
|
g_assert_cmpfloat(qnum_get_double(qnum), ==, -92233720368547758e2);
|
||||||
|
g_assert(!qnum_get_try_uint(qnum, &val));
|
||||||
|
g_assert(!qnum_get_try_int(qnum, &ival));
|
||||||
|
|
||||||
|
str = qobject_to_json(QOBJECT(qnum));
|
||||||
|
g_assert_cmpstr(qstring_get_str(str), ==, "-9223372036854775808");
|
||||||
|
QDECREF(str);
|
||||||
|
QDECREF(qnum);
|
||||||
|
}
|
||||||
|
|
||||||
static void float_number(void)
|
static void float_number(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -1475,6 +1518,7 @@ int main(int argc, char **argv)
|
|||||||
g_test_add_func("/literals/string/vararg", vararg_string);
|
g_test_add_func("/literals/string/vararg", vararg_string);
|
||||||
|
|
||||||
g_test_add_func("/literals/number/simple", simple_number);
|
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/float", float_number);
|
||||||
g_test_add_func("/literals/number/vararg", vararg_number);
|
g_test_add_func("/literals/number/vararg", vararg_number);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user