aboutsummaryrefslogtreecommitdiff
path: root/qobject
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@redhat.com>2017-06-07 20:35:58 +0400
committerMarkus Armbruster <armbru@redhat.com>2017-06-20 14:31:31 +0200
commit01b2ffcedd94ad7b42bc870e4c6936c87ad03429 (patch)
tree39bbadfbbaa229bfbda245840a256fe0132b2390 /qobject
parent58634047b7deeab36e4b07c4744e44d698975561 (diff)
qapi: merge QInt and QFloat in QNum
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 <marcandre.lureau@redhat.com> Message-Id: <20170607163635.17635-7-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> [parse_stats_intervals() simplified a bit, comment in test_visitor_in_int_overflow() tidied up, suppress bogus warnings] Signed-off-by: Markus Armbruster <armbru@redhat.com>
Diffstat (limited to 'qobject')
-rw-r--r--qobject/Makefile.objs2
-rw-r--r--qobject/json-parser.c30
-rw-r--r--qobject/qdict.c37
-rw-r--r--qobject/qfloat.c62
-rw-r--r--qobject/qint.c61
-rw-r--r--qobject/qjson.c37
-rw-r--r--qobject/qnum.c159
-rw-r--r--qobject/qobject.c3
8 files changed, 194 insertions, 197 deletions
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 <aliguori@us.ibm.com>
- *
- * 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 <lcapitulino@redhat.com>
- *
- * 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 <lcapitulino@redhat.com>
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * 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,
};