Commit bcebf102 authored by Kevin Wolf's avatar Kevin Wolf
Browse files

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: default avatarKevin Wolf <kwolf@redhat.com>
Reviewed-by: default avatarMax Reitz <mreitz@redhat.com>
Reviewed-by: default avatarEric Blake <eblake@redhat.com>
parent 37974a97
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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 */
+34 −0
Original line number Diff line number Diff line
@@ -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;
}
+129 −0
Original line number Diff line number Diff line
@@ -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);