From nobody Wed Nov 5 07:34:31 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 149928168414781.54248754037144; Wed, 5 Jul 2017 12:08:04 -0700 (PDT) Received: from localhost ([::1]:47720 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dSpeZ-0006m3-PH for importer@patchew.org; Wed, 05 Jul 2017 15:07:59 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39975) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dSpaw-0003xi-Kg for qemu-devel@nongnu.org; Wed, 05 Jul 2017 15:04:16 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dSpav-0005tB-J2 for qemu-devel@nongnu.org; Wed, 05 Jul 2017 15:04:14 -0400 Received: from mx1.redhat.com ([209.132.183.28]:54870) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dSpat-0005rO-44; Wed, 05 Jul 2017 15:04:11 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0BA1C61D38; Wed, 5 Jul 2017 19:04:10 +0000 (UTC) Received: from localhost (ovpn-204-111.brq.redhat.com [10.40.204.111]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 919865C897; Wed, 5 Jul 2017 19:04:09 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 0BA1C61D38 Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=mreitz@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 0BA1C61D38 From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 5 Jul 2017 21:04:00 +0200 Message-Id: <20170705190404.22449-2-mreitz@redhat.com> In-Reply-To: <20170705190404.22449-1-mreitz@redhat.com> References: <20170705190404.22449-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Wed, 05 Jul 2017 19:04:10 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 1/5] qapi/qnull: Add own header X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Markus Armbruster , qemu-devel@nongnu.org, Max Reitz Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Reviewed-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Max Reitz --- include/qapi/qmp/qnull.h | 26 ++++++++++++++++++++++++++ include/qapi/qmp/qobject.h | 8 -------- include/qapi/qmp/types.h | 1 + qobject/qnull.c | 2 +- tests/check-qnull.c | 2 +- 5 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 include/qapi/qmp/qnull.h diff --git a/include/qapi/qmp/qnull.h b/include/qapi/qmp/qnull.h new file mode 100644 index 0000000..48edad4 --- /dev/null +++ b/include/qapi/qmp/qnull.h @@ -0,0 +1,26 @@ +/* + * QNull + * + * Copyright (C) 2015 Red Hat, Inc. + * + * Authors: + * Markus Armbruster + * + * 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. + */ + +#ifndef QNULL_H +#define QNULL_H + +#include "qapi/qmp/qobject.h" + +extern QObject qnull_; + +static inline QObject *qnull(void) +{ + qobject_incref(&qnull_); + return &qnull_; +} + +#endif /* QNULL_H */ diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index b8ddbca..ef1d1a9 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qapi/qmp/qobject.h @@ -93,12 +93,4 @@ static inline QType qobject_type(const QObject *obj) return obj->type; } =20 -extern QObject qnull_; - -static inline QObject *qnull(void) -{ - qobject_incref(&qnull_); - return &qnull_; -} - #endif /* QOBJECT_H */ diff --git a/include/qapi/qmp/types.h b/include/qapi/qmp/types.h index a4bc662..749ac44 100644 --- a/include/qapi/qmp/types.h +++ b/include/qapi/qmp/types.h @@ -19,5 +19,6 @@ #include "qapi/qmp/qstring.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnull.h" =20 #endif /* QAPI_QMP_TYPES_H */ diff --git a/qobject/qnull.c b/qobject/qnull.c index c124d05..43918f1 100644 --- a/qobject/qnull.c +++ b/qobject/qnull.c @@ -12,7 +12,7 @@ =20 #include "qemu/osdep.h" #include "qemu-common.h" -#include "qapi/qmp/qobject.h" +#include "qapi/qmp/qnull.h" =20 QObject qnull_ =3D { .type =3D QTYPE_QNULL, diff --git a/tests/check-qnull.c b/tests/check-qnull.c index 8dd1c96..4a67b9a 100644 --- a/tests/check-qnull.c +++ b/tests/check-qnull.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" =20 -#include "qapi/qmp/qobject.h" +#include "qapi/qmp/qnull.h" #include "qemu-common.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" --=20 2.9.4 From nobody Wed Nov 5 07:34:31 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1499281576812758.6458537469786; Wed, 5 Jul 2017 12:06:16 -0700 (PDT) Received: from localhost ([::1]:47712 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dSpct-0005L5-D2 for importer@patchew.org; Wed, 05 Jul 2017 15:06:15 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40059) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dSpb4-00042s-2t for qemu-devel@nongnu.org; Wed, 05 Jul 2017 15:04:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dSpb0-0005v0-SA for qemu-devel@nongnu.org; Wed, 05 Jul 2017 15:04:22 -0400 Received: from mx1.redhat.com ([209.132.183.28]:38588) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dSpaw-0005tH-Jc; Wed, 05 Jul 2017 15:04:14 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 85DF37C840; Wed, 5 Jul 2017 19:04:13 +0000 (UTC) Received: from localhost (ovpn-204-111.brq.redhat.com [10.40.204.111]) by smtp.corp.redhat.com (Postfix) with ESMTPS id C7043600CC; Wed, 5 Jul 2017 19:04:12 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 85DF37C840 Authentication-Results: ext-mx03.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx03.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=mreitz@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 85DF37C840 From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 5 Jul 2017 21:04:01 +0200 Message-Id: <20170705190404.22449-3-mreitz@redhat.com> In-Reply-To: <20170705190404.22449-1-mreitz@redhat.com> References: <20170705190404.22449-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Wed, 05 Jul 2017 19:04:13 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 2/5] qapi: Add qobject_is_equal() X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Markus Armbruster , qemu-devel@nongnu.org, Max Reitz Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" This generic function (along with its implementations for different types) determines whether two QObjects are equal. Signed-off-by: Max Reitz Reviewed-by: Eric Blake --- Markus also proposed just reporting two values as unequal if they have a different internal representation (i.e. a different QNum kind). I don't like this very much, because I feel like QInt and QFloat have been unified for a reason: Outside of these classes, nobody should care about the exact internal representation. In JSON, there is no difference anyway. We probably want to use integers as long as we can and doubles whenever we cannot. In any case, I feel like the class should hide the different internal representations from the user. This necessitates being able to compare floating point values against integers. Since apparently the main use of QObject is to parse and emit JSON (and represent such objects internally), we also have to agree that JSON doesn't make a difference: 42 is just the same as 42.0. Finally, I think it's rather pointless not to consider 42u and 42 the same value. But since unsigned/signed are two different kinds of QNums already, we cannot consider them equal without considering 42.0 equal, too. Because of this, I have decided to continue to compare QNum values even if they are of a different kind. --- include/qapi/qmp/qbool.h | 1 + include/qapi/qmp/qdict.h | 1 + include/qapi/qmp/qlist.h | 1 + include/qapi/qmp/qnull.h | 2 ++ include/qapi/qmp/qnum.h | 1 + include/qapi/qmp/qobject.h | 9 ++++++ include/qapi/qmp/qstring.h | 1 + qobject/qbool.c | 8 +++++ qobject/qdict.c | 29 ++++++++++++++++++ qobject/qlist.c | 32 ++++++++++++++++++++ qobject/qnull.c | 9 ++++++ qobject/qnum.c | 73 ++++++++++++++++++++++++++++++++++++++++++= ++++ qobject/qobject.c | 29 ++++++++++++++++++ qobject/qstring.c | 9 ++++++ 14 files changed, 205 insertions(+) diff --git a/include/qapi/qmp/qbool.h b/include/qapi/qmp/qbool.h index a41111c..f77ea86 100644 --- a/include/qapi/qmp/qbool.h +++ b/include/qapi/qmp/qbool.h @@ -24,6 +24,7 @@ typedef struct QBool { QBool *qbool_from_bool(bool value); bool qbool_get_bool(const QBool *qb); QBool *qobject_to_qbool(const QObject *obj); +bool qbool_is_equal(const QObject *x, const QObject *y); void qbool_destroy_obj(QObject *obj); =20 #endif /* QBOOL_H */ diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index 363e431..84f8ea7 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -42,6 +42,7 @@ void qdict_del(QDict *qdict, const char *key); int qdict_haskey(const QDict *qdict, const char *key); QObject *qdict_get(const QDict *qdict, const char *key); QDict *qobject_to_qdict(const QObject *obj); +bool qdict_is_equal(const QObject *x, const QObject *y); void qdict_iter(const QDict *qdict, void (*iter)(const char *key, QObject *obj, void *opaque), void *opaque); diff --git a/include/qapi/qmp/qlist.h b/include/qapi/qmp/qlist.h index c4b5fda..24e1e9f 100644 --- a/include/qapi/qmp/qlist.h +++ b/include/qapi/qmp/qlist.h @@ -58,6 +58,7 @@ QObject *qlist_peek(QList *qlist); int qlist_empty(const QList *qlist); size_t qlist_size(const QList *qlist); QList *qobject_to_qlist(const QObject *obj); +bool qlist_is_equal(const QObject *x, const QObject *y); void qlist_destroy_obj(QObject *obj); =20 static inline const QListEntry *qlist_first(const QList *qlist) diff --git a/include/qapi/qmp/qnull.h b/include/qapi/qmp/qnull.h index 48edad4..f4fbcae 100644 --- a/include/qapi/qmp/qnull.h +++ b/include/qapi/qmp/qnull.h @@ -23,4 +23,6 @@ static inline QObject *qnull(void) return &qnull_; } =20 +bool qnull_is_equal(const QObject *x, const QObject *y); + #endif /* QNULL_H */ diff --git a/include/qapi/qmp/qnum.h b/include/qapi/qmp/qnum.h index 09d745c..237d01b 100644 --- a/include/qapi/qmp/qnum.h +++ b/include/qapi/qmp/qnum.h @@ -48,6 +48,7 @@ double qnum_get_double(QNum *qn); char *qnum_to_string(QNum *qn); =20 QNum *qobject_to_qnum(const QObject *obj); +bool qnum_is_equal(const QObject *x, const QObject *y); void qnum_destroy_obj(QObject *obj); =20 #endif /* QNUM_H */ diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index ef1d1a9..38ac688 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qapi/qmp/qobject.h @@ -68,6 +68,15 @@ static inline void qobject_incref(QObject *obj) } =20 /** + * qobject_is_equal(): Return whether the two objects are equal. + * + * Any of the pointers may be NULL; return true if both are. Always + * return false if only one is (therefore a QNull object is not + * considered equal to a NULL pointer). + */ +bool qobject_is_equal(const QObject *x, const QObject *y); + +/** * qobject_destroy(): Free resources used by the object */ void qobject_destroy(QObject *obj); diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h index 10076b7..65c05a9 100644 --- a/include/qapi/qmp/qstring.h +++ b/include/qapi/qmp/qstring.h @@ -31,6 +31,7 @@ void qstring_append_int(QString *qstring, int64_t value); void qstring_append(QString *qstring, const char *str); void qstring_append_chr(QString *qstring, int c); QString *qobject_to_qstring(const QObject *obj); +bool qstring_is_equal(const QObject *x, const QObject *y); void qstring_destroy_obj(QObject *obj); =20 #endif /* QSTRING_H */ diff --git a/qobject/qbool.c b/qobject/qbool.c index 0606bbd..ac825fc 100644 --- a/qobject/qbool.c +++ b/qobject/qbool.c @@ -52,6 +52,14 @@ QBool *qobject_to_qbool(const QObject *obj) } =20 /** + * qbool_is_equal(): Test whether the two QBools are equal + */ +bool qbool_is_equal(const QObject *x, const QObject *y) +{ + return qobject_to_qbool(x)->value =3D=3D qobject_to_qbool(y)->value; +} + +/** * qbool_destroy_obj(): Free all memory allocated by a * QBool object */ diff --git a/qobject/qdict.c b/qobject/qdict.c index 576018e..e8f15f1 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -403,6 +403,35 @@ void qdict_del(QDict *qdict, const char *key) } =20 /** + * qdict_is_equal(): Test whether the two QDicts are equal + * + * Here, equality means whether they contain the same keys and whether + * the respective values are in turn equal (i.e. invoking + * qobject_is_equal() on them yields true). + */ +bool qdict_is_equal(const QObject *x, const QObject *y) +{ + const QDict *dict_x =3D qobject_to_qdict(x); + const QDict *dict_y =3D qobject_to_qdict(y); + const QDictEntry *e; + + if (qdict_size(dict_x) !=3D qdict_size(dict_y)) { + return false; + } + + for (e =3D qdict_first(dict_x); e; e =3D qdict_next(dict_x, e)) { + const QObject *obj_x =3D qdict_entry_value(e); + const QObject *obj_y =3D qdict_get(dict_y, qdict_entry_key(e)); + + if (!qobject_is_equal(obj_x, obj_y)) { + return false; + } + } + + return true; +} + +/** * qdict_destroy_obj(): Free all the memory allocated by a QDict */ void qdict_destroy_obj(QObject *obj) diff --git a/qobject/qlist.c b/qobject/qlist.c index 86b60cb..3ef57d3 100644 --- a/qobject/qlist.c +++ b/qobject/qlist.c @@ -140,6 +140,38 @@ QList *qobject_to_qlist(const QObject *obj) } =20 /** + * qlist_is_equal(): Test whether the two QLists are equal + * + * In order to be considered equal, the respective two objects at each + * index of the two lists have to compare equal (regarding + * qobject_is_equal()), and both lists have to have the same number of + * elements. + * That means both lists have to contain equal objects in equal order. + */ +bool qlist_is_equal(const QObject *x, const QObject *y) +{ + const QList *list_x =3D qobject_to_qlist(x); + const QList *list_y =3D qobject_to_qlist(y); + const QListEntry *entry_x, *entry_y; + + entry_x =3D qlist_first(list_x); + entry_y =3D qlist_first(list_y); + + while (entry_x && entry_y) { + if (!qobject_is_equal(qlist_entry_obj(entry_x), + qlist_entry_obj(entry_y))) + { + return false; + } + + entry_x =3D qlist_next(entry_x); + entry_y =3D qlist_next(entry_y); + } + + return !entry_x && !entry_y; +} + +/** * qlist_destroy_obj(): Free all the memory allocated by a QList */ void qlist_destroy_obj(QObject *obj) diff --git a/qobject/qnull.c b/qobject/qnull.c index 43918f1..4b9cdbc 100644 --- a/qobject/qnull.c +++ b/qobject/qnull.c @@ -18,3 +18,12 @@ QObject qnull_ =3D { .type =3D QTYPE_QNULL, .refcnt =3D 1, }; + +/** + * qnull_is_equal(): Always return true because any two QNull objects + * are equal. + */ +bool qnull_is_equal(const QObject *x, const QObject *y) +{ + return true; +} diff --git a/qobject/qnum.c b/qobject/qnum.c index 476e81c..96c348c 100644 --- a/qobject/qnum.c +++ b/qobject/qnum.c @@ -18,6 +18,8 @@ #include "qapi/qmp/qobject.h" #include "qemu-common.h" =20 +#include + /** * qnum_from_int(): Create a new QNum from an int64_t * @@ -213,6 +215,77 @@ QNum *qobject_to_qnum(const QObject *obj) } =20 /** + * qnum_is_equal(): Test whether the two QNums are equal + * + * Negative integers are never considered equal to unsigned integers. + * Doubles are only considered equal to integers if their fractional + * part is zero and their integral part is exactly equal to the + * integer. Because doubles have limited precision, there are + * therefore integers which do not have an equal double (e.g. + * INT64_MAX). + */ +bool qnum_is_equal(const QObject *x, const QObject *y) +{ + QNum *num_x =3D qobject_to_qnum(x); + QNum *num_y =3D qobject_to_qnum(y); + double integral_part; /* Needed for the modf() calls below */ + + switch (num_x->kind) { + case QNUM_I64: + switch (num_y->kind) { + case QNUM_I64: + /* Comparison in native int64_t type */ + return num_x->u.i64 =3D=3D num_y->u.i64; + case QNUM_U64: + /* Implicit conversion of x to uin64_t, so we have to + * check its sign before */ + return num_x->u.i64 >=3D 0 && num_x->u.i64 =3D=3D num_y->u.u64; + case QNUM_DOUBLE: + /* Comparing x to y in double (which the implicit + * conversion would do) is not exact. So after having + * checked that y is an integer in the int64_t range + * (i.e. that it is within bounds and its fractional part + * is zero), compare both as integers. */ + return num_y->u.dbl >=3D -0x1p63 && num_y->u.dbl < 0x1p63 && + modf(num_y->u.dbl, &integral_part) =3D=3D 0.0 && + num_x->u.i64 =3D=3D (int64_t)num_y->u.dbl; + } + abort(); + case QNUM_U64: + switch (num_y->kind) { + case QNUM_I64: + return qnum_is_equal(y, x); + case QNUM_U64: + /* Comparison in native uint64_t type */ + return num_x->u.u64 =3D=3D num_y->u.u64; + case QNUM_DOUBLE: + /* Comparing x to y in double (which the implicit + * conversion would do) is not exact. So after having + * checked that y is an integer in the uint64_t range + * (i.e. that it is within bounds and its fractional part + * is zero), compare both as integers. */ + return num_y->u.dbl >=3D 0 && num_y->u.dbl < 0x1p64 && + modf(num_y->u.dbl, &integral_part) =3D=3D 0.0 && + num_x->u.u64 =3D=3D (uint64_t)num_y->u.dbl; + } + abort(); + case QNUM_DOUBLE: + switch (num_y->kind) { + case QNUM_I64: + return qnum_is_equal(y, x); + case QNUM_U64: + return qnum_is_equal(y, x); + case QNUM_DOUBLE: + /* Comparison in native double type */ + return num_x->u.dbl =3D=3D num_y->u.dbl; + } + abort(); + } + + abort(); +} + +/** * qnum_destroy_obj(): Free all memory allocated by a * QNum object */ diff --git a/qobject/qobject.c b/qobject/qobject.c index b0cafb6..b2a5360 100644 --- a/qobject/qobject.c +++ b/qobject/qobject.c @@ -27,3 +27,32 @@ void qobject_destroy(QObject *obj) assert(QTYPE_QNULL < obj->type && obj->type < QTYPE__MAX); qdestroy[obj->type](obj); } + + +static bool (*qis_equal[QTYPE__MAX])(const QObject *, const QObject *) =3D= { + [QTYPE_NONE] =3D NULL, /* No such object exists */ + [QTYPE_QNULL] =3D qnull_is_equal, + [QTYPE_QNUM] =3D qnum_is_equal, + [QTYPE_QSTRING] =3D qstring_is_equal, + [QTYPE_QDICT] =3D qdict_is_equal, + [QTYPE_QLIST] =3D qlist_is_equal, + [QTYPE_QBOOL] =3D qbool_is_equal, +}; + +bool qobject_is_equal(const QObject *x, const QObject *y) +{ + /* We cannot test x =3D=3D y because an object does not need to be + * equal to itself (e.g. NaN floats are not). */ + + if (!x && !y) { + return true; + } + + if (!x || !y || x->type !=3D y->type) { + return false; + } + + assert(QTYPE_NONE < x->type && x->type < QTYPE__MAX); + + return qis_equal[x->type](x, y); +} diff --git a/qobject/qstring.c b/qobject/qstring.c index 5da7b5f..74182a1 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -129,6 +129,15 @@ const char *qstring_get_str(const QString *qstring) } =20 /** + * qstring_is_equal(): Test whether the two QStrings are equal + */ +bool qstring_is_equal(const QObject *x, const QObject *y) +{ + return !strcmp(qobject_to_qstring(x)->string, + qobject_to_qstring(y)->string); +} + +/** * qstring_destroy_obj(): Free all memory allocated by a QString * object */ --=20 2.9.4 From nobody Wed Nov 5 07:34:31 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1499281576932968.0718542552729; Wed, 5 Jul 2017 12:06:16 -0700 (PDT) Received: from localhost ([::1]:47711 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dSpcq-0005J0-LC for importer@patchew.org; Wed, 05 Jul 2017 15:06:12 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40075) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dSpb5-00043k-6N for qemu-devel@nongnu.org; Wed, 05 Jul 2017 15:04:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dSpb4-0005wN-76 for qemu-devel@nongnu.org; Wed, 05 Jul 2017 15:04:23 -0400 Received: from mx1.redhat.com ([209.132.183.28]:60726) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dSpaz-0005uM-Gd; Wed, 05 Jul 2017 15:04:17 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 7D9EFC056791; Wed, 5 Jul 2017 19:04:16 +0000 (UTC) Received: from localhost (ovpn-204-111.brq.redhat.com [10.40.204.111]) by smtp.corp.redhat.com (Postfix) with ESMTPS id F01915D723; Wed, 5 Jul 2017 19:04:15 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 7D9EFC056791 Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=mreitz@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 7D9EFC056791 From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 5 Jul 2017 21:04:02 +0200 Message-Id: <20170705190404.22449-4-mreitz@redhat.com> In-Reply-To: <20170705190404.22449-1-mreitz@redhat.com> References: <20170705190404.22449-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Wed, 05 Jul 2017 19:04:16 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 3/5] block: qobject_is_equal() in bdrv_reopen_prepare() X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Markus Armbruster , qemu-devel@nongnu.org, Max Reitz Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Currently, bdrv_reopen_prepare() assumes that all BDS options are strings. However, this is not the case if the BDS has been created through the json: pseudo-protocol or blockdev-add. Note that the user-invokable reopen command is an HMP command, so you can only specify strings there. Therefore, specifying a non-string option with the "same" value as it was when originally created will now return an error because the values are supposedly similar (and there is no way for the user to circumvent this but to just not specify the option again -- however, this is still strictly better than just crashing). Signed-off-by: Max Reitz Reviewed-by: Eric Blake --- block.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/block.c b/block.c index 913bb43..f8c8e92 100644 --- a/block.c +++ b/block.c @@ -2947,19 +2947,26 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_sta= te, BlockReopenQueue *queue, const QDictEntry *entry =3D qdict_first(reopen_state->options); =20 do { - QString *new_obj =3D qobject_to_qstring(entry->value); - const char *new =3D qstring_get_str(new_obj); + QObject *new =3D entry->value; + QObject *old =3D qdict_get(reopen_state->bs->options, entry->k= ey); + /* - * Caution: while qdict_get_try_str() is fine, getting - * non-string types would require more care. When - * bs->options come from -blockdev or blockdev_add, its - * members are typed according to the QAPI schema, but - * when they come from -drive, they're all QString. + * TODO: When using -drive to specify blockdev options, all va= lues + * will be strings; however, when using -blockdev, blockdev-ad= d or + * filenames using the json:{} pseudo-protocol, they will be + * correctly typed. + * In contrast, reopening options are (currently) always strin= gs + * (because you can only specify them through qemu-io; all oth= er + * callers do not specify any options). + * Therefore, when using anything other than -drive to create = a BDS, + * this cannot detect non-string options as unchanged, because + * qobject_is_equal() always returns false for objects of diff= erent + * type. In the future, this should be remedied by correctly = typing + * all options. For now, this is not too big of an issue beca= use + * the user can simply omit options which cannot be changed an= yway, + * so they will stay unchanged. */ - const char *old =3D qdict_get_try_str(reopen_state->bs->option= s, - entry->key); - - if (!old || strcmp(new, old)) { + if (!qobject_is_equal(new, old)) { error_setg(errp, "Cannot change the option '%s'", entry->k= ey); ret =3D -EINVAL; goto error; --=20 2.9.4 From nobody Wed Nov 5 07:34:31 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1499281691582274.8915674297591; Wed, 5 Jul 2017 12:08:11 -0700 (PDT) Received: from localhost ([::1]:47721 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dSpei-0006rn-9b for importer@patchew.org; Wed, 05 Jul 2017 15:08:08 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40096) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dSpb6-00044L-4a for qemu-devel@nongnu.org; Wed, 05 Jul 2017 15:04:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dSpb5-0005wx-BE for qemu-devel@nongnu.org; Wed, 05 Jul 2017 15:04:24 -0400 Received: from mx1.redhat.com ([209.132.183.28]:40326) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dSpb3-0005ve-1E; Wed, 05 Jul 2017 15:04:21 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id DC8EBA4FB9; Wed, 5 Jul 2017 19:04:19 +0000 (UTC) Received: from localhost (ovpn-204-111.brq.redhat.com [10.40.204.111]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 3D99C60A99; Wed, 5 Jul 2017 19:04:19 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com DC8EBA4FB9 Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=mreitz@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com DC8EBA4FB9 From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 5 Jul 2017 21:04:03 +0200 Message-Id: <20170705190404.22449-5-mreitz@redhat.com> In-Reply-To: <20170705190404.22449-1-mreitz@redhat.com> References: <20170705190404.22449-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Wed, 05 Jul 2017 19:04:20 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 4/5] iotests: Add test for non-string option reopening X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Markus Armbruster , qemu-devel@nongnu.org, Max Reitz Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Reviewed-by: Kevin Wolf Signed-off-by: Max Reitz --- tests/qemu-iotests/133 | 9 +++++++++ tests/qemu-iotests/133.out | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/tests/qemu-iotests/133 b/tests/qemu-iotests/133 index 9d35a6a..af6b3e1 100755 --- a/tests/qemu-iotests/133 +++ b/tests/qemu-iotests/133 @@ -83,6 +83,15 @@ $QEMU_IO -c 'reopen -o driver=3Dqcow2' $TEST_IMG $QEMU_IO -c 'reopen -o file.driver=3Dfile' $TEST_IMG $QEMU_IO -c 'reopen -o backing.driver=3Dqcow2' $TEST_IMG =20 +echo +echo "=3D=3D=3D Check that reopening works with non-string options =3D=3D= =3D" +echo + +# Using the json: pseudo-protocol we can create non-string options +# (Invoke 'info' just so we get some output afterwards) +IMGOPTSSYNTAX=3Dfalse $QEMU_IO -f null-co -c 'reopen' -c 'info' \ + "json:{'driver': 'null-co', 'size': 65536}" + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/133.out b/tests/qemu-iotests/133.out index cc86b94..f4a85ae 100644 --- a/tests/qemu-iotests/133.out +++ b/tests/qemu-iotests/133.out @@ -19,4 +19,9 @@ Cannot change the option 'driver' =20 =3D=3D=3D Check that unchanged driver is okay =3D=3D=3D =20 + +=3D=3D=3D Check that reopening works with non-string options =3D=3D=3D + +format name: null-co +format name: null-co *** done --=20 2.9.4 From nobody Wed Nov 5 07:34:31 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1499281770539565.2328393275051; Wed, 5 Jul 2017 12:09:30 -0700 (PDT) Received: from localhost ([::1]:47731 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dSpg0-0008MZ-0Z for importer@patchew.org; Wed, 05 Jul 2017 15:09:28 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40187) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dSpbF-0004Cj-GI for qemu-devel@nongnu.org; Wed, 05 Jul 2017 15:04:35 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dSpbC-0005zI-9w for qemu-devel@nongnu.org; Wed, 05 Jul 2017 15:04:33 -0400 Received: from mx1.redhat.com ([209.132.183.28]:44758) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dSpb6-0005xB-Tl; Wed, 05 Jul 2017 15:04:25 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id DB10D4E4D2; Wed, 5 Jul 2017 19:04:23 +0000 (UTC) Received: from localhost (ovpn-204-111.brq.redhat.com [10.40.204.111]) by smtp.corp.redhat.com (Postfix) with ESMTPS id B315160BE3; Wed, 5 Jul 2017 19:04:22 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com DB10D4E4D2 Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=mreitz@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com DB10D4E4D2 From: Max Reitz To: qemu-block@nongnu.org Date: Wed, 5 Jul 2017 21:04:04 +0200 Message-Id: <20170705190404.22449-6-mreitz@redhat.com> In-Reply-To: <20170705190404.22449-1-mreitz@redhat.com> References: <20170705190404.22449-1-mreitz@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Wed, 05 Jul 2017 19:04:24 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 5/5] tests: Add check-qobject for equality tests X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Markus Armbruster , qemu-devel@nongnu.org, Max Reitz Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Add a new test file (check-qobject.c) for unit tests that concern QObjects as a whole. Its only purpose for now is to test the qobject_is_equal() function. Signed-off-by: Max Reitz Reviewed-by: Eric Blake --- tests/Makefile.include | 4 +- qobject/qnum.c | 16 +- tests/check-qobject.c | 404 +++++++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 417 insertions(+), 7 deletions(-) create mode 100644 tests/check-qobject.c diff --git a/tests/Makefile.include b/tests/Makefile.include index 42e17e2..07b130c 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -18,6 +18,7 @@ check-unit-y +=3D tests/check-qlist$(EXESUF) gcov-files-check-qlist-y =3D qobject/qlist.c check-unit-y +=3D tests/check-qnull$(EXESUF) gcov-files-check-qnull-y =3D qobject/qnull.c +check-unit-y +=3D tests/check-qobject$(EXESUF) check-unit-y +=3D tests/check-qjson$(EXESUF) gcov-files-check-qjson-y =3D qobject/qjson.c check-unit-y +=3D tests/test-qobject-output-visitor$(EXESUF) @@ -508,7 +509,7 @@ GENERATED_FILES +=3D tests/test-qapi-types.h tests/test= -qapi-visit.h \ tests/test-qmp-introspect.h =20 test-obj-y =3D tests/check-qnum.o tests/check-qstring.o tests/check-qdict.= o \ - tests/check-qlist.o tests/check-qnull.o \ + tests/check-qlist.o tests/check-qnull.o tests/check-qobject.o \ tests/check-qjson.o \ tests/test-coroutine.o tests/test-string-output-visitor.o \ tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \ @@ -541,6 +542,7 @@ tests/check-qstring$(EXESUF): tests/check-qstring.o $(t= est-util-obj-y) tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y) tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y) tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y) +tests/check-qobject$(EXESUF): tests/check-qobject.o $(test-util-obj-y) tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y) tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom= -obj-y) tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-o= bj-y) diff --git a/qobject/qnum.c b/qobject/qnum.c index 96c348c..3d029f6 100644 --- a/qobject/qnum.c +++ b/qobject/qnum.c @@ -217,12 +217,16 @@ QNum *qobject_to_qnum(const QObject *obj) /** * qnum_is_equal(): Test whether the two QNums are equal * - * Negative integers are never considered equal to unsigned integers. - * Doubles are only considered equal to integers if their fractional - * part is zero and their integral part is exactly equal to the - * integer. Because doubles have limited precision, there are - * therefore integers which do not have an equal double (e.g. - * INT64_MAX). + * This comparison is done independently of the internal + * representation. Any two numbers are considered equal if they are + * mathmatically equal, that means: + * - Negative integers are never considered equal to unsigned + * integers. + * - Floating point values are only considered equal to integers if + * their fractional part is zero and their integral part is exactly + * equal to the integer. Because doubles have limited precision, + * there are therefore integers which do not have an equal floating + * point value (e.g. INT64_MAX). */ bool qnum_is_equal(const QObject *x, const QObject *y) { diff --git a/tests/check-qobject.c b/tests/check-qobject.c new file mode 100644 index 0000000..fd964bf --- /dev/null +++ b/tests/check-qobject.c @@ -0,0 +1,404 @@ +/* + * Generic QObject unit-tests. + * + * Copyright (C) 2017 Red Hat Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or l= ater. + * See the COPYING.LIB file in the top-level directory. + */ +#include "qemu/osdep.h" + +#include "qapi/qmp/types.h" +#include "qemu-common.h" + +/* Marks the end of the test_equality() argument list. + * We cannot use NULL there because that is a valid argument. */ +static QObject _test_equality_end_of_arguments; + +/** + * Test whether all variadic QObject *arguments are equal (@expected + * is true) or whether they are all not equal (@expected is false). + * Every QObject is tested to be equal to itself (to test + * reflexivity), all tests are done both ways (to test symmetry), and + * transitivity is not assumed but checked (each object is compared to + * every other one). + * + * Note that qobject_is_equal() is not really an equivalence relation, + * so this function may not be used for all objects (reflexivity is + * not guaranteed, e.g. in the case of a QNum containing NaN). + */ +static void do_test_equality(bool expected, ...) +{ + va_list ap_count, ap_extract; + QObject **args; + int arg_count =3D 0; + int i, j; + + va_start(ap_count, expected); + va_copy(ap_extract, ap_count); + while (va_arg(ap_count, QObject *) !=3D &_test_equality_end_of_argumen= ts) { + arg_count++; + } + va_end(ap_count); + + args =3D g_new(QObject *, arg_count); + for (i =3D 0; i < arg_count; i++) { + args[i] =3D va_arg(ap_extract, QObject *); + } + va_end(ap_extract); + + for (i =3D 0; i < arg_count; i++) { + g_assert(qobject_is_equal(args[i], args[i]) =3D=3D true); + + for (j =3D i + 1; j < arg_count; j++) { + g_assert(qobject_is_equal(args[i], args[j]) =3D=3D expected); + } + } +} + +#define test_equality(expected, ...) \ + do_test_equality(expected, __VA_ARGS__, &_test_equality_end_of_argumen= ts) + +static void do_free_all(int _, ...) +{ + va_list ap; + QObject *obj; + + va_start(ap, _); + while ((obj =3D va_arg(ap, QObject *)) !=3D NULL) { + qobject_decref(obj); + } + va_end(ap); +} + +#define free_all(...) \ + do_free_all(0, __VA_ARGS__, NULL) + +static void qobject_is_equal_null_test(void) +{ + test_equality(false, qnull(), NULL); +} + +static void qobject_is_equal_num_test(void) +{ + QNum *u0, *i0, *d0, *d0p25, *dnan, *um42, *im42, *dm42; + QNum *umax, *imax, *umax_exact, *umax_exact_p1; + QNum *dumax, *dimax, *dumax_exact, *dumax_exact_p1; + QString *s0, *s_empty; + QBool *bfalse; + + u0 =3D qnum_from_uint(0u); + i0 =3D qnum_from_int(0); + d0 =3D qnum_from_double(0.0); + d0p25 =3D qnum_from_double(0.25); + dnan =3D qnum_from_double(0.0 / 0.0); + um42 =3D qnum_from_uint((uint64_t)-42); + im42 =3D qnum_from_int(-42); + dm42 =3D qnum_from_int(-42.0); + + /* 2^64 - 1: Not exactly representable as a double (needs 64 bits + * of precision, but double only has 53). The double equivalent + * may be either 2^64 or 2^64 - 2^11. */ + umax =3D qnum_from_uint(UINT64_MAX); + + /* 2^63 - 1: Not exactly representable as a double (needs 63 bits + * of precision, but double only has 53). The double equivalent + * may be either 2^63 or 2^63 - 2^10. */ + imax =3D qnum_from_int(INT64_MAX); + /* 2^64 - 2^11: Exactly representable as a double (the least + * significant 11 bits are set to 0, so we only need the 53 bits + * of precision double offers). This is the maximum value which + * is exactly representable both as a uint64_t and a double. */ + umax_exact =3D qnum_from_uint(UINT64_MAX - 0x7ff); + + /* 2^64 - 2^11 + 1: Not exactly representable as a double (needs + * 64 bits again), but whereas (double)UINT64_MAX may be rounded + * up to 2^64, this will most likely be rounded down to + * 2^64 - 2^11. */ + umax_exact_p1 =3D qnum_from_uint(UINT64_MAX - 0x7ff + 1); + + dumax =3D qnum_from_double((double)qnum_get_uint(umax)); + dimax =3D qnum_from_double((double)qnum_get_int(imax)); + dumax_exact =3D qnum_from_double((double)qnum_get_uint(umax_exact)); + dumax_exact_p1 =3D qnum_from_double((double)qnum_get_uint(umax_exact_p= 1)); + + s0 =3D qstring_from_str("0"); + s_empty =3D qstring_new(); + bfalse =3D qbool_from_bool(false); + + /* The internal representation should not matter, as long as the + * precision is sufficient */ + test_equality(true, u0, i0, d0); + + /* No automatic type conversion */ + test_equality(false, u0, s0, s_empty, bfalse, qnull(), NULL); + test_equality(false, i0, s0, s_empty, bfalse, qnull(), NULL); + test_equality(false, d0, s0, s_empty, bfalse, qnull(), NULL); + + /* Do not round */ + test_equality(false, u0, d0p25); + test_equality(false, i0, d0p25); + + /* Do not assume any object is equal to itself -- note however + * that NaN cannot occur in a JSON object anyway. */ + g_assert(qobject_is_equal(QOBJECT(dnan), QOBJECT(dnan)) =3D=3D false); + + /* No unsigned overflow */ + test_equality(false, um42, im42); + test_equality(false, um42, dm42); + test_equality(true, im42, dm42); + + + /* + * Floating point values must match integers exactly to be + * considered equal; it does not suffice that converting the + * integer to a double yields the same value. + * Each of the following four tests follows the same pattern: + * 1. Check that both QNum objects compare unequal because they + * are (mathematically). The third test is an exception, + * because here they are indeed equal. + * 2. Check that when converting the integer QNum to a double, + * that value is equal to the double QNum. We can thus see + * that the QNum comparison does not simply convert the + * integer to a floating point value (in a potentially lossy + * operation). + * 3. Sanity checks: Check that the double QNum has the expected + * value (which may be one of two in case it was rounded; the + * exact result is then implementation-defined). + * If there are multiple valid values, check that they are + * distinct values when represented as double (just proving + * that our assumptions about the precision of doubles are + * correct). + * + * The first two tests are interesting because they may involve a + * double value which is out of the uint64_t or int64_t range, + * respectively (if it is rounded to 2^64 or 2^63 during + * conversion). + * + * Since both are intended to involve rounding the value up during + * conversion, we also have the fourth test which is indended to + * test behavior if the value was rounded down. This is the fourth + * test. + * + * The third test simply proves that the value used in the fourth + * test is indeed just one above a number that can be exactly + * represented in a double. + */ + + test_equality(false, umax, dumax); + g_assert(qnum_get_double(umax) =3D=3D qnum_get_double(dumax)); + g_assert(qnum_get_double(dumax) =3D=3D 0x1p64 || + qnum_get_double(dumax) =3D=3D 0x1p64 - 0x1p11); + g_assert(0x1p64 !=3D 0x1p64 - 0x1p11); + + test_equality(false, imax, dimax); + g_assert(qnum_get_double(imax) =3D=3D qnum_get_double(dimax)); + g_assert(qnum_get_double(dimax) =3D=3D 0x1p63 || + qnum_get_double(dimax) =3D=3D 0x1p63 - 0x1p10); + g_assert(0x1p63 !=3D 0x1p63 - 0x1p10); + + test_equality(true, umax_exact, dumax_exact); + g_assert(qnum_get_double(umax_exact) =3D=3D qnum_get_double(dumax_exac= t)); + g_assert(qnum_get_double(dumax_exact) =3D=3D 0x1p64 - 0x1p11); + + test_equality(false, umax_exact_p1, dumax_exact_p1); + g_assert(qnum_get_double(umax_exact_p1) =3D=3D qnum_get_double(dumax_e= xact_p1)); + g_assert(qnum_get_double(dumax_exact_p1) =3D=3D 0x1p64 || + qnum_get_double(dumax_exact_p1) =3D=3D 0x1p64 - 0x1p11); + g_assert(0x1p64 !=3D 0x1p64 - 0x1p11); + + + free_all(u0, i0, d0, d0p25, dnan, um42, im42, dm42, + umax, imax, umax_exact, umax_exact_p1, + dumax, dimax, dumax_exact, dumax_exact_p1, + s0, s_empty, bfalse); +} + +static void qobject_is_equal_bool_test(void) +{ + QBool *btrue_0, *btrue_1, *bfalse_0, *bfalse_1; + + /* Automatic type conversion is tested in the QNum test */ + + btrue_0 =3D qbool_from_bool(true); + btrue_1 =3D qbool_from_bool(true); + bfalse_0 =3D qbool_from_bool(false); + bfalse_1 =3D qbool_from_bool(false); + + test_equality(true, btrue_0, btrue_1); + test_equality(true, bfalse_0, bfalse_1); + test_equality(false, btrue_0, bfalse_0); + test_equality(false, btrue_1, bfalse_1); + + free_all(btrue_0, btrue_1, bfalse_0, bfalse_1); +} + +static void qobject_is_equal_string_test(void) +{ + QString *str_base, *str_whitespace_0, *str_whitespace_1, *str_whitespa= ce_2; + QString *str_whitespace_3, *str_case, *str_built; + + str_base =3D qstring_from_str("foo"); + str_whitespace_0 =3D qstring_from_str(" foo"); + str_whitespace_1 =3D qstring_from_str("foo "); + str_whitespace_2 =3D qstring_from_str("foo\b"); + str_whitespace_3 =3D qstring_from_str("fooo\b"); + str_case =3D qstring_from_str("Foo"); + + /* Should yield "foo" */ + str_built =3D qstring_from_substr("form", 0, 1); + qstring_append_chr(str_built, 'o'); + + test_equality(false, str_base, str_whitespace_0, str_whitespace_1, + str_whitespace_2, str_whitespace_3, str_case); + + test_equality(true, str_base, str_built); + + free_all(str_base, str_whitespace_0, str_whitespace_1, str_whitespace_= 2, + str_whitespace_3, str_case, str_built); +} + +static void qobject_is_equal_list_test(void) +{ + QList *list_0, *list_1, *list_cloned; + QList *list_reordered, *list_longer, *list_shorter; + + list_0 =3D qlist_new(); + list_1 =3D qlist_new(); + list_reordered =3D qlist_new(); + list_longer =3D qlist_new(); + list_shorter =3D qlist_new(); + + qlist_append_int(list_0, 1); + qlist_append_int(list_0, 2); + qlist_append_int(list_0, 3); + + qlist_append_int(list_1, 1); + qlist_append_int(list_1, 2); + qlist_append_int(list_1, 3); + + qlist_append_int(list_reordered, 1); + qlist_append_int(list_reordered, 3); + qlist_append_int(list_reordered, 2); + + qlist_append_int(list_longer, 1); + qlist_append_int(list_longer, 2); + qlist_append_int(list_longer, 3); + qlist_append_obj(list_longer, qnull()); + + qlist_append_int(list_shorter, 1); + qlist_append_int(list_shorter, 2); + + list_cloned =3D qlist_copy(list_0); + + test_equality(true, list_0, list_1, list_cloned); + test_equality(false, list_0, list_reordered, list_longer, list_shorter= ); + + /* With a NaN in it, the list should no longer compare equal to + * itself */ + qlist_append(list_0, qnum_from_double(0.0 / 0.0)); + g_assert(qobject_is_equal(QOBJECT(list_0), QOBJECT(list_0)) =3D=3D fal= se); + + free_all(list_0, list_1, list_cloned, list_reordered, list_longer, lis= t_shorter); +} + +static void qobject_is_equal_dict_test(void) +{ + Error *local_err =3D NULL; + QDict *dict_0, *dict_1, *dict_cloned; + QDict *dict_different_key, *dict_different_value, *dict_different_null= _key; + QDict *dict_longer, *dict_shorter, *dict_nested; + QDict *dict_crumpled; + + dict_0 =3D qdict_new(); + dict_1 =3D qdict_new(); + dict_different_key =3D qdict_new(); + dict_different_value =3D qdict_new(); + dict_different_null_key =3D qdict_new(); + dict_longer =3D qdict_new(); + dict_shorter =3D qdict_new(); + dict_nested =3D qdict_new(); + + qdict_put_int(dict_0, "f.o", 1); + qdict_put_int(dict_0, "bar", 2); + qdict_put_int(dict_0, "baz", 3); + qdict_put_obj(dict_0, "null", qnull()); + + qdict_put_int(dict_1, "f.o", 1); + qdict_put_int(dict_1, "bar", 2); + qdict_put_int(dict_1, "baz", 3); + qdict_put_obj(dict_1, "null", qnull()); + + qdict_put_int(dict_different_key, "F.o", 1); + qdict_put_int(dict_different_key, "bar", 2); + qdict_put_int(dict_different_key, "baz", 3); + qdict_put_obj(dict_different_key, "null", qnull()); + + qdict_put_int(dict_different_value, "f.o", 42); + qdict_put_int(dict_different_value, "bar", 2); + qdict_put_int(dict_different_value, "baz", 3); + qdict_put_obj(dict_different_value, "null", qnull()); + + qdict_put_int(dict_different_null_key, "f.o", 1); + qdict_put_int(dict_different_null_key, "bar", 2); + qdict_put_int(dict_different_null_key, "baz", 3); + qdict_put_obj(dict_different_null_key, "none", qnull()); + + qdict_put_int(dict_longer, "f.o", 1); + qdict_put_int(dict_longer, "bar", 2); + qdict_put_int(dict_longer, "baz", 3); + qdict_put_int(dict_longer, "xyz", 4); + qdict_put_obj(dict_longer, "null", qnull()); + + qdict_put_int(dict_shorter, "f.o", 1); + qdict_put_int(dict_shorter, "bar", 2); + qdict_put_int(dict_shorter, "baz", 3); + + qdict_put(dict_nested, "f", qdict_new()); + qdict_put_int(qdict_get_qdict(dict_nested, "f"), "o", 1); + qdict_put_int(dict_nested, "bar", 2); + qdict_put_int(dict_nested, "baz", 3); + qdict_put_obj(dict_nested, "null", qnull()); + + dict_cloned =3D qdict_clone_shallow(dict_0); + + test_equality(true, dict_0, dict_1, dict_cloned); + test_equality(false, dict_0, dict_different_key, dict_different_value, + dict_different_null_key, dict_longer, dict_shorte= r, + dict_nested); + + dict_crumpled =3D qobject_to_qdict(qdict_crumple(dict_1, &local_err)); + g_assert(!local_err); + test_equality(true, dict_crumpled, dict_nested); + + qdict_flatten(dict_nested); + test_equality(true, dict_0, dict_nested); + + /* Containing an NaN value will make this dict compare unequal to + * itself */ + qdict_put(dict_0, "NaN", qnum_from_double(0.0 / 0.0)); + g_assert(qobject_is_equal(QOBJECT(dict_0), QOBJECT(dict_0)) =3D=3D fal= se); + + free_all(dict_0, dict_1, dict_cloned, dict_different_key, + dict_different_value, dict_different_null_key, dict_longer, + dict_shorter, dict_nested, dict_crumpled); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/public/qobject_is_equal_null", + qobject_is_equal_null_test); + g_test_add_func("/public/qobject_is_equal_num", qobject_is_equal_num_t= est); + g_test_add_func("/public/qobject_is_equal_bool", + qobject_is_equal_bool_test); + g_test_add_func("/public/qobject_is_equal_string", + qobject_is_equal_string_test); + g_test_add_func("/public/qobject_is_equal_list", + qobject_is_equal_list_test); + g_test_add_func("/public/qobject_is_equal_dict", + qobject_is_equal_dict_test); + + return g_test_run(); +} --=20 2.9.4