]> Git Repo - qemu.git/blob - qapi/qobject-input-visitor.c
Merge remote-tracking branch 'remotes/xtensa/tags/20170306-xtensa' into staging
[qemu.git] / qapi / qobject-input-visitor.c
1 /*
2  * Input Visitor
3  *
4  * Copyright (C) 2012-2016 Red Hat, Inc.
5  * Copyright IBM, Corp. 2011
6  *
7  * Authors:
8  *  Anthony Liguori   <[email protected]>
9  *
10  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
11  * See the COPYING.LIB file in the top-level directory.
12  *
13  */
14
15 #include "qemu/osdep.h"
16 #include "qapi/error.h"
17 #include "qapi/qobject-input-visitor.h"
18 #include "qapi/visitor-impl.h"
19 #include "qemu/queue.h"
20 #include "qemu-common.h"
21 #include "qapi/qmp/types.h"
22 #include "qapi/qmp/qerror.h"
23
24 typedef struct StackObject {
25     const char *name;            /* Name of @obj in its parent, if any */
26     QObject *obj;                /* QDict or QList being visited */
27     void *qapi; /* sanity check that caller uses same pointer */
28
29     GHashTable *h;              /* If @obj is QDict: unvisited keys */
30     const QListEntry *entry;    /* If @obj is QList: unvisited tail */
31     unsigned index;             /* If @obj is QList: list index of @entry */
32
33     QSLIST_ENTRY(StackObject) node; /* parent */
34 } StackObject;
35
36 struct QObjectInputVisitor {
37     Visitor visitor;
38
39     /* Root of visit at visitor creation. */
40     QObject *root;
41
42     /* Stack of objects being visited (all entries will be either
43      * QDict or QList). */
44     QSLIST_HEAD(, StackObject) stack;
45
46     GString *errname;           /* Accumulator for full_name() */
47 };
48
49 static QObjectInputVisitor *to_qiv(Visitor *v)
50 {
51     return container_of(v, QObjectInputVisitor, visitor);
52 }
53
54 static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name,
55                                  int n)
56 {
57     StackObject *so;
58     char buf[32];
59
60     if (qiv->errname) {
61         g_string_truncate(qiv->errname, 0);
62     } else {
63         qiv->errname = g_string_new("");
64     }
65
66     QSLIST_FOREACH(so , &qiv->stack, node) {
67         if (n) {
68             n--;
69         } else if (qobject_type(so->obj) == QTYPE_QDICT) {
70             g_string_prepend(qiv->errname, name ?: "<anonymous>");
71             g_string_prepend_c(qiv->errname, '.');
72         } else {
73             snprintf(buf, sizeof(buf), "[%u]", so->index);
74             g_string_prepend(qiv->errname, buf);
75         }
76         name = so->name;
77     }
78     assert(!n);
79
80     if (name) {
81         g_string_prepend(qiv->errname, name);
82     } else if (qiv->errname->str[0] == '.') {
83         g_string_erase(qiv->errname, 0, 1);
84     } else if (!qiv->errname->str[0]) {
85         return "<anonymous>";
86     }
87
88     return qiv->errname->str;
89 }
90
91 static const char *full_name(QObjectInputVisitor *qiv, const char *name)
92 {
93     return full_name_nth(qiv, name, 0);
94 }
95
96 static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv,
97                                              const char *name,
98                                              bool consume)
99 {
100     StackObject *tos;
101     QObject *qobj;
102     QObject *ret;
103
104     if (QSLIST_EMPTY(&qiv->stack)) {
105         /* Starting at root, name is ignored. */
106         assert(qiv->root);
107         return qiv->root;
108     }
109
110     /* We are in a container; find the next element. */
111     tos = QSLIST_FIRST(&qiv->stack);
112     qobj = tos->obj;
113     assert(qobj);
114
115     if (qobject_type(qobj) == QTYPE_QDICT) {
116         assert(name);
117         ret = qdict_get(qobject_to_qdict(qobj), name);
118         if (tos->h && consume && ret) {
119             bool removed = g_hash_table_remove(tos->h, name);
120             assert(removed);
121         }
122     } else {
123         assert(qobject_type(qobj) == QTYPE_QLIST);
124         assert(!name);
125         if (tos->entry) {
126             ret = qlist_entry_obj(tos->entry);
127             if (consume) {
128                 tos->entry = qlist_next(tos->entry);
129             }
130         } else {
131             ret = NULL;
132         }
133         if (consume) {
134             tos->index++;
135         }
136     }
137
138     return ret;
139 }
140
141 static QObject *qobject_input_get_object(QObjectInputVisitor *qiv,
142                                          const char *name,
143                                          bool consume, Error **errp)
144 {
145     QObject *obj = qobject_input_try_get_object(qiv, name, consume);
146
147     if (!obj) {
148         error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name));
149     }
150     return obj;
151 }
152
153 static void qdict_add_key(const char *key, QObject *obj, void *opaque)
154 {
155     GHashTable *h = opaque;
156     g_hash_table_insert(h, (gpointer) key, NULL);
157 }
158
159 static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv,
160                                             const char *name,
161                                             QObject *obj, void *qapi)
162 {
163     GHashTable *h;
164     StackObject *tos = g_new0(StackObject, 1);
165
166     assert(obj);
167     tos->name = name;
168     tos->obj = obj;
169     tos->qapi = qapi;
170
171     if (qobject_type(obj) == QTYPE_QDICT) {
172         h = g_hash_table_new(g_str_hash, g_str_equal);
173         qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
174         tos->h = h;
175     } else {
176         assert(qobject_type(obj) == QTYPE_QLIST);
177         tos->entry = qlist_first(qobject_to_qlist(obj));
178         tos->index = -1;
179     }
180
181     QSLIST_INSERT_HEAD(&qiv->stack, tos, node);
182     return tos->entry;
183 }
184
185
186 static void qobject_input_check_struct(Visitor *v, Error **errp)
187 {
188     QObjectInputVisitor *qiv = to_qiv(v);
189     StackObject *tos = QSLIST_FIRST(&qiv->stack);
190     GHashTableIter iter;
191     const char *key;
192
193     assert(tos && !tos->entry);
194
195     g_hash_table_iter_init(&iter, tos->h);
196     if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
197         error_setg(errp, "Parameter '%s' is unexpected",
198                    full_name(qiv, key));
199     }
200 }
201
202 static void qobject_input_stack_object_free(StackObject *tos)
203 {
204     if (tos->h) {
205         g_hash_table_unref(tos->h);
206     }
207
208     g_free(tos);
209 }
210
211 static void qobject_input_pop(Visitor *v, void **obj)
212 {
213     QObjectInputVisitor *qiv = to_qiv(v);
214     StackObject *tos = QSLIST_FIRST(&qiv->stack);
215
216     assert(tos && tos->qapi == obj);
217     QSLIST_REMOVE_HEAD(&qiv->stack, node);
218     qobject_input_stack_object_free(tos);
219 }
220
221 static void qobject_input_start_struct(Visitor *v, const char *name, void **obj,
222                                        size_t size, Error **errp)
223 {
224     QObjectInputVisitor *qiv = to_qiv(v);
225     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
226
227     if (obj) {
228         *obj = NULL;
229     }
230     if (!qobj) {
231         return;
232     }
233     if (qobject_type(qobj) != QTYPE_QDICT) {
234         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
235                    full_name(qiv, name), "object");
236         return;
237     }
238
239     qobject_input_push(qiv, name, qobj, obj);
240
241     if (obj) {
242         *obj = g_malloc0(size);
243     }
244 }
245
246
247 static void qobject_input_start_list(Visitor *v, const char *name,
248                                      GenericList **list, size_t size,
249                                      Error **errp)
250 {
251     QObjectInputVisitor *qiv = to_qiv(v);
252     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
253     const QListEntry *entry;
254
255     if (list) {
256         *list = NULL;
257     }
258     if (!qobj) {
259         return;
260     }
261     if (qobject_type(qobj) != QTYPE_QLIST) {
262         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
263                    full_name(qiv, name), "array");
264         return;
265     }
266
267     entry = qobject_input_push(qiv, name, qobj, list);
268     if (entry && list) {
269         *list = g_malloc0(size);
270     }
271 }
272
273 static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail,
274                                             size_t size)
275 {
276     QObjectInputVisitor *qiv = to_qiv(v);
277     StackObject *tos = QSLIST_FIRST(&qiv->stack);
278
279     assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST);
280
281     if (!tos->entry) {
282         return NULL;
283     }
284     tail->next = g_malloc0(size);
285     return tail->next;
286 }
287
288 static void qobject_input_check_list(Visitor *v, Error **errp)
289 {
290     QObjectInputVisitor *qiv = to_qiv(v);
291     StackObject *tos = QSLIST_FIRST(&qiv->stack);
292
293     assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST);
294
295     if (tos->entry) {
296         error_setg(errp, "Only %u list elements expected in %s",
297                    tos->index + 1, full_name_nth(qiv, NULL, 1));
298     }
299 }
300
301
302 static void qobject_input_start_alternate(Visitor *v, const char *name,
303                                           GenericAlternate **obj, size_t size,
304                                           bool promote_int, Error **errp)
305 {
306     QObjectInputVisitor *qiv = to_qiv(v);
307     QObject *qobj = qobject_input_get_object(qiv, name, false, errp);
308
309     if (!qobj) {
310         *obj = NULL;
311         return;
312     }
313     *obj = g_malloc0(size);
314     (*obj)->type = qobject_type(qobj);
315     if (promote_int && (*obj)->type == QTYPE_QINT) {
316         (*obj)->type = QTYPE_QFLOAT;
317     }
318 }
319
320 static void qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj,
321                                      Error **errp)
322 {
323     QObjectInputVisitor *qiv = to_qiv(v);
324     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
325     QInt *qint;
326
327     if (!qobj) {
328         return;
329     }
330     qint = qobject_to_qint(qobj);
331     if (!qint) {
332         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
333                    full_name(qiv, name), "integer");
334         return;
335     }
336
337     *obj = qint_get_int(qint);
338 }
339
340 static void qobject_input_type_uint64(Visitor *v, const char *name,
341                                       uint64_t *obj, Error **errp)
342 {
343     /* FIXME: qobject_to_qint mishandles values over INT64_MAX */
344     QObjectInputVisitor *qiv = to_qiv(v);
345     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
346     QInt *qint;
347
348     if (!qobj) {
349         return;
350     }
351     qint = qobject_to_qint(qobj);
352     if (!qint) {
353         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
354                    full_name(qiv, name), "integer");
355         return;
356     }
357
358     *obj = qint_get_int(qint);
359 }
360
361 static void qobject_input_type_bool(Visitor *v, const char *name, bool *obj,
362                                     Error **errp)
363 {
364     QObjectInputVisitor *qiv = to_qiv(v);
365     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
366     QBool *qbool;
367
368     if (!qobj) {
369         return;
370     }
371     qbool = qobject_to_qbool(qobj);
372     if (!qbool) {
373         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
374                    full_name(qiv, name), "boolean");
375         return;
376     }
377
378     *obj = qbool_get_bool(qbool);
379 }
380
381 static void qobject_input_type_str(Visitor *v, const char *name, char **obj,
382                                    Error **errp)
383 {
384     QObjectInputVisitor *qiv = to_qiv(v);
385     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
386     QString *qstr;
387
388     *obj = NULL;
389     if (!qobj) {
390         return;
391     }
392     qstr = qobject_to_qstring(qobj);
393     if (!qstr) {
394         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
395                    full_name(qiv, name), "string");
396         return;
397     }
398
399     *obj = g_strdup(qstring_get_str(qstr));
400 }
401
402 static void qobject_input_type_number(Visitor *v, const char *name, double *obj,
403                                       Error **errp)
404 {
405     QObjectInputVisitor *qiv = to_qiv(v);
406     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
407     QInt *qint;
408     QFloat *qfloat;
409
410     if (!qobj) {
411         return;
412     }
413     qint = qobject_to_qint(qobj);
414     if (qint) {
415         *obj = qint_get_int(qobject_to_qint(qobj));
416         return;
417     }
418
419     qfloat = qobject_to_qfloat(qobj);
420     if (qfloat) {
421         *obj = qfloat_get_double(qobject_to_qfloat(qobj));
422         return;
423     }
424
425     error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
426                full_name(qiv, name), "number");
427 }
428
429 static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj,
430                                    Error **errp)
431 {
432     QObjectInputVisitor *qiv = to_qiv(v);
433     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
434
435     *obj = NULL;
436     if (!qobj) {
437         return;
438     }
439
440     qobject_incref(qobj);
441     *obj = qobj;
442 }
443
444 static void qobject_input_type_null(Visitor *v, const char *name, Error **errp)
445 {
446     QObjectInputVisitor *qiv = to_qiv(v);
447     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
448
449     if (!qobj) {
450         return;
451     }
452
453     if (qobject_type(qobj) != QTYPE_QNULL) {
454         error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
455                    full_name(qiv, name), "null");
456     }
457 }
458
459 static void qobject_input_optional(Visitor *v, const char *name, bool *present)
460 {
461     QObjectInputVisitor *qiv = to_qiv(v);
462     QObject *qobj = qobject_input_try_get_object(qiv, name, false);
463
464     if (!qobj) {
465         *present = false;
466         return;
467     }
468
469     *present = true;
470 }
471
472 static void qobject_input_free(Visitor *v)
473 {
474     QObjectInputVisitor *qiv = to_qiv(v);
475
476     while (!QSLIST_EMPTY(&qiv->stack)) {
477         StackObject *tos = QSLIST_FIRST(&qiv->stack);
478
479         QSLIST_REMOVE_HEAD(&qiv->stack, node);
480         qobject_input_stack_object_free(tos);
481     }
482
483     qobject_decref(qiv->root);
484     if (qiv->errname) {
485         g_string_free(qiv->errname, TRUE);
486     }
487     g_free(qiv);
488 }
489
490 Visitor *qobject_input_visitor_new(QObject *obj)
491 {
492     QObjectInputVisitor *v;
493
494     assert(obj);
495     v = g_malloc0(sizeof(*v));
496
497     v->visitor.type = VISITOR_INPUT;
498     v->visitor.start_struct = qobject_input_start_struct;
499     v->visitor.check_struct = qobject_input_check_struct;
500     v->visitor.end_struct = qobject_input_pop;
501     v->visitor.start_list = qobject_input_start_list;
502     v->visitor.next_list = qobject_input_next_list;
503     v->visitor.check_list = qobject_input_check_list;
504     v->visitor.end_list = qobject_input_pop;
505     v->visitor.start_alternate = qobject_input_start_alternate;
506     v->visitor.type_int64 = qobject_input_type_int64;
507     v->visitor.type_uint64 = qobject_input_type_uint64;
508     v->visitor.type_bool = qobject_input_type_bool;
509     v->visitor.type_str = qobject_input_type_str;
510     v->visitor.type_number = qobject_input_type_number;
511     v->visitor.type_any = qobject_input_type_any;
512     v->visitor.type_null = qobject_input_type_null;
513     v->visitor.optional = qobject_input_optional;
514     v->visitor.free = qobject_input_free;
515
516     v->root = obj;
517     qobject_incref(obj);
518
519     return &v->visitor;
520 }
This page took 0.059674 seconds and 4 git commands to generate.