aboutsummaryrefslogtreecommitdiff
path: root/qapi
diff options
context:
space:
mode:
Diffstat (limited to 'qapi')
-rw-r--r--qapi/Makefile.objs2
-rw-r--r--qapi/qapi-clone-visitor.c182
-rw-r--r--qapi/qapi-visit-core.c28
3 files changed, 202 insertions, 10 deletions
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 2278970690..7ea4aebb00 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -1,6 +1,6 @@
util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
util-obj-y += string-input-visitor.o string-output-visitor.o
-util-obj-y += opts-visitor.o
+util-obj-y += opts-visitor.o qapi-clone-visitor.o
util-obj-y += qmp-event.o
util-obj-y += qapi-util.o
diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c
new file mode 100644
index 0000000000..0bb8216372
--- /dev/null
+++ b/qapi/qapi-clone-visitor.c
@@ -0,0 +1,182 @@
+/*
+ * Copy one QAPI object to another
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/clone-visitor.h"
+#include "qapi/visitor-impl.h"
+#include "qapi/error.h"
+
+struct QapiCloneVisitor {
+ Visitor visitor;
+ size_t depth;
+};
+
+static QapiCloneVisitor *to_qcv(Visitor *v)
+{
+ return container_of(v, QapiCloneVisitor, visitor);
+}
+
+static void qapi_clone_start_struct(Visitor *v, const char *name, void **obj,
+ size_t size, Error **errp)
+{
+ QapiCloneVisitor *qcv = to_qcv(v);
+
+ if (!obj) {
+ assert(qcv->depth);
+ /* Only possible when visiting an alternate's object
+ * branch. Nothing further to do here, since the earlier
+ * visit_start_alternate() already copied memory. */
+ return;
+ }
+
+ *obj = g_memdup(*obj, size);
+ qcv->depth++;
+}
+
+static void qapi_clone_end(Visitor *v, void **obj)
+{
+ QapiCloneVisitor *qcv = to_qcv(v);
+
+ assert(qcv->depth);
+ if (obj) {
+ qcv->depth--;
+ }
+}
+
+static void qapi_clone_start_list(Visitor *v, const char *name,
+ GenericList **listp, size_t size,
+ Error **errp)
+{
+ qapi_clone_start_struct(v, name, (void **)listp, size, errp);
+}
+
+static GenericList *qapi_clone_next_list(Visitor *v, GenericList *tail,
+ size_t size)
+{
+ QapiCloneVisitor *qcv = to_qcv(v);
+
+ assert(qcv->depth);
+ /* Unshare the tail of the list cloned by g_memdup() */
+ tail->next = g_memdup(tail->next, size);
+ return tail->next;
+}
+
+static void qapi_clone_start_alternate(Visitor *v, const char *name,
+ GenericAlternate **obj, size_t size,
+ bool promote_int, Error **errp)
+{
+ qapi_clone_start_struct(v, name, (void **)obj, size, errp);
+}
+
+static void qapi_clone_type_int64(Visitor *v, const char *name, int64_t *obj,
+ Error **errp)
+{
+ QapiCloneVisitor *qcv = to_qcv(v);
+
+ assert(qcv->depth);
+ /* Value was already cloned by g_memdup() */
+}
+
+static void qapi_clone_type_uint64(Visitor *v, const char *name,
+ uint64_t *obj, Error **errp)
+{
+ QapiCloneVisitor *qcv = to_qcv(v);
+
+ assert(qcv->depth);
+ /* Value was already cloned by g_memdup() */
+}
+
+static void qapi_clone_type_bool(Visitor *v, const char *name, bool *obj,
+ Error **errp)
+{
+ QapiCloneVisitor *qcv = to_qcv(v);
+
+ assert(qcv->depth);
+ /* Value was already cloned by g_memdup() */
+}
+
+static void qapi_clone_type_str(Visitor *v, const char *name, char **obj,
+ Error **errp)
+{
+ QapiCloneVisitor *qcv = to_qcv(v);
+
+ assert(qcv->depth);
+ /*
+ * Pointer was already cloned by g_memdup; create fresh copy.
+ * Note that as long as qmp-output-visitor accepts NULL instead of
+ * "", then we must do likewise. However, we want to obey the
+ * input visitor semantics of never producing NULL when the empty
+ * string is intended.
+ */
+ *obj = g_strdup(*obj ?: "");
+}
+
+static void qapi_clone_type_number(Visitor *v, const char *name, double *obj,
+ Error **errp)
+{
+ QapiCloneVisitor *qcv = to_qcv(v);
+
+ assert(qcv->depth);
+ /* Value was already cloned by g_memdup() */
+}
+
+static void qapi_clone_type_null(Visitor *v, const char *name, Error **errp)
+{
+ QapiCloneVisitor *qcv = to_qcv(v);
+
+ assert(qcv->depth);
+ /* Nothing to do */
+}
+
+static void qapi_clone_free(Visitor *v)
+{
+ g_free(v);
+}
+
+static Visitor *qapi_clone_visitor_new(void)
+{
+ QapiCloneVisitor *v;
+
+ v = g_malloc0(sizeof(*v));
+
+ v->visitor.type = VISITOR_CLONE;
+ v->visitor.start_struct = qapi_clone_start_struct;
+ v->visitor.end_struct = qapi_clone_end;
+ v->visitor.start_list = qapi_clone_start_list;
+ v->visitor.next_list = qapi_clone_next_list;
+ v->visitor.end_list = qapi_clone_end;
+ v->visitor.start_alternate = qapi_clone_start_alternate;
+ v->visitor.end_alternate = qapi_clone_end;
+ v->visitor.type_int64 = qapi_clone_type_int64;
+ v->visitor.type_uint64 = qapi_clone_type_uint64;
+ v->visitor.type_bool = qapi_clone_type_bool;
+ v->visitor.type_str = qapi_clone_type_str;
+ v->visitor.type_number = qapi_clone_type_number;
+ v->visitor.type_null = qapi_clone_type_null;
+ v->visitor.free = qapi_clone_free;
+
+ return &v->visitor;
+}
+
+void *qapi_clone(const void *src, void (*visit_type)(Visitor *, const char *,
+ void **, Error **))
+{
+ Visitor *v;
+ void *dst = (void *) src; /* Cast away const */
+
+ if (!src) {
+ return NULL;
+ }
+
+ v = qapi_clone_visitor_new();
+ visit_type(v, NULL, &dst, &error_abort);
+ visit_free(v);
+ return dst;
+}
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index eb7dd7253c..55f5876dc0 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -42,10 +42,10 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
if (obj) {
assert(size);
- assert(v->type != VISITOR_OUTPUT || *obj);
+ assert(!(v->type & VISITOR_OUTPUT) || *obj);
}
v->start_struct(v, name, obj, size, &err);
- if (obj && v->type == VISITOR_INPUT) {
+ if (obj && (v->type & VISITOR_INPUT)) {
assert(!err != !*obj);
}
error_propagate(errp, err);
@@ -70,7 +70,7 @@ void visit_start_list(Visitor *v, const char *name, GenericList **list,
assert(!list || size >= sizeof(GenericList));
v->start_list(v, name, list, size, &err);
- if (list && v->type == VISITOR_INPUT) {
+ if (list && (v->type & VISITOR_INPUT)) {
assert(!(err && *list));
}
error_propagate(errp, err);
@@ -94,11 +94,11 @@ void visit_start_alternate(Visitor *v, const char *name,
Error *err = NULL;
assert(obj && size >= sizeof(GenericAlternate));
- assert(v->type != VISITOR_OUTPUT || *obj);
+ assert(!(v->type & VISITOR_OUTPUT) || *obj);
if (v->start_alternate) {
v->start_alternate(v, name, obj, size, promote_int, &err);
}
- if (v->type == VISITOR_INPUT) {
+ if (v->type & VISITOR_INPUT) {
assert(v->start_alternate && !err != !*obj);
}
error_propagate(errp, err);
@@ -250,10 +250,10 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
assert(obj);
/* TODO: Fix callers to not pass NULL when they mean "", so that we
* can enable:
- assert(v->type != VISITOR_OUTPUT || *obj);
+ assert(!(v->type & VISITOR_OUTPUT) || *obj);
*/
v->type_str(v, name, obj, &err);
- if (v->type == VISITOR_INPUT) {
+ if (v->type & VISITOR_INPUT) {
assert(!err != !*obj);
}
error_propagate(errp, err);
@@ -335,9 +335,19 @@ void visit_type_enum(Visitor *v, const char *name, int *obj,
const char *const strings[], Error **errp)
{
assert(obj && strings);
- if (v->type == VISITOR_INPUT) {
+ switch (v->type) {
+ case VISITOR_INPUT:
input_type_enum(v, name, obj, strings, errp);
- } else if (v->type == VISITOR_OUTPUT) {
+ break;
+ case VISITOR_OUTPUT:
output_type_enum(v, name, obj, strings, errp);
+ break;
+ case VISITOR_CLONE:
+ /* nothing further to do, scalar value was already copied by
+ * g_memdup() during visit_start_*() */
+ break;
+ case VISITOR_DEALLOC:
+ /* nothing to deallocate for a scalar */
+ break;
}
}