Re: [Qemu-devel] [PATCH v2][ 10/21] qapi: add QMP input visiter
On Fri, 3 Jun 2011 17:33:08 -0500 Michael Roth wrote: > +static void qmp_input_type_int(Visiter *v, int64_t *obj, const char *name, > Error **errp) > +{ > +QmpInputVisiter *qiv = to_qiv(v); > +QObject *qobj = qmp_input_get_object(qiv, name); > + > +if (!qobj || qobject_type(qobj) != QTYPE_QINT) { > +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "integer"); > +return; > +} > + > +*obj = qint_get_int(qobject_to_qint(qobj)); Let me warn you for an error I just got: if 'name' is NULL and the if test is true, then error_set() will segfault, because 'name' must not be NULL. So either, we always pass 'name' in the generated code or we automatically build a new string if 'name' is NULL.
Re: [Qemu-devel] [PATCH v2][ 10/21] qapi: add QMP input visiter
On 06/09/2011 11:41 AM, Michael Roth wrote: On 06/09/2011 11:26 AM, Peter Maydell wrote: On 3 June 2011 23:33, Michael Roth wrote: A type of Visiter class [randomly noted against this patch because this is where I happened to notice it...] Should be "Visitor" throughout (and in other patches), please? That was my Jersey-accent kicking in I think ;-) Regards, Anthony Liguori -- PMM Stefan pointed that out too...I was hoping to bring "visiter" back in style, but we can change it :)
Re: [Qemu-devel] [PATCH v2][ 10/21] qapi: add QMP input visiter
On 06/09/2011 10:55 AM, Luiz Capitulino wrote: On Thu, 09 Jun 2011 10:41:53 -0500 Michael Roth wrote: On 06/09/2011 10:30 AM, Luiz Capitulino wrote: On Fri, 3 Jun 2011 17:33:08 -0500 Michael Roth wrote: A type of Visiter class that is used to walk a qobject's structure and assign each entry to the corresponding native C type. Command marshaling function will use this to pull out QMP command parameters recieved over the wire and pass them as native arguments to the corresponding C functions. Signed-off-by: Michael Roth --- qapi/qmp-input-visiter.c | 239 ++ qapi/qmp-input-visiter.h | 26 + 2 files changed, 265 insertions(+), 0 deletions(-) create mode 100644 qapi/qmp-input-visiter.c create mode 100644 qapi/qmp-input-visiter.h diff --git a/qapi/qmp-input-visiter.c b/qapi/qmp-input-visiter.c new file mode 100644 index 000..6767e39 --- /dev/null +++ b/qapi/qmp-input-visiter.c @@ -0,0 +1,239 @@ +#include "qmp-input-visiter.h" +#include "qemu-queue.h" +#include "qemu-common.h" +#include "qemu-objects.h" +#include "qerror.h" + +#define QAPI_OBJECT_SIZE 512 + +#define QIV_STACK_SIZE 1024 + +typedef struct StackObject +{ +QObject *obj; +QListEntry *entry; +} StackObject; + +struct QmpInputVisiter +{ +Visiter visiter; +QObject *obj; +StackObject stack[QIV_STACK_SIZE]; +int nb_stack; +}; + +static QmpInputVisiter *to_qiv(Visiter *v) +{ +return container_of(v, QmpInputVisiter, visiter); +} + +static QObject *qmp_input_get_object(QmpInputVisiter *qiv, const char *name) +{ +QObject *qobj; + +if (qiv->nb_stack == 0) { +qobj = qiv->obj; +} else { +qobj = qiv->stack[qiv->nb_stack - 1].obj; +} + +if (name&& qobject_type(qobj) == QTYPE_QDICT) { +return qdict_get(qobject_to_qdict(qobj), name); +} else if (qiv->nb_stack> 0&& qobject_type(qobj) == QTYPE_QLIST) { +return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); +} + +return qobj; +} + +static void qmp_input_push(QmpInputVisiter *qiv, QObject *obj) +{ +qiv->stack[qiv->nb_stack].obj = obj; +if (qobject_type(obj) == QTYPE_QLIST) { +qiv->stack[qiv->nb_stack].entry = qlist_first(qobject_to_qlist(obj)); +} +qiv->nb_stack++; + +assert(qiv->nb_stack< QIV_STACK_SIZE); // FIXME Can't this limit be reached if a client sends a nested object? Why don't we make it dynamic and/or return an error if a limit is reached? Yup, I think that's what the fixme was for. It's been fixed in my tree, just sets an Error and returns instead now. In reality the token limit added to the json parser with the set1 patches would catch overrun attempts from the client though, so it's just an extra layer of protection. Isn't this limit only in effect for individual tokens? Or does it also catches the number of values in a dict? You're right, looking again we have a limit on individual token size, and a limit on nested levels to avoid arbitrary levels of recursion in the parser. But the the number of entries that can be put into a dict/list are unbounded, so this check might actually needed. We'll probably want to harden the parser/streamer stuff for case as well, to the point where we can put an upper bound on how large a QMP client-produced qobject can be. I'll look at a separate patch against master for this. +} + +static void qmp_input_pop(QmpInputVisiter *qiv) +{ +qiv->nb_stack--; +assert(qiv->nb_stack>= 0); // FIXME +} + +static void qmp_input_start_struct(Visiter *v, void **obj, const char *kind, const char *name, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); +QObject *qobj = qmp_input_get_object(qiv, name); + +if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "object"); +return; +} + +qmp_input_push(qiv, qobj); + +if (obj) { +*obj = qemu_mallocz(QAPI_OBJECT_SIZE); I'm not sure I understand how this is being handled. This is allocating the struct size, right? What happens if struct size> QAPI_OBJECT_SIZE? Badness :) We'll need to pick a reasonable value and note it in the schema documentation. Isn't it possible to pass the struct size to visit_start_struct()? The Indeed! The generated code knows the struct type, so it could pass in the size as a parameter. I'll take a look at this. object itself is passed... Another (complex) solution would be to walk through the dictionary's elements and calculate the struct size. The walking approach might have issues with optional fields and whatnot. +} +} + +static void qmp_input_end_struct(Visiter *v, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); + +qmp_input_pop(qiv); +} + +static void qmp_input_start_list(Visiter *v, const char *name, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); +QObject *qobj = qmp_input_get_object(qiv, name); + +if (!qobj || qobject_typ
Re: [Qemu-devel] [PATCH v2][ 10/21] qapi: add QMP input visiter
On 06/09/2011 11:26 AM, Peter Maydell wrote: On 3 June 2011 23:33, Michael Roth wrote: A type of Visiter class [randomly noted against this patch because this is where I happened to notice it...] Should be "Visitor" throughout (and in other patches), please? -- PMM Stefan pointed that out too...I was hoping to bring "visiter" back in style, but we can change it :)
Re: [Qemu-devel] [PATCH v2][ 10/21] qapi: add QMP input visiter
On 3 June 2011 23:33, Michael Roth wrote: > A type of Visiter class [randomly noted against this patch because this is where I happened to notice it...] Should be "Visitor" throughout (and in other patches), please? -- PMM
Re: [Qemu-devel] [PATCH v2][ 10/21] qapi: add QMP input visiter
On Thu, 09 Jun 2011 10:41:53 -0500 Michael Roth wrote: > On 06/09/2011 10:30 AM, Luiz Capitulino wrote: > > On Fri, 3 Jun 2011 17:33:08 -0500 > > Michael Roth wrote: > > > >> A type of Visiter class that is used to walk a qobject's > >> structure and assign each entry to the corresponding native C type. > >> Command marshaling function will use this to pull out QMP command > >> parameters recieved over the wire and pass them as native arguments > >> to the corresponding C functions. > >> > >> Signed-off-by: Michael Roth > >> --- > >> qapi/qmp-input-visiter.c | 239 > >> ++ > >> qapi/qmp-input-visiter.h | 26 + > >> 2 files changed, 265 insertions(+), 0 deletions(-) > >> create mode 100644 qapi/qmp-input-visiter.c > >> create mode 100644 qapi/qmp-input-visiter.h > >> > >> diff --git a/qapi/qmp-input-visiter.c b/qapi/qmp-input-visiter.c > >> new file mode 100644 > >> index 000..6767e39 > >> --- /dev/null > >> +++ b/qapi/qmp-input-visiter.c > >> @@ -0,0 +1,239 @@ > >> +#include "qmp-input-visiter.h" > >> +#include "qemu-queue.h" > >> +#include "qemu-common.h" > >> +#include "qemu-objects.h" > >> +#include "qerror.h" > >> + > >> +#define QAPI_OBJECT_SIZE 512 > >> + > >> +#define QIV_STACK_SIZE 1024 > >> + > >> +typedef struct StackObject > >> +{ > >> +QObject *obj; > >> +QListEntry *entry; > >> +} StackObject; > >> + > >> +struct QmpInputVisiter > >> +{ > >> +Visiter visiter; > >> +QObject *obj; > >> +StackObject stack[QIV_STACK_SIZE]; > >> +int nb_stack; > >> +}; > >> + > >> +static QmpInputVisiter *to_qiv(Visiter *v) > >> +{ > >> +return container_of(v, QmpInputVisiter, visiter); > >> +} > >> + > >> +static QObject *qmp_input_get_object(QmpInputVisiter *qiv, const char > >> *name) > >> +{ > >> +QObject *qobj; > >> + > >> +if (qiv->nb_stack == 0) { > >> +qobj = qiv->obj; > >> +} else { > >> +qobj = qiv->stack[qiv->nb_stack - 1].obj; > >> +} > >> + > >> +if (name&& qobject_type(qobj) == QTYPE_QDICT) { > >> +return qdict_get(qobject_to_qdict(qobj), name); > >> +} else if (qiv->nb_stack> 0&& qobject_type(qobj) == QTYPE_QLIST) { > >> +return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); > >> +} > >> + > >> +return qobj; > >> +} > >> + > >> +static void qmp_input_push(QmpInputVisiter *qiv, QObject *obj) > >> +{ > >> +qiv->stack[qiv->nb_stack].obj = obj; > >> +if (qobject_type(obj) == QTYPE_QLIST) { > >> +qiv->stack[qiv->nb_stack].entry = > >> qlist_first(qobject_to_qlist(obj)); > >> +} > >> +qiv->nb_stack++; > >> + > >> +assert(qiv->nb_stack< QIV_STACK_SIZE); // FIXME > > > > Can't this limit be reached if a client sends a nested object? Why > > don't we make it dynamic and/or return an error if a limit is reached? > > > > Yup, I think that's what the fixme was for. It's been fixed in my tree, > just sets an Error and returns instead now. > > In reality the token limit added to the json parser with the set1 > patches would catch overrun attempts from the client though, so it's > just an extra layer of protection. Isn't this limit only in effect for individual tokens? Or does it also catches the number of values in a dict? > > >> +} > >> + > >> +static void qmp_input_pop(QmpInputVisiter *qiv) > >> +{ > >> +qiv->nb_stack--; > >> +assert(qiv->nb_stack>= 0); // FIXME > >> +} > >> + > >> +static void qmp_input_start_struct(Visiter *v, void **obj, const char > >> *kind, const char *name, Error **errp) > >> +{ > >> +QmpInputVisiter *qiv = to_qiv(v); > >> +QObject *qobj = qmp_input_get_object(qiv, name); > >> + > >> +if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { > >> +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "object"); > >> +return; > >> +} > >> + > >> +qmp_input_push(qiv, qobj); > >> + > >> +if (obj) { > >> +*obj = qemu_mallocz(QAPI_OBJECT_SIZE); > > > > I'm not sure I understand how this is being handled. This is allocating > > the struct size, right? What happens if struct size> QAPI_OBJECT_SIZE? > > > > Badness :) We'll need to pick a reasonable value and note it in the > schema documentation. Isn't it possible to pass the struct size to visit_start_struct()? The object itself is passed... Another (complex) solution would be to walk through the dictionary's elements and calculate the struct size. > > >> +} > >> +} > >> + > >> +static void qmp_input_end_struct(Visiter *v, Error **errp) > >> +{ > >> +QmpInputVisiter *qiv = to_qiv(v); > >> + > >> +qmp_input_pop(qiv); > >> +} > >> + > >> +static void qmp_input_start_list(Visiter *v, const char *name, Error > >> **errp) > >> +{ > >> +QmpInputVisiter *qiv = to_qiv(v); > >> +QObject *qobj = qmp_input_get_object(qiv, name); > >> + > >> +if (!qobj || qobject_type(qobj) != QTYPE_QLIST) { > >> +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "list"); > >> +
Re: [Qemu-devel] [PATCH v2][ 10/21] qapi: add QMP input visiter
On 06/09/2011 10:30 AM, Luiz Capitulino wrote: On Fri, 3 Jun 2011 17:33:08 -0500 Michael Roth wrote: A type of Visiter class that is used to walk a qobject's structure and assign each entry to the corresponding native C type. Command marshaling function will use this to pull out QMP command parameters recieved over the wire and pass them as native arguments to the corresponding C functions. Signed-off-by: Michael Roth --- qapi/qmp-input-visiter.c | 239 ++ qapi/qmp-input-visiter.h | 26 + 2 files changed, 265 insertions(+), 0 deletions(-) create mode 100644 qapi/qmp-input-visiter.c create mode 100644 qapi/qmp-input-visiter.h diff --git a/qapi/qmp-input-visiter.c b/qapi/qmp-input-visiter.c new file mode 100644 index 000..6767e39 --- /dev/null +++ b/qapi/qmp-input-visiter.c @@ -0,0 +1,239 @@ +#include "qmp-input-visiter.h" +#include "qemu-queue.h" +#include "qemu-common.h" +#include "qemu-objects.h" +#include "qerror.h" + +#define QAPI_OBJECT_SIZE 512 + +#define QIV_STACK_SIZE 1024 + +typedef struct StackObject +{ +QObject *obj; +QListEntry *entry; +} StackObject; + +struct QmpInputVisiter +{ +Visiter visiter; +QObject *obj; +StackObject stack[QIV_STACK_SIZE]; +int nb_stack; +}; + +static QmpInputVisiter *to_qiv(Visiter *v) +{ +return container_of(v, QmpInputVisiter, visiter); +} + +static QObject *qmp_input_get_object(QmpInputVisiter *qiv, const char *name) +{ +QObject *qobj; + +if (qiv->nb_stack == 0) { +qobj = qiv->obj; +} else { +qobj = qiv->stack[qiv->nb_stack - 1].obj; +} + +if (name&& qobject_type(qobj) == QTYPE_QDICT) { +return qdict_get(qobject_to_qdict(qobj), name); +} else if (qiv->nb_stack> 0&& qobject_type(qobj) == QTYPE_QLIST) { +return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); +} + +return qobj; +} + +static void qmp_input_push(QmpInputVisiter *qiv, QObject *obj) +{ +qiv->stack[qiv->nb_stack].obj = obj; +if (qobject_type(obj) == QTYPE_QLIST) { +qiv->stack[qiv->nb_stack].entry = qlist_first(qobject_to_qlist(obj)); +} +qiv->nb_stack++; + +assert(qiv->nb_stack< QIV_STACK_SIZE); // FIXME Can't this limit be reached if a client sends a nested object? Why don't we make it dynamic and/or return an error if a limit is reached? Yup, I think that's what the fixme was for. It's been fixed in my tree, just sets an Error and returns instead now. In reality the token limit added to the json parser with the set1 patches would catch overrun attempts from the client though, so it's just an extra layer of protection. +} + +static void qmp_input_pop(QmpInputVisiter *qiv) +{ +qiv->nb_stack--; +assert(qiv->nb_stack>= 0); // FIXME +} + +static void qmp_input_start_struct(Visiter *v, void **obj, const char *kind, const char *name, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); +QObject *qobj = qmp_input_get_object(qiv, name); + +if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "object"); +return; +} + +qmp_input_push(qiv, qobj); + +if (obj) { +*obj = qemu_mallocz(QAPI_OBJECT_SIZE); I'm not sure I understand how this is being handled. This is allocating the struct size, right? What happens if struct size> QAPI_OBJECT_SIZE? Badness :) We'll need to pick a reasonable value and note it in the schema documentation. +} +} + +static void qmp_input_end_struct(Visiter *v, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); + +qmp_input_pop(qiv); +} + +static void qmp_input_start_list(Visiter *v, const char *name, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); +QObject *qobj = qmp_input_get_object(qiv, name); + +if (!qobj || qobject_type(qobj) != QTYPE_QLIST) { +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "list"); +return; +} + +qmp_input_push(qiv, qobj); +} + +static GenericList *qmp_input_next_list(Visiter *v, GenericList **list, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); +GenericList *entry; +StackObject *so =&qiv->stack[qiv->nb_stack - 1]; + +if (so->entry == NULL) { +return NULL; +} + +entry = qemu_mallocz(sizeof(*entry)); +if (*list) { +so->entry = qlist_next(so->entry); +if (so->entry == NULL) { +qemu_free(entry); +return NULL; +} +(*list)->next = entry; +} +*list = entry; + + +return entry; +} + +static void qmp_input_end_list(Visiter *v, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); + +qmp_input_pop(qiv); +} + +static void qmp_input_type_int(Visiter *v, int64_t *obj, const char *name, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); +QObject *qobj = qmp_input_get_object(qiv, name); + +if (!qobj || qobject_type(qobj) != QTYPE_QINT) { +error_set(errp, QERR_INVALID_PARAMETE
Re: [Qemu-devel] [PATCH v2][ 10/21] qapi: add QMP input visiter
On Fri, 3 Jun 2011 17:33:08 -0500 Michael Roth wrote: > A type of Visiter class that is used to walk a qobject's > structure and assign each entry to the corresponding native C type. > Command marshaling function will use this to pull out QMP command > parameters recieved over the wire and pass them as native arguments > to the corresponding C functions. > > Signed-off-by: Michael Roth > --- > qapi/qmp-input-visiter.c | 239 > ++ > qapi/qmp-input-visiter.h | 26 + > 2 files changed, 265 insertions(+), 0 deletions(-) > create mode 100644 qapi/qmp-input-visiter.c > create mode 100644 qapi/qmp-input-visiter.h > > diff --git a/qapi/qmp-input-visiter.c b/qapi/qmp-input-visiter.c > new file mode 100644 > index 000..6767e39 > --- /dev/null > +++ b/qapi/qmp-input-visiter.c > @@ -0,0 +1,239 @@ > +#include "qmp-input-visiter.h" > +#include "qemu-queue.h" > +#include "qemu-common.h" > +#include "qemu-objects.h" > +#include "qerror.h" > + > +#define QAPI_OBJECT_SIZE 512 > + > +#define QIV_STACK_SIZE 1024 > + > +typedef struct StackObject > +{ > +QObject *obj; > +QListEntry *entry; > +} StackObject; > + > +struct QmpInputVisiter > +{ > +Visiter visiter; > +QObject *obj; > +StackObject stack[QIV_STACK_SIZE]; > +int nb_stack; > +}; > + > +static QmpInputVisiter *to_qiv(Visiter *v) > +{ > +return container_of(v, QmpInputVisiter, visiter); > +} > + > +static QObject *qmp_input_get_object(QmpInputVisiter *qiv, const char *name) > +{ > +QObject *qobj; > + > +if (qiv->nb_stack == 0) { > +qobj = qiv->obj; > +} else { > +qobj = qiv->stack[qiv->nb_stack - 1].obj; > +} > + > +if (name && qobject_type(qobj) == QTYPE_QDICT) { > +return qdict_get(qobject_to_qdict(qobj), name); > +} else if (qiv->nb_stack > 0 && qobject_type(qobj) == QTYPE_QLIST) { > +return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); > +} > + > +return qobj; > +} > + > +static void qmp_input_push(QmpInputVisiter *qiv, QObject *obj) > +{ > +qiv->stack[qiv->nb_stack].obj = obj; > +if (qobject_type(obj) == QTYPE_QLIST) { > +qiv->stack[qiv->nb_stack].entry = qlist_first(qobject_to_qlist(obj)); > +} > +qiv->nb_stack++; > + > +assert(qiv->nb_stack < QIV_STACK_SIZE); // FIXME Can't this limit be reached if a client sends a nested object? Why don't we make it dynamic and/or return an error if a limit is reached? > +} > + > +static void qmp_input_pop(QmpInputVisiter *qiv) > +{ > +qiv->nb_stack--; > +assert(qiv->nb_stack >= 0); // FIXME > +} > + > +static void qmp_input_start_struct(Visiter *v, void **obj, const char *kind, > const char *name, Error **errp) > +{ > +QmpInputVisiter *qiv = to_qiv(v); > +QObject *qobj = qmp_input_get_object(qiv, name); > + > +if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { > +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "object"); > +return; > +} > + > +qmp_input_push(qiv, qobj); > + > +if (obj) { > +*obj = qemu_mallocz(QAPI_OBJECT_SIZE); I'm not sure I understand how this is being handled. This is allocating the struct size, right? What happens if struct size > QAPI_OBJECT_SIZE? > +} > +} > + > +static void qmp_input_end_struct(Visiter *v, Error **errp) > +{ > +QmpInputVisiter *qiv = to_qiv(v); > + > +qmp_input_pop(qiv); > +} > + > +static void qmp_input_start_list(Visiter *v, const char *name, Error **errp) > +{ > +QmpInputVisiter *qiv = to_qiv(v); > +QObject *qobj = qmp_input_get_object(qiv, name); > + > +if (!qobj || qobject_type(qobj) != QTYPE_QLIST) { > +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "list"); > +return; > +} > + > +qmp_input_push(qiv, qobj); > +} > + > +static GenericList *qmp_input_next_list(Visiter *v, GenericList **list, > Error **errp) > +{ > +QmpInputVisiter *qiv = to_qiv(v); > +GenericList *entry; > +StackObject *so = &qiv->stack[qiv->nb_stack - 1]; > + > +if (so->entry == NULL) { > +return NULL; > +} > + > +entry = qemu_mallocz(sizeof(*entry)); > +if (*list) { > +so->entry = qlist_next(so->entry); > +if (so->entry == NULL) { > +qemu_free(entry); > +return NULL; > +} > +(*list)->next = entry; > +} > +*list = entry; > + > + > +return entry; > +} > + > +static void qmp_input_end_list(Visiter *v, Error **errp) > +{ > +QmpInputVisiter *qiv = to_qiv(v); > + > +qmp_input_pop(qiv); > +} > + > +static void qmp_input_type_int(Visiter *v, int64_t *obj, const char *name, > Error **errp) > +{ > +QmpInputVisiter *qiv = to_qiv(v); > +QObject *qobj = qmp_input_get_object(qiv, name); > + > +if (!qobj || qobject_type(qobj) != QTYPE_QINT) { > +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "integer"); > +return; > +} > + > +*obj = qint_get_int(qobject_to_qint(qobj)); >
[Qemu-devel] [PATCH v2][ 10/21] qapi: add QMP input visiter
A type of Visiter class that is used to walk a qobject's structure and assign each entry to the corresponding native C type. Command marshaling function will use this to pull out QMP command parameters recieved over the wire and pass them as native arguments to the corresponding C functions. Signed-off-by: Michael Roth --- qapi/qmp-input-visiter.c | 239 ++ qapi/qmp-input-visiter.h | 26 + 2 files changed, 265 insertions(+), 0 deletions(-) create mode 100644 qapi/qmp-input-visiter.c create mode 100644 qapi/qmp-input-visiter.h diff --git a/qapi/qmp-input-visiter.c b/qapi/qmp-input-visiter.c new file mode 100644 index 000..6767e39 --- /dev/null +++ b/qapi/qmp-input-visiter.c @@ -0,0 +1,239 @@ +#include "qmp-input-visiter.h" +#include "qemu-queue.h" +#include "qemu-common.h" +#include "qemu-objects.h" +#include "qerror.h" + +#define QAPI_OBJECT_SIZE 512 + +#define QIV_STACK_SIZE 1024 + +typedef struct StackObject +{ +QObject *obj; +QListEntry *entry; +} StackObject; + +struct QmpInputVisiter +{ +Visiter visiter; +QObject *obj; +StackObject stack[QIV_STACK_SIZE]; +int nb_stack; +}; + +static QmpInputVisiter *to_qiv(Visiter *v) +{ +return container_of(v, QmpInputVisiter, visiter); +} + +static QObject *qmp_input_get_object(QmpInputVisiter *qiv, const char *name) +{ +QObject *qobj; + +if (qiv->nb_stack == 0) { +qobj = qiv->obj; +} else { +qobj = qiv->stack[qiv->nb_stack - 1].obj; +} + +if (name && qobject_type(qobj) == QTYPE_QDICT) { +return qdict_get(qobject_to_qdict(qobj), name); +} else if (qiv->nb_stack > 0 && qobject_type(qobj) == QTYPE_QLIST) { +return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); +} + +return qobj; +} + +static void qmp_input_push(QmpInputVisiter *qiv, QObject *obj) +{ +qiv->stack[qiv->nb_stack].obj = obj; +if (qobject_type(obj) == QTYPE_QLIST) { +qiv->stack[qiv->nb_stack].entry = qlist_first(qobject_to_qlist(obj)); +} +qiv->nb_stack++; + +assert(qiv->nb_stack < QIV_STACK_SIZE); // FIXME +} + +static void qmp_input_pop(QmpInputVisiter *qiv) +{ +qiv->nb_stack--; +assert(qiv->nb_stack >= 0); // FIXME +} + +static void qmp_input_start_struct(Visiter *v, void **obj, const char *kind, const char *name, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); +QObject *qobj = qmp_input_get_object(qiv, name); + +if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "object"); +return; +} + +qmp_input_push(qiv, qobj); + +if (obj) { +*obj = qemu_mallocz(QAPI_OBJECT_SIZE); +} +} + +static void qmp_input_end_struct(Visiter *v, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); + +qmp_input_pop(qiv); +} + +static void qmp_input_start_list(Visiter *v, const char *name, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); +QObject *qobj = qmp_input_get_object(qiv, name); + +if (!qobj || qobject_type(qobj) != QTYPE_QLIST) { +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "list"); +return; +} + +qmp_input_push(qiv, qobj); +} + +static GenericList *qmp_input_next_list(Visiter *v, GenericList **list, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); +GenericList *entry; +StackObject *so = &qiv->stack[qiv->nb_stack - 1]; + +if (so->entry == NULL) { +return NULL; +} + +entry = qemu_mallocz(sizeof(*entry)); +if (*list) { +so->entry = qlist_next(so->entry); +if (so->entry == NULL) { +qemu_free(entry); +return NULL; +} +(*list)->next = entry; +} +*list = entry; + + +return entry; +} + +static void qmp_input_end_list(Visiter *v, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); + +qmp_input_pop(qiv); +} + +static void qmp_input_type_int(Visiter *v, int64_t *obj, const char *name, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); +QObject *qobj = qmp_input_get_object(qiv, name); + +if (!qobj || qobject_type(qobj) != QTYPE_QINT) { +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "integer"); +return; +} + +*obj = qint_get_int(qobject_to_qint(qobj)); +} + +static void qmp_input_type_bool(Visiter *v, bool *obj, const char *name, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); +QObject *qobj = qmp_input_get_object(qiv, name); + +if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) { +error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, "boolean"); +return; +} + +*obj = qbool_get_int(qobject_to_qbool(qobj)); +} + +static void qmp_input_type_str(Visiter *v, char **obj, const char *name, Error **errp) +{ +QmpInputVisiter *qiv = to_qiv(v); +QObject *qobj = qmp_input_get_object(qiv, name); + +if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) { +error_set(errp, QERR_INVALID_P