qdict: Add qdict_array_entries()

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 <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
Kevin Wolf 2015-01-21 17:15:44 +01:00
parent a68197ff5b
commit bd50530a9f
2 changed files with 75 additions and 4 deletions

View File

@ -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);

View File

@ -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.
@ -666,6 +671,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.