qdict: Introduce qdict_rename_keys()

A few block drivers will need to rename .bdrv_create options for their
QAPIfication, so let's have a helper function for that.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Kevin Wolf 2018-02-01 20:00:44 +01:00
parent 37974a97d4
commit bcebf102cc
3 changed files with 169 additions and 0 deletions

View File

@ -81,4 +81,10 @@ QObject *qdict_crumple(const QDict *src, Error **errp);
void qdict_join(QDict *dest, QDict *src, bool overwrite);
typedef struct QDictRenames {
const char *from;
const char *to;
} QDictRenames;
bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp);
#endif /* QDICT_H */

View File

@ -1072,3 +1072,37 @@ void qdict_join(QDict *dest, QDict *src, bool overwrite)
entry = next;
}
}
/**
* qdict_rename_keys(): Rename keys in qdict according to the replacements
* specified in the array renames. The array must be terminated by an entry
* with from = NULL.
*
* The renames are performed individually in the order of the array, so entries
* may be renamed multiple times and may or may not conflict depending on the
* order of the renames array.
*
* Returns true for success, false in error cases.
*/
bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp)
{
QObject *qobj;
while (renames->from) {
if (qdict_haskey(qdict, renames->from)) {
if (qdict_haskey(qdict, renames->to)) {
error_setg(errp, "'%s' and its alias '%s' can't be used at the "
"same time", renames->to, renames->from);
return false;
}
qobj = qdict_get(qdict, renames->from);
qobject_incref(qobj);
qdict_put_obj(qdict, renames->to, qobj);
qdict_del(qdict, renames->from);
}
renames++;
}
return true;
}

View File

@ -665,6 +665,133 @@ static void qdict_crumple_test_empty(void)
QDECREF(dst);
}
static int qdict_count_entries(QDict *dict)
{
const QDictEntry *e;
int count = 0;
for (e = qdict_first(dict); e; e = qdict_next(dict, e)) {
count++;
}
return count;
}
static void qdict_rename_keys_test(void)
{
QDict *dict = qdict_new();
QDict *copy;
QDictRenames *renames;
Error *local_err = NULL;
qdict_put_str(dict, "abc", "foo");
qdict_put_str(dict, "abcdef", "bar");
qdict_put_int(dict, "number", 42);
qdict_put_bool(dict, "flag", true);
qdict_put_null(dict, "nothing");
/* Empty rename list */
renames = (QDictRenames[]) {
{ NULL, "this can be anything" }
};
copy = qdict_clone_shallow(dict);
qdict_rename_keys(copy, renames, &error_abort);
g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo");
g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar");
g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42);
g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true);
g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL);
g_assert_cmpint(qdict_count_entries(copy), ==, 5);
QDECREF(copy);
/* Simple rename of all entries */
renames = (QDictRenames[]) {
{ "abc", "str1" },
{ "abcdef", "str2" },
{ "number", "int" },
{ "flag", "bool" },
{ "nothing", "null" },
{ NULL , NULL }
};
copy = qdict_clone_shallow(dict);
qdict_rename_keys(copy, renames, &error_abort);
g_assert(!qdict_haskey(copy, "abc"));
g_assert(!qdict_haskey(copy, "abcdef"));
g_assert(!qdict_haskey(copy, "number"));
g_assert(!qdict_haskey(copy, "flag"));
g_assert(!qdict_haskey(copy, "nothing"));
g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo");
g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar");
g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42);
g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true);
g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL);
g_assert_cmpint(qdict_count_entries(copy), ==, 5);
QDECREF(copy);
/* Renames are processed top to bottom */
renames = (QDictRenames[]) {
{ "abc", "tmp" },
{ "abcdef", "abc" },
{ "number", "abcdef" },
{ "flag", "number" },
{ "nothing", "flag" },
{ "tmp", "nothing" },
{ NULL , NULL }
};
copy = qdict_clone_shallow(dict);
qdict_rename_keys(copy, renames, &error_abort);
g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo");
g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar");
g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42);
g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true);
g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL);
g_assert(!qdict_haskey(copy, "tmp"));
g_assert_cmpint(qdict_count_entries(copy), ==, 5);
QDECREF(copy);
/* Conflicting rename */
renames = (QDictRenames[]) {
{ "abcdef", "abc" },
{ NULL , NULL }
};
copy = qdict_clone_shallow(dict);
qdict_rename_keys(copy, renames, &local_err);
g_assert(local_err != NULL);
error_free(local_err);
local_err = NULL;
g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo");
g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar");
g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42);
g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true);
g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL);
g_assert_cmpint(qdict_count_entries(copy), ==, 5);
QDECREF(copy);
/* Renames in an empty dict */
renames = (QDictRenames[]) {
{ "abcdef", "abc" },
{ NULL , NULL }
};
QDECREF(dict);
dict = qdict_new();
qdict_rename_keys(dict, renames, &error_abort);
g_assert(qdict_first(dict) == NULL);
QDECREF(dict);
}
static void qdict_crumple_test_bad_inputs(void)
{
QDict *src;
@ -880,6 +1007,8 @@ int main(int argc, char **argv)
g_test_add_func("/public/crumple/bad_inputs",
qdict_crumple_test_bad_inputs);
g_test_add_func("/public/rename_keys", qdict_rename_keys_test);
/* The Big one */
if (g_test_slow()) {
g_test_add_func("/stress/test", qdict_stress_test);