From 01b2ffcedd94ad7b42bc870e4c6936c87ad03429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 7 Jun 2017 20:35:58 +0400 Subject: qapi: merge QInt and QFloat in QNum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We would like to use a same QObject type to represent numbers, whether they are int, uint, or floats. Getters will allow some compatibility between the various types if the number fits other representations. Add a few more tests while at it. Signed-off-by: Marc-André Lureau Message-Id: <20170607163635.17635-7-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [parse_stats_intervals() simplified a bit, comment in test_visitor_in_int_overflow() tidied up, suppress bogus warnings] Signed-off-by: Markus Armbruster --- qobject/Makefile.objs | 2 +- qobject/json-parser.c | 30 +++++----- qobject/qdict.c | 37 +++++------- qobject/qfloat.c | 62 -------------------- qobject/qint.c | 61 ------------------- qobject/qjson.c | 37 ++---------- qobject/qnum.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++ qobject/qobject.c | 3 +- 8 files changed, 194 insertions(+), 197 deletions(-) delete mode 100644 qobject/qfloat.c delete mode 100644 qobject/qint.c create mode 100644 qobject/qnum.c (limited to 'qobject') diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs index bed55084bb..fc8885c9a4 100644 --- a/qobject/Makefile.objs +++ b/qobject/Makefile.objs @@ -1,2 +1,2 @@ -util-obj-y = qnull.o qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o +util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o diff --git a/qobject/json-parser.c b/qobject/json-parser.c index c18e48ab94..5e808289f5 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -466,16 +466,16 @@ static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap) } else if (!strcmp(token->str, "%i")) { return QOBJECT(qbool_from_bool(va_arg(*ap, int))); } else if (!strcmp(token->str, "%d")) { - return QOBJECT(qint_from_int(va_arg(*ap, int))); + return QOBJECT(qnum_from_int(va_arg(*ap, int))); } else if (!strcmp(token->str, "%ld")) { - return QOBJECT(qint_from_int(va_arg(*ap, long))); + return QOBJECT(qnum_from_int(va_arg(*ap, long))); } else if (!strcmp(token->str, "%lld") || !strcmp(token->str, "%I64d")) { - return QOBJECT(qint_from_int(va_arg(*ap, long long))); + return QOBJECT(qnum_from_int(va_arg(*ap, long long))); } else if (!strcmp(token->str, "%s")) { return QOBJECT(qstring_from_str(va_arg(*ap, const char *))); } else if (!strcmp(token->str, "%f")) { - return QOBJECT(qfloat_from_double(va_arg(*ap, double))); + return QOBJECT(qnum_from_double(va_arg(*ap, double))); } return NULL; } @@ -491,24 +491,22 @@ static QObject *parse_literal(JSONParserContext *ctxt) case JSON_STRING: return QOBJECT(qstring_from_escaped_str(ctxt, token)); case JSON_INTEGER: { - /* 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 QInt in place of a QFloat in - * cases like these. + /* + * Represent JSON_INTEGER as QNUM_I64 if possible, else as + * QNUM_DOUBLE. Note that strtoll() fails with ERANGE when + * it's not possible. * - * However, in some cases these values will overflow/underflow a - * QInt/int64 container, thus we should assume these are to be handled - * as QFloats/doubles rather than silently changing their values. - * - * strtoll() indicates these instances by setting errno to ERANGE + * qnum_get_int() will then work for any signed 64-bit + * JSON_INTEGER, and qnum_get_double() both for any + * JSON_INTEGER and any JSON_FLOAT (with precision loss for + * integers beyond 53 bits) */ int64_t value; errno = 0; /* strtoll doesn't set errno on success */ value = strtoll(token->str, NULL, 10); if (errno != ERANGE) { - return QOBJECT(qint_from_int(value)); + return QOBJECT(qnum_from_int(value)); } /* fall through to JSON_FLOAT */ } @@ -516,7 +514,7 @@ static QObject *parse_literal(JSONParserContext *ctxt) /* FIXME dependent on locale; a pervasive issue in QEMU */ /* FIXME our lexer matches RFC 7159 in forbidding Inf or NaN, * but those might be useful extensions beyond JSON */ - return QOBJECT(qfloat_from_double(strtod(token->str, NULL))); + return QOBJECT(qnum_from_double(strtod(token->str, NULL))); default: abort(); } diff --git a/qobject/qdict.c b/qobject/qdict.c index 88e2ecd658..576018e531 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -11,8 +11,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qfloat.h" +#include "qapi/qmp/qnum.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qstring.h" @@ -180,37 +179,26 @@ size_t qdict_size(const QDict *qdict) /** * qdict_get_double(): Get an number mapped by 'key' * - * This function assumes that 'key' exists and it stores a - * QFloat or QInt object. + * This function assumes that 'key' exists and it stores a QNum. * * Return number mapped by 'key'. */ double qdict_get_double(const QDict *qdict, const char *key) { - QObject *obj = qdict_get(qdict, key); - - assert(obj); - switch (qobject_type(obj)) { - case QTYPE_QFLOAT: - return qfloat_get_double(qobject_to_qfloat(obj)); - case QTYPE_QINT: - return qint_get_int(qobject_to_qint(obj)); - default: - abort(); - } + return qnum_get_double(qobject_to_qnum(qdict_get(qdict, key))); } /** * qdict_get_int(): Get an integer mapped by 'key' * * This function assumes that 'key' exists and it stores a - * QInt object. + * QNum representable as int. * * Return integer mapped by 'key'. */ int64_t qdict_get_int(const QDict *qdict, const char *key) { - return qint_get_int(qobject_to_qint(qdict_get(qdict, key))); + return qnum_get_int(qobject_to_qnum(qdict_get(qdict, key))); } /** @@ -259,16 +247,21 @@ const char *qdict_get_str(const QDict *qdict, const char *key) /** * qdict_get_try_int(): Try to get integer mapped by 'key' * - * Return integer mapped by 'key', if it is not present in - * the dictionary or if the stored object is not of QInt type - * 'def_value' will be returned. + * Return integer mapped by 'key', if it is not present in the + * dictionary or if the stored object is not a QNum representing an + * integer, 'def_value' will be returned. */ int64_t qdict_get_try_int(const QDict *qdict, const char *key, int64_t def_value) { - QInt *qint = qobject_to_qint(qdict_get(qdict, key)); + QNum *qnum = qobject_to_qnum(qdict_get(qdict, key)); + int64_t val; + + if (!qnum || !qnum_get_try_int(qnum, &val)) { + return def_value; + } - return qint ? qint_get_int(qint) : def_value; + return val; } /** diff --git a/qobject/qfloat.c b/qobject/qfloat.c deleted file mode 100644 index d5da847701..0000000000 --- a/qobject/qfloat.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * QFloat Module - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/qmp/qfloat.h" -#include "qapi/qmp/qobject.h" -#include "qemu-common.h" - -/** - * qfloat_from_int(): Create a new QFloat from a float - * - * Return strong reference. - */ -QFloat *qfloat_from_double(double value) -{ - QFloat *qf; - - qf = g_malloc(sizeof(*qf)); - qobject_init(QOBJECT(qf), QTYPE_QFLOAT); - qf->value = value; - - return qf; -} - -/** - * qfloat_get_double(): Get the stored float - */ -double qfloat_get_double(const QFloat *qf) -{ - return qf->value; -} - -/** - * qobject_to_qfloat(): Convert a QObject into a QFloat - */ -QFloat *qobject_to_qfloat(const QObject *obj) -{ - if (!obj || qobject_type(obj) != QTYPE_QFLOAT) { - return NULL; - } - return container_of(obj, QFloat, base); -} - -/** - * qfloat_destroy_obj(): Free all memory allocated by a - * QFloat object - */ -void qfloat_destroy_obj(QObject *obj) -{ - assert(obj != NULL); - g_free(qobject_to_qfloat(obj)); -} diff --git a/qobject/qint.c b/qobject/qint.c deleted file mode 100644 index d7d1b3021f..0000000000 --- a/qobject/qint.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * QInt Module - * - * Copyright (C) 2009 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qobject.h" -#include "qemu-common.h" - -/** - * qint_from_int(): Create a new QInt from an int64_t - * - * Return strong reference. - */ -QInt *qint_from_int(int64_t value) -{ - QInt *qi; - - qi = g_malloc(sizeof(*qi)); - qobject_init(QOBJECT(qi), QTYPE_QINT); - qi->value = value; - - return qi; -} - -/** - * qint_get_int(): Get the stored integer - */ -int64_t qint_get_int(const QInt *qi) -{ - return qi->value; -} - -/** - * qobject_to_qint(): Convert a QObject into a QInt - */ -QInt *qobject_to_qint(const QObject *obj) -{ - if (!obj || qobject_type(obj) != QTYPE_QINT) { - return NULL; - } - return container_of(obj, QInt, base); -} - -/** - * qint_destroy_obj(): Free all memory allocated by a - * QInt object - */ -void qint_destroy_obj(QObject *obj) -{ - assert(obj != NULL); - g_free(qobject_to_qint(obj)); -} diff --git a/qobject/qjson.c b/qobject/qjson.c index b2f3bfec53..2e0930884e 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -132,12 +132,11 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent) case QTYPE_QNULL: qstring_append(str, "null"); break; - case QTYPE_QINT: { - QInt *val = qobject_to_qint(obj); - char buffer[1024]; - - snprintf(buffer, sizeof(buffer), "%" PRId64, qint_get_int(val)); + case QTYPE_QNUM: { + QNum *val = qobject_to_qnum(obj); + char *buffer = qnum_to_string(val); qstring_append(str, buffer); + g_free(buffer); break; } case QTYPE_QSTRING: { @@ -234,34 +233,6 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent) qstring_append(str, "]"); break; } - case QTYPE_QFLOAT: { - QFloat *val = qobject_to_qfloat(obj); - char buffer[1024]; - int len; - - /* FIXME: snprintf() is locale dependent; but JSON requires - * numbers to be formatted as if in the C locale. Dependence - * on C locale is a pervasive issue in QEMU. */ - /* FIXME: This risks printing Inf or NaN, which are not valid - * JSON values. */ - /* FIXME: the default precision of 6 for %f often causes - * rounding errors; we should be using DBL_DECIMAL_DIG (17), - * and only rounding to a shorter number if the result would - * still produce the same floating point value. */ - len = snprintf(buffer, sizeof(buffer), "%f", qfloat_get_double(val)); - while (len > 0 && buffer[len - 1] == '0') { - len--; - } - - if (len && buffer[len - 1] == '.') { - buffer[len - 1] = 0; - } else { - buffer[len] = 0; - } - - qstring_append(str, buffer); - break; - } case QTYPE_QBOOL: { QBool *val = qobject_to_qbool(obj); diff --git a/qobject/qnum.c b/qobject/qnum.c new file mode 100644 index 0000000000..7bb9006763 --- /dev/null +++ b/qobject/qnum.c @@ -0,0 +1,159 @@ +/* + * QNum Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * Anthony Liguori + * Marc-André Lureau + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qobject.h" +#include "qemu-common.h" + +/** + * qnum_from_int(): Create a new QNum from an int64_t + * + * Return strong reference. + */ +QNum *qnum_from_int(int64_t value) +{ + QNum *qn = g_new(QNum, 1); + + qobject_init(QOBJECT(qn), QTYPE_QNUM); + qn->kind = QNUM_I64; + qn->u.i64 = value; + + return qn; +} + +/** + * qnum_from_double(): Create a new QNum from a double + * + * Return strong reference. + */ +QNum *qnum_from_double(double value) +{ + QNum *qn = g_new(QNum, 1); + + qobject_init(QOBJECT(qn), QTYPE_QNUM); + qn->kind = QNUM_DOUBLE; + qn->u.dbl = value; + + return qn; +} + +/** + * qnum_get_try_int(): Get an integer representation of the number + * + * Return true on success. + */ +bool qnum_get_try_int(const QNum *qn, int64_t *val) +{ + switch (qn->kind) { + case QNUM_I64: + *val = qn->u.i64; + return true; + case QNUM_DOUBLE: + return false; + } + + assert(0); + return false; +} + +/** + * qnum_get_int(): Get an integer representation of the number + * + * assert() on failure. + */ +int64_t qnum_get_int(const QNum *qn) +{ + int64_t val; + bool success = qnum_get_try_int(qn, &val); + assert(success); + return val; +} + +/** + * qnum_get_double(): Get a float representation of the number + * + * qnum_get_double() loses precision for integers beyond 53 bits. + */ +double qnum_get_double(QNum *qn) +{ + switch (qn->kind) { + case QNUM_I64: + return qn->u.i64; + case QNUM_DOUBLE: + return qn->u.dbl; + } + + assert(0); + return 0.0; +} + +char *qnum_to_string(QNum *qn) +{ + char *buffer; + int len; + + switch (qn->kind) { + case QNUM_I64: + return g_strdup_printf("%" PRId64, qn->u.i64); + case QNUM_DOUBLE: + /* FIXME: snprintf() is locale dependent; but JSON requires + * numbers to be formatted as if in the C locale. Dependence + * on C locale is a pervasive issue in QEMU. */ + /* FIXME: This risks printing Inf or NaN, which are not valid + * JSON values. */ + /* FIXME: the default precision of 6 for %f often causes + * rounding errors; we should be using DBL_DECIMAL_DIG (17), + * and only rounding to a shorter number if the result would + * still produce the same floating point value. */ + buffer = g_strdup_printf("%f" , qn->u.dbl); + len = strlen(buffer); + while (len > 0 && buffer[len - 1] == '0') { + len--; + } + + if (len && buffer[len - 1] == '.') { + buffer[len - 1] = 0; + } else { + buffer[len] = 0; + } + + return buffer; + } + + assert(0); + return NULL; +} + +/** + * qobject_to_qnum(): Convert a QObject into a QNum + */ +QNum *qobject_to_qnum(const QObject *obj) +{ + if (!obj || qobject_type(obj) != QTYPE_QNUM) { + return NULL; + } + return container_of(obj, QNum, base); +} + +/** + * qnum_destroy_obj(): Free all memory allocated by a + * QNum object + */ +void qnum_destroy_obj(QObject *obj) +{ + assert(obj != NULL); + g_free(qobject_to_qnum(obj)); +} diff --git a/qobject/qobject.c b/qobject/qobject.c index fe4fa10989..b0cafb66f1 100644 --- a/qobject/qobject.c +++ b/qobject/qobject.c @@ -14,11 +14,10 @@ static void (*qdestroy[QTYPE__MAX])(QObject *) = { [QTYPE_NONE] = NULL, /* No such object exists */ [QTYPE_QNULL] = NULL, /* qnull_ is indestructible */ - [QTYPE_QINT] = qint_destroy_obj, + [QTYPE_QNUM] = qnum_destroy_obj, [QTYPE_QSTRING] = qstring_destroy_obj, [QTYPE_QDICT] = qdict_destroy_obj, [QTYPE_QLIST] = qlist_destroy_obj, - [QTYPE_QFLOAT] = qfloat_destroy_obj, [QTYPE_QBOOL] = qbool_destroy_obj, }; -- cgit v1.2.3