From patchwork Fri Jun 12 16:23:22 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Wolf X-Patchwork-Id: 483692 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id A42B4140129 for ; Sat, 13 Jun 2015 02:31:34 +1000 (AEST) Received: from localhost ([::1]:52526 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z3Rrg-000268-KF for incoming@patchwork.ozlabs.org; Fri, 12 Jun 2015 12:31:32 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:59577) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z3RkS-0004qc-A1 for qemu-devel@nongnu.org; Fri, 12 Jun 2015 12:24:05 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Z3RkR-0004zU-2c for qemu-devel@nongnu.org; Fri, 12 Jun 2015 12:24:04 -0400 Received: from mx1.redhat.com ([209.132.183.28]:47204) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z3RkM-0004xz-Ds; Fri, 12 Jun 2015 12:23:58 -0400 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (Postfix) with ESMTPS id 19B8E3674B8; Fri, 12 Jun 2015 16:23:58 +0000 (UTC) Received: from noname.redhat.com (ovpn-116-102.ams2.redhat.com [10.36.116.102]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t5CGNck2006646; Fri, 12 Jun 2015 12:23:57 -0400 From: Kevin Wolf To: qemu-block@nongnu.org Date: Fri, 12 Jun 2015 18:23:22 +0200 Message-Id: <1434126214-11681-14-git-send-email-kwolf@redhat.com> In-Reply-To: <1434126214-11681-1-git-send-email-kwolf@redhat.com> References: <1434126214-11681-1-git-send-email-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [Qemu-devel] [PULL 13/25] qdict: Add qdict_array_entries() X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This counts the entries in a flattened array in a QDict without actually splitting the QDict into a QList. bdrv_open_image() doesn't take a QList, but rather a QDict and a key prefix string, so this is more convenient for block drivers which have a dynamically sized list of child nodes (e.g. Quorum) and are to be converted to using bdrv_open_image() as the standard interface for opening child nodes. Signed-off-by: Kevin Wolf Reviewed-by: Eric Blake Reviewed-by: Max Reitz --- include/qapi/qmp/qdict.h | 1 + qobject/qdict.c | 78 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index d68f4eb..d20db94 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -70,6 +70,7 @@ void qdict_flatten(QDict *qdict); void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); void qdict_array_split(QDict *src, QList **dst); +int qdict_array_entries(QDict *src, const char *subqdict); void qdict_join(QDict *dest, QDict *src, bool overwrite); diff --git a/qobject/qdict.c b/qobject/qdict.c index ea239f0..50c0ab3 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -597,17 +597,21 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) } } -static bool qdict_has_prefixed_entries(const QDict *src, const char *start) +static int qdict_count_prefixed_entries(const QDict *src, const char *start) { const QDictEntry *entry; + int count = 0; for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { if (strstart(entry->key, start, NULL)) { - return true; + if (count == INT_MAX) { + return -ERANGE; + } + count++; } } - return false; + return count; } /** @@ -646,7 +650,8 @@ void qdict_array_split(QDict *src, QList **dst) snprintf_ret = snprintf(prefix, 32, "%u.", i); assert(snprintf_ret < 32); - is_subqdict = qdict_has_prefixed_entries(src, prefix); + /* Overflow is the same as positive non-zero results */ + is_subqdict = qdict_count_prefixed_entries(src, prefix); // There may be either a single subordinate object (named "%u") or // multiple objects (each with a key prefixed "%u."), but not both. @@ -667,6 +672,71 @@ void qdict_array_split(QDict *src, QList **dst) } /** + * qdict_array_entries(): Returns the number of direct array entries if the + * sub-QDict of src specified by the prefix in subqdict (or src itself for + * prefix == "") is valid as an array, i.e. the length of the created list if + * the sub-QDict would become empty after calling qdict_array_split() on it. If + * the array is not valid, -EINVAL is returned. + */ +int qdict_array_entries(QDict *src, const char *subqdict) +{ + const QDictEntry *entry; + unsigned i; + unsigned entries = 0; + size_t subqdict_len = strlen(subqdict); + + assert(!subqdict_len || subqdict[subqdict_len - 1] == '.'); + + /* qdict_array_split() loops until UINT_MAX, but as we want to return + * negative errors, we only have a signed return value here. Any additional + * entries will lead to -EINVAL. */ + for (i = 0; i < INT_MAX; i++) { + QObject *subqobj; + int subqdict_entries; + size_t slen = 32 + subqdict_len; + char indexstr[slen], prefix[slen]; + size_t snprintf_ret; + + snprintf_ret = snprintf(indexstr, slen, "%s%u", subqdict, i); + assert(snprintf_ret < slen); + + subqobj = qdict_get(src, indexstr); + + snprintf_ret = snprintf(prefix, slen, "%s%u.", subqdict, i); + assert(snprintf_ret < slen); + + subqdict_entries = qdict_count_prefixed_entries(src, prefix); + if (subqdict_entries < 0) { + return subqdict_entries; + } + + /* There may be either a single subordinate object (named "%u") or + * multiple objects (each with a key prefixed "%u."), but not both. */ + if (subqobj && subqdict_entries) { + return -EINVAL; + } else if (!subqobj && !subqdict_entries) { + break; + } + + entries += subqdict_entries ? subqdict_entries : 1; + } + + /* Consider everything handled that isn't part of the given sub-QDict */ + for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { + if (!strstart(qdict_entry_key(entry), subqdict, NULL)) { + entries++; + } + } + + /* Anything left in the sub-QDict that wasn't handled? */ + if (qdict_size(src) != entries) { + return -EINVAL; + } + + return i; +} + +/** * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all * elements from src to dest. *