[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PULL 02/12] keyval: introduce keyval_merge
From: |
Paolo Bonzini |
Subject: |
[PULL 02/12] keyval: introduce keyval_merge |
Date: |
Tue, 8 Jun 2021 11:40:07 +0200 |
This patch introduces a function that merges two keyval-produced
(or keyval-like) QDicts. It can be used to emulate the behavior of
.merge_lists = true QemuOpts groups, merging -readconfig sections and
command-line options in a single QDict, and also to implement -set.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/qemu/option.h | 1 +
tests/unit/test-keyval.c | 56 ++++++++++++++++++++++++++++++++++++++++
util/keyval.c | 47 +++++++++++++++++++++++++++++++++
3 files changed, 104 insertions(+)
diff --git a/include/qemu/option.h b/include/qemu/option.h
index f73e0dc7d9..d89c66145a 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -149,5 +149,6 @@ QemuOptsList *qemu_opts_append(QemuOptsList *dst,
QemuOptsList *list);
QDict *keyval_parse(const char *params, const char *implied_key,
bool *help, Error **errp);
+void keyval_merge(QDict *old, const QDict *new, Error **errp);
#endif
diff --git a/tests/unit/test-keyval.c b/tests/unit/test-keyval.c
index e20c07cf3e..254b51e98c 100644
--- a/tests/unit/test-keyval.c
+++ b/tests/unit/test-keyval.c
@@ -747,6 +747,59 @@ static void test_keyval_visit_any(void)
visit_free(v);
}
+static void test_keyval_merge_success(void)
+{
+ QDict *old = keyval_parse("opt1=abc,opt2.sub1=def,opt2.sub2=ghi,opt3=xyz",
+ NULL, NULL, &error_abort);
+ QDict *new = keyval_parse("opt1=ABC,opt2.sub2=GHI,opt2.sub3=JKL",
+ NULL, NULL, &error_abort);
+ QDict *combined =
keyval_parse("opt1=ABC,opt2.sub1=def,opt2.sub2=GHI,opt2.sub3=JKL,opt3=xyz",
+ NULL, NULL, &error_abort);
+ Error *err = NULL;
+
+ keyval_merge(old, new, &err);
+ g_assert(!err);
+ g_assert(qobject_is_equal(QOBJECT(combined), QOBJECT(old)));
+ qobject_unref(old);
+ qobject_unref(new);
+ qobject_unref(combined);
+}
+
+static void test_keyval_merge_list(void)
+{
+ QDict *old = keyval_parse("opt1.0=abc,opt2.0=xyz",
+ NULL, NULL, &error_abort);
+ QDict *new = keyval_parse("opt1.0=def",
+ NULL, NULL, &error_abort);
+ QDict *combined = keyval_parse("opt1.0=abc,opt1.1=def,opt2.0=xyz",
+ NULL, NULL, &error_abort);
+ Error *err = NULL;
+
+ keyval_merge(old, new, &err);
+ g_assert(!err);
+ g_assert(qobject_is_equal(QOBJECT(combined), QOBJECT(old)));
+ qobject_unref(old);
+ qobject_unref(new);
+ qobject_unref(combined);
+}
+
+static void test_keyval_merge_conflict(void)
+{
+ QDict *old = keyval_parse("opt2.sub1=def,opt2.sub2=ghi",
+ NULL, NULL, &error_abort);
+ QDict *new = keyval_parse("opt2=ABC",
+ NULL, NULL, &error_abort);
+ Error *err = NULL;
+
+ keyval_merge(new, old, &err);
+ error_free_or_abort(&err);
+ keyval_merge(old, new, &err);
+ error_free_or_abort(&err);
+
+ qobject_unref(old);
+ qobject_unref(new);
+}
+
int main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
@@ -760,6 +813,9 @@ int main(int argc, char *argv[])
g_test_add_func("/keyval/visit/optional", test_keyval_visit_optional);
g_test_add_func("/keyval/visit/alternate", test_keyval_visit_alternate);
g_test_add_func("/keyval/visit/any", test_keyval_visit_any);
+ g_test_add_func("/keyval/merge/success", test_keyval_merge_success);
+ g_test_add_func("/keyval/merge/list", test_keyval_merge_list);
+ g_test_add_func("/keyval/merge/conflict", test_keyval_merge_conflict);
g_test_run();
return 0;
}
diff --git a/util/keyval.c b/util/keyval.c
index be34928813..0797f36e1d 100644
--- a/util/keyval.c
+++ b/util/keyval.c
@@ -310,6 +310,53 @@ static char *reassemble_key(GSList *key)
return g_string_free(s, FALSE);
}
+/* Merge two dictionaries. */
+static void keyval_do_merge(QDict *old, const QDict *new, GString *str, Error
**errp)
+{
+ size_t save_len = str->len;
+ const QDictEntry *ent;
+ QObject *old_value;
+
+ for (ent = qdict_first(new); ent; ent = qdict_next(new, ent)) {
+ old_value = qdict_get(old, ent->key);
+ if (old_value) {
+ if (qobject_type(old_value) != qobject_type(ent->value)) {
+ error_setg(errp, "Parameter '%s%s' used inconsistently",
str->str, ent->key);
+ return;
+ } else if (qobject_type(ent->value) == QTYPE_QDICT) {
+ /* Merge sub-dictionaries. */
+ g_string_append(str, ent->key);
+ g_string_append_c(str, '.');
+ keyval_do_merge(qobject_to(QDict, old_value),
+ qobject_to(QDict, ent->value),
+ str, errp);
+ g_string_truncate(str, save_len);
+ continue;
+ } else if (qobject_type(ent->value) == QTYPE_QLIST) {
+ /* Append to old list. */
+ QList *old = qobject_to(QList, old_value);
+ QList *new = qobject_to(QList, ent->value);
+ const QListEntry *item;
+ QLIST_FOREACH_ENTRY(new, item) {
+ qobject_ref(item->value);
+ qlist_append_obj(old, item->value);
+ }
+ continue;
+ }
+ }
+
+ qobject_ref(ent->value);
+ qdict_put_obj(old, ent->key, ent->value);
+ }
+}
+
+void keyval_merge(QDict *old, const QDict *new, Error **errp)
+{
+ GString *str = g_string_new("");
+ keyval_do_merge(old, new, str, errp);
+ g_string_free(str, TRUE);
+}
+
/*
* Listify @cur recursively.
* Replace QDicts whose keys are all valid list indexes by QLists.
--
2.31.1
- [PULL 00/12] Machine and OS X changes for 2021-06-08, Paolo Bonzini, 2021/06/08
- [PULL 04/12] vl: switch -M parsing to keyval, Paolo Bonzini, 2021/06/08
- [PULL 07/12] machine: move common smp_parse code to caller, Paolo Bonzini, 2021/06/08
- [PULL 01/12] qom: export more functions for use with non-UserCreatable objects, Paolo Bonzini, 2021/06/08
- [PULL 06/12] machine: move dies from X86MachineState to CpuTopology, Paolo Bonzini, 2021/06/08
- [PULL 05/12] qemu-option: remove now-dead code, Paolo Bonzini, 2021/06/08
- [PULL 03/12] keyval: introduce keyval_parse_into, Paolo Bonzini, 2021/06/08
- [PULL 02/12] keyval: introduce keyval_merge,
Paolo Bonzini <=
- [PULL 08/12] machine: add error propagation to mc->smp_parse, Paolo Bonzini, 2021/06/08
- [PULL 10/12] machine: reject -smp dies!=1 for non-PC machines, Paolo Bonzini, 2021/06/08
- [PULL 09/12] machine: pass QAPI struct to mc->smp_parse, Paolo Bonzini, 2021/06/08
- [PULL 11/12] machine: add smp compound property, Paolo Bonzini, 2021/06/08
- [PULL 12/12] vnc: avoid deprecation warnings for SASL on OS X, Paolo Bonzini, 2021/06/08
- Re: [PULL 00/12] Machine and OS X changes for 2021-06-08, Daniel P . Berrangé, 2021/06/08
- Re: [PULL 00/12] Machine and OS X changes for 2021-06-08, no-reply, 2021/06/09