qemu-ppc
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-ppc] [Qemu-devel] [PATCH v14 18/19] qapi: Simplify semantics o


From: Markus Armbruster
Subject: Re: [Qemu-ppc] [Qemu-devel] [PATCH v14 18/19] qapi: Simplify semantics of visit_next_list()
Date: Fri, 15 Apr 2016 16:09:29 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux)

Eric Blake <address@hidden> writes:

> We have two uses of list visits in the entire code base: one in
> spapr_drc (which completely avoids visit_next_list(), feeding in
> integers from a different source than uint8List), and one in
> qapi-visit.py

Period here, convert the parenthesis to a sentence.

>               (that is, all other list visitors are generated
> in qapi-visit.c, and share the same paradigm based on a qapi
> FooList type).

The above reviews existing uses.  Next, you show that visit_next_list()
semantics are awkward.  Suggest to start with the awkward semantics, and
only then review existing uses.

>                 What's more, the semantics of the list visit are
> somewhat baroque, with the following pseudocode when FooList is
> used:
>
> start()
> for (prev = head; cur = next(prev); prev = &cur) {
>     visit(&cur->value)
> }
>
> Note that these semantics (advance before visit) requires that
> the first call to next() return the list head, while all other
> calls return the next element of the list; that is, every visitor
> implementation is required to track extra state to decide whether
> to return the input as-is, or to advance.  It also requires an
> argument of 'GenericList **' to next(), solely because the first
> iteration might need to modify the caller's GenericList head, so
> that all other calls have to do a layer of dereferencing.
>
> We can greatly simplify things by hoisting the special case
> into the start() routine, and flipping the order in the loop
> to visit before advance:
>
> start(head)
> for (tail = *head; tail; tail = next(tail)) {
>     visit(&tail->value)
> }
>
> With the simpler semantics, visitors have less state to track,
> the argument to next() is reduced to 'GenericList *', and it
> also becomes obvious whether an input visitor is allocating a
> FooList during visit_start_list() (rather than the old way of
> not knowing if an allocation happened until the first
> visit_next_list()).

Minor drawback: we now allocate both in start_list() and in next_list().
Minor, because the allocation is trivial.

> The signature of visit_start_list() is chosen to match
> visit_start_struct(), with the new parameters after 'name'.

Good idea.

> The spapr_drc case is a virtual visit, done by passing NULL for
> list, similarly to how NULL is passed to visit_start_struct()
> when a qapi type is not used in those visits.  It was easy to
> provide these semantics for qmp-output and dealloc visitors,
> and a bit harder for qmp-input (several prerequisite patches
> refactored things to make this patch straightforward).  But it
> turned out that the string and opts visitors munge enough other
> state during visit_next_list() to make it easier to just
> document and require a GenericList visit for now; an assertion
> will remind us to adjust things if we need the semantics in the
> future.
>
> Signed-off-by: Eric Blake <address@hidden>
>
> ---
> v14: no change
> v13: documentation wording tweaks
> v12: rebase to earlier changes, drop R-b, improve documentation,
> split out qmp-input cleanups into prereq patches
> [no v10, v11]
> v9: no change
> v8: consistent parameter order, fix qmp_input_get_next_type() to not
> skip list entries
> v7: new patch
> ---
>  include/qapi/visitor.h               | 47 
> +++++++++++++++++++-----------------
>  include/qapi/visitor-impl.h          |  7 +++---
>  scripts/qapi-visit.py                | 18 +++++++-------
>  include/qapi/opts-visitor.h          |  2 +-
>  include/qapi/string-input-visitor.h  |  3 ++-
>  include/qapi/string-output-visitor.h |  3 ++-
>  qapi/qapi-visit-core.c               | 12 +++++----
>  hw/ppc/spapr_drc.c                   |  2 +-
>  qapi/opts-visitor.c                  | 33 +++++++++++--------------
>  qapi/qapi-dealloc-visitor.c          | 35 ++++++---------------------
>  qapi/qmp-input-visitor.c             | 32 ++++++++++++------------
>  qapi/qmp-output-visitor.c            | 22 ++++-------------
>  qapi/string-input-visitor.c          | 36 +++++++++++++--------------
>  qapi/string-output-visitor.c         | 41 +++++++++++--------------------
>  docs/qapi-code-gen.txt               | 17 +++++++------
>  15 files changed, 133 insertions(+), 177 deletions(-)
>
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index e1213a3..a22a7ce 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -165,7 +165,7 @@
>   *  if (err) {
>   *      goto outobj;
>   *  }
> - *  visit_start_list(v, "list", &err);
> + *  visit_start_list(v, "list", NULL, 0, &err);
>   *  if (err) {
>   *      goto outobj;
>   *  }
> @@ -268,40 +268,43 @@ void visit_end_struct(Visitor *v);
>   * @name expresses the relationship of this list to its parent
>   * container; see the general description of @name above.
>   *
> + * @list must be non-NULL for a real walk, in which case @size
> + * determines how much memory an input visitor will allocate into
> + * address@hidden (at least sizeof(GenericList)).  Some visitors also allow
> + * @list to be NULL for a virtual walk, in which case @size is
> + * ignored.
> + *
>   * @errp must be NULL-initialized, and is set if an error is detected
>   * (such as if a member @name is not present, or is present but not a
> - * list).
> + * list).  On error, input visitors set address@hidden to NULL.

I guess you copied this from visit_start_struct(), which only makes
sense.  Except the thing is called @list here.  More of the same below.

>   *
>   * After visit_start_list() succeeds, the caller may visit its members
> - * one after the other.  A real visit uses visit_next_list() for
> - * traversing the linked list, while a virtual visit uses other means.
> - * For each list element, call the appropriate visit_type_FOO() with
> - * name set to NULL and obj set to the address of the value member of
> - * the list element.  Finally, visit_end_list() needs to be called to
> - * clean up, even if intermediate visits fail.  See the examples
> - * above.
> + * one after the other.  A real visit (where @obj is non-NULL) uses
> + * visit_next_list() for traversing the linked list, while a virtual
> + * visit (where @obj is NULL) uses other means.  For each list
> + * element, call the appropriate visit_type_FOO() with name set to
> + * NULL and obj set to the address of the value member of the list
> + * element.  Finally, visit_end_list() needs to be called to clean up,
> + * even if intermediate visits fail.  See the examples above.
>   */
> -void visit_start_list(Visitor *v, const char *name, Error **errp);
> +void visit_start_list(Visitor *v, const char *name, GenericList **list,
> +                      size_t size, Error **errp);
>
>  /*
> - * Iterate over a GenericList during a list visit.
> + * Iterate over a GenericList during a non-virtual list visit.

Is "non-virtual" new in this patch?  If not, is this the right patch to
insert it?

>   *
> - * @size represents the size of a linked list node.
> + * @size represents the size of a linked list node (at least
> + * sizeof(GenericList)).

Likewise.

>   *
> - * @list must not be NULL; on the first call, @list contains the
> - * address of the list head, and on subsequent calls address@hidden must be
> - * the previously returned value.  Must be called in a loop until a
> + * @tail must not be NULL; on the first call, @tail is the value of
> + * *list after visit_start_list(), and on subsequent calls @tail must
> + * be the previously returned value.  Must be called in a loop until a
>   * NULL return or error occurs; for each non-NULL return, the caller
>   * must then call the appropriate visit_type_*() for the element type
>   * of the list, with that function's name parameter set to NULL and
> - * obj set to the address of (address@hidden)->value.
> - *
> - * FIXME: This interface is awkward; it requires all callbacks to
> - * track whether it is the first or a subsequent call.  A better
> - * interface would pass the head of the list through
> - * visit_start_list().
> + * obj set to the address of @tail->value.
>   */
> -GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size);
> +GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size);
>
>  /*
>   * Complete a list visit started earlier.
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index d44fcd1..0471465 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -45,11 +45,12 @@ struct Visitor
>      /* Must be set to visit structs.  */
>      void (*end_struct)(Visitor *v);
>
> -    /* Must be set.  */
> -    void (*start_list)(Visitor *v, const char *name, Error **errp);
> +    /* Must be set; document if @list may not be NULL.  */

Too terse, I'm afraid.  Suggest something like "implementations may
require @list to be non-null, but must document it."

> +    void (*start_list)(Visitor *v, const char *name, GenericList **list,
> +                       size_t size, Error **errp);
>
>      /* Must be set.  */
> -    GenericList *(*next_list)(Visitor *v, GenericList **list, size_t size);
> +    GenericList *(*next_list)(Visitor *v, GenericList *tail, size_t size);
>
>      /* Must be set.  */
>      void (*end_list)(Visitor *v);
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index bfe4a09..69f0133 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -117,20 +117,20 @@ def gen_visit_list(name, element_type):
>  void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, 
> Error **errp)
>  {
>      Error *err = NULL;
> -    GenericList *i, **prev;
> +    %(c_name)s *tail;
> +    size_t size = sizeof(**obj);
>
> -    visit_start_list(v, name, &err);
> +    visit_start_list(v, name, (GenericList **)obj, size, &err);
>      if (err) {
>          goto out;
>      }
> -
> -    for (prev = (GenericList **)obj;
> -         !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL;
> -         prev = &i) {
> -        %(c_name)s *native_i = (%(c_name)s *)i;
> -        visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
> +    for (tail = *obj; tail;
> +         tail = (%(c_name)s *)visit_next_list(v, (GenericList *)tail, size)) 
> {
> +        visit_type_%(c_elt_type)s(v, NULL, &tail->value, &err);
> +        if (err) {
> +            break;
> +        }
>      }
> -

Keep the blank lines around the loop?

>      visit_end_list(v);
>  out:
>      error_propagate(errp, err);
> diff --git a/include/qapi/opts-visitor.h b/include/qapi/opts-visitor.h
> index 3fcd327..7c9a7e5 100644
> --- a/include/qapi/opts-visitor.h
> +++ b/include/qapi/opts-visitor.h
> @@ -32,7 +32,7 @@ typedef struct OptsVisitor OptsVisitor;
>   *
>   * The Opts input visitor does not yet implement support for visiting
>   * QAPI alternates, numbers (other than integers), null, or arbitrary
> - * QTypes.
> + * QTypes.  It also requires non-NULL list to visit_start_list().

non-null list argument to

>   */
>  OptsVisitor *opts_visitor_new(const QemuOpts *opts);
>  void opts_visitor_cleanup(OptsVisitor *nv);
> diff --git a/include/qapi/string-input-visitor.h 
> b/include/qapi/string-input-visitor.h
> index 1a34c52..d26a845 100644
> --- a/include/qapi/string-input-visitor.h
> +++ b/include/qapi/string-input-visitor.h
> @@ -19,7 +19,8 @@ typedef struct StringInputVisitor StringInputVisitor;
>
>  /*
>   * The string input visitor does not yet implement support for
> - * visiting QAPI structs, alternates, null, or arbitrary QTypes.
> + * visiting QAPI structs, alternates, null, or arbitrary QTypes.  It
> + * also requires non-NULL list to visit_start_list().

Likewise.

>   */
>  StringInputVisitor *string_input_visitor_new(const char *str);
>  void string_input_visitor_cleanup(StringInputVisitor *v);
> diff --git a/include/qapi/string-output-visitor.h 
> b/include/qapi/string-output-visitor.h
> index 2564833..0a9354c 100644
> --- a/include/qapi/string-output-visitor.h
> +++ b/include/qapi/string-output-visitor.h
> @@ -19,7 +19,8 @@ typedef struct StringOutputVisitor StringOutputVisitor;
>
>  /*
>   * The string output visitor does not yet implement support for
> - * visiting QAPI structs, alternates, null, or arbitrary QTypes.
> + * visiting QAPI structs, alternates, null, or arbitrary QTypes.  It
> + * also requires non-NULL list to visit_start_list().

Likewise.

>   */
>  StringOutputVisitor *string_output_visitor_new(bool human);
>  void string_output_visitor_cleanup(StringOutputVisitor *v);
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 76ea690..622b315 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -42,15 +42,17 @@ void visit_end_struct(Visitor *v)
>      v->end_struct(v);
>  }
>
> -void visit_start_list(Visitor *v, const char *name, Error **errp)
> +void visit_start_list(Visitor *v, const char *name, GenericList **list,
> +                      size_t size, Error **errp)
>  {
> -    v->start_list(v, name, errp);
> +    assert(!list || size >= sizeof(GenericList));
> +    v->start_list(v, name, list, size, errp);
>  }
>
> -GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size)
> +GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size)
>  {
> -    assert(list && size >= sizeof(GenericList));
> -    return v->next_list(v, list, size);
> +    assert(tail && size >= sizeof(GenericList));
> +    return v->next_list(v, tail, size);
>  }
>
>  void visit_end_list(Visitor *v)
> diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
> index 5395c02..dd3c502 100644
> --- a/hw/ppc/spapr_drc.c
> +++ b/hw/ppc/spapr_drc.c
> @@ -309,7 +309,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const 
> char *name,
>              int i;
>              prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len);
>              name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
> -            visit_start_list(v, name, &err);
> +            visit_start_list(v, name, NULL, 0, &err);
>              if (err) {
>                  error_propagate(errp, err);
>                  return;
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index a08d5a7..6d572cc 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -23,9 +23,8 @@
>  enum ListMode
>  {
>      LM_NONE,             /* not traversing a list of repeated options */
> -    LM_STARTED,          /* opts_start_list() succeeded */
>
> -    LM_IN_PROGRESS,      /* opts_next_list() has been called.
> +    LM_IN_PROGRESS,      /* opts_next_list() ready to be called.
>                            *
>                            * Generating the next list link will consume the 
> most
>                            * recently parsed QemuOpt instance of the repeated
> @@ -214,35 +213,33 @@ lookup_distinct(const OptsVisitor *ov, const char 
> *name, Error **errp)
>
>
>  static void
> -opts_start_list(Visitor *v, const char *name, Error **errp)
> +opts_start_list(Visitor *v, const char *name, GenericList **list, size_t 
> size,
> +                Error **errp)
>  {
>      OptsVisitor *ov = to_ov(v);
>
>      /* we can't traverse a list in a list */
>      assert(ov->list_mode == LM_NONE);
> +    /* we don't support visits without GenericList, yet */

without a list

Do we plan to support them?  If not, scratch "yet".

> +    assert(list);
>      ov->repeated_opts = lookup_distinct(ov, name, errp);
> -    if (ov->repeated_opts != NULL) {
> -        ov->list_mode = LM_STARTED;
> +    if (ov->repeated_opts) {
> +        ov->list_mode = LM_IN_PROGRESS;
> +        *list = g_malloc0(size);
> +    } else {
> +        *list = NULL;
>      }
>  }
>
>
>  static GenericList *
> -opts_next_list(Visitor *v, GenericList **list, size_t size)
> +opts_next_list(Visitor *v, GenericList *tail, size_t size)
>  {
>      OptsVisitor *ov = to_ov(v);
> -    GenericList **link;
>
>      switch (ov->list_mode) {
> -    case LM_STARTED:
> -        ov->list_mode = LM_IN_PROGRESS;
> -        link = list;
> -        break;
> -
>      case LM_SIGNED_INTERVAL:
>      case LM_UNSIGNED_INTERVAL:
> -        link = &(*list)->next;
> -
>          if (ov->list_mode == LM_SIGNED_INTERVAL) {
>              if (ov->range_next.s < ov->range_limit.s) {
>                  ++ov->range_next.s;
> @@ -263,7 +260,6 @@ opts_next_list(Visitor *v, GenericList **list, size_t 
> size)
>              g_hash_table_remove(ov->unprocessed_opts, opt->name);
>              return NULL;
>          }
> -        link = &(*list)->next;
>          break;
>      }
>
> @@ -271,8 +267,8 @@ opts_next_list(Visitor *v, GenericList **list, size_t 
> size)
>          abort();
>      }
>
> -    *link = g_malloc0(size);
> -    return *link;
> +    tail->next = g_malloc0(size);
> +    return tail->next;
>  }
>
>
> @@ -281,8 +277,7 @@ opts_end_list(Visitor *v)
>  {
>      OptsVisitor *ov = to_ov(v);
>
> -    assert(ov->list_mode == LM_STARTED ||
> -           ov->list_mode == LM_IN_PROGRESS ||
> +    assert(ov->list_mode == LM_IN_PROGRESS ||
>             ov->list_mode == LM_SIGNED_INTERVAL ||
>             ov->list_mode == LM_UNSIGNED_INTERVAL);
>      ov->repeated_opts = NULL;
> diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
> index 9005bad..cd68b55 100644
> --- a/qapi/qapi-dealloc-visitor.c
> +++ b/qapi/qapi-dealloc-visitor.c
> @@ -22,7 +22,6 @@
>  typedef struct StackEntry
>  {
>      void *value;
> -    bool is_list_head;
>      QTAILQ_ENTRY(StackEntry) node;
>  } StackEntry;
>
> @@ -43,10 +42,6 @@ static void qapi_dealloc_push(QapiDeallocVisitor *qov, 
> void *value)
>
>      e->value = value;
>
> -    /* see if we're just pushing a list head tracker */
> -    if (value == NULL) {
> -        e->is_list_head = true;
> -    }
>      QTAILQ_INSERT_HEAD(&qov->stack, e, node);
>  }
>
> @@ -93,38 +88,22 @@ static void qapi_dealloc_end_alternate(Visitor *v)
>      }
>  }
>
> -static void qapi_dealloc_start_list(Visitor *v, const char *name, Error 
> **errp)
> +static void qapi_dealloc_start_list(Visitor *v, const char *name,
> +                                    GenericList **list, size_t size,
> +                                    Error **errp)
>  {
> -    QapiDeallocVisitor *qov = to_qov(v);
> -    qapi_dealloc_push(qov, NULL);
>  }
>
> -static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp,
> +static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *tail,
>                                             size_t size)
>  {
> -    GenericList *list = *listp;
> -    QapiDeallocVisitor *qov = to_qov(v);
> -    StackEntry *e = QTAILQ_FIRST(&qov->stack);
> -
> -    if (e && e->is_list_head) {
> -        e->is_list_head = false;
> -        return list;
> -    }
> -
> -    if (list) {
> -        list = list->next;
> -        g_free(*listp);
> -        return list;
> -    }
> -
> -    return NULL;
> +    GenericList *next = tail->next;
> +    g_free(tail);
> +    return next;
>  }
>
>  static void qapi_dealloc_end_list(Visitor *v)
>  {
> -    QapiDeallocVisitor *qov = to_qov(v);
> -    void *obj = qapi_dealloc_pop(qov);
> -    assert(obj == NULL); /* should've been list head tracker with no payload 
> */
>  }
>

One step closer to killing the stack.

>  static void qapi_dealloc_type_str(Visitor *v, const char *name, char **obj,
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index 16f2f5a..5360744 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -29,8 +29,6 @@ typedef struct StackObject
>
>      /* If obj is list: tail of list still needing visits */
>      const QListEntry *entry;
> -    /* If obj is list: true if head is not visited yet */
> -    bool first;
>
>      GHashTable *h; /* If obj is dict: remaining keys needing visits */
>  } StackObject;
> @@ -87,7 +85,6 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
>          assert(!name);
>
>          if (tos->entry) {
> -            assert(!tos->first);
>              qobj = qlist_entry_obj(tos->entry);
>              if (consume) {
>                  tos->entry = qlist_next(tos->entry);
> @@ -117,7 +114,6 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject 
> *obj,
>
>      tos->obj = obj;
>      tos->entry = entry;
> -    tos->first = true;
>      tos->h = NULL;
>
>      if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
> @@ -194,13 +190,17 @@ static void qmp_input_start_struct(Visitor *v, const 
> char *name, void **obj,
>  }
>
>
> -static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
> +static void qmp_input_start_list(Visitor *v, const char *name,
> +                                 GenericList **list, size_t size, Error 
> **errp)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
>      QObject *qobj = qmp_input_get_object(qiv, name, true);
>      const QListEntry *entry;
>
>      if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
> +        if (list) {
> +            *list = NULL;
> +        }
>          error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
>                     "list");
>          return;
> @@ -208,28 +208,26 @@ static void qmp_input_start_list(Visitor *v, const char 
> *name, Error **errp)
>
>      entry = qlist_first(qobject_to_qlist(qobj));
>      qmp_input_push(qiv, qobj, entry, errp);
> +    if (list) {
> +        if (entry) {
> +            *list = g_malloc0(size);
> +        } else {
> +            *list = NULL;
> +        }
> +    }
>  }
>
> -static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
> +static GenericList *qmp_input_next_list(Visitor *v, GenericList *tail,
>                                          size_t size)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
> -    GenericList *entry;
>      StackObject *so = &qiv->stack[qiv->nb_stack - 1];
>
>      if (!so->entry) {
>          return NULL;
>      }
> -
> -    entry = g_malloc0(size);
> -    if (so->first) {
> -        *list = entry;
> -        so->first = false;
> -    } else {
> -        (*list)->next = entry;
> -    }
> -
> -    return entry;
> +    tail->next = g_malloc0(size);
> +    return tail->next;
>  }
>
>
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index ecb2005..40657be 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -22,7 +22,6 @@
>  typedef struct QStackEntry
>  {
>      QObject *value;
> -    bool is_list_head;
>      QTAILQ_ENTRY(QStackEntry) node;
>  } QStackEntry;
>
> @@ -52,9 +51,6 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, 
> QObject *value)
>      assert(qov->root);
>      assert(value);
>      e->value = value;
> -    if (qobject_type(e->value) == QTYPE_QLIST) {
> -        e->is_list_head = true;
> -    }
>      QTAILQ_INSERT_HEAD(&qov->stack, e, node);
>  }
>
> @@ -120,7 +116,9 @@ static void qmp_output_end_struct(Visitor *v)
>      assert(qobject_type(value) == QTYPE_QDICT);
>  }
>
> -static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
> +static void qmp_output_start_list(Visitor *v, const char *name,
> +                                  GenericList **listp, size_t size,
> +                                  Error **errp)
>  {
>      QmpOutputVisitor *qov = to_qov(v);
>      QList *list = qlist_new();
> @@ -129,20 +127,10 @@ static void qmp_output_start_list(Visitor *v, const 
> char *name, Error **errp)
>      qmp_output_push(qov, list);
>  }
>
> -static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp,
> +static GenericList *qmp_output_next_list(Visitor *v, GenericList *tail,
>                                           size_t size)
>  {
> -    GenericList *list = *listp;
> -    QmpOutputVisitor *qov = to_qov(v);
> -    QStackEntry *e = QTAILQ_FIRST(&qov->stack);
> -
> -    assert(e);
> -    if (e->is_list_head) {
> -        e->is_list_head = false;
> -        return list;
> -    }
> -
> -    return list ? list->next : NULL;
> +    return tail->next;
>  }
>
>  static void qmp_output_end_list(Visitor *v)
> diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
> index 797973a..77dd1a7 100644
> --- a/qapi/string-input-visitor.c
> +++ b/qapi/string-input-visitor.c
> @@ -25,8 +25,6 @@ struct StringInputVisitor
>  {
>      Visitor visitor;
>
> -    bool head;
> -
>      GList *ranges;
>      GList *cur_range;
>      int64_t cur;
> @@ -125,11 +123,21 @@ error:
>  }
>
>  static void
> -start_list(Visitor *v, const char *name, Error **errp)
> +start_list(Visitor *v, const char *name, GenericList **list, size_t size,
> +           Error **errp)
>  {
>      StringInputVisitor *siv = to_siv(v);
> +    Error *err = NULL;
>
> -    parse_str(siv, errp);
> +    /* We don't support visits without a GenericList, yet */

without a list

Do we plan to support them?  If not, scratch "yet".

> +    assert(list);
> +
> +    parse_str(siv, &err);
> +    if (err) {
> +        *list = NULL;
> +        error_propagate(errp, err);
> +        return;
> +    }
>
>      siv->cur_range = g_list_first(siv->ranges);
>      if (siv->cur_range) {
> @@ -137,13 +145,15 @@ start_list(Visitor *v, const char *name, Error **errp)
>          if (r) {
>              siv->cur = r->begin;
>          }
> +        *list = g_malloc0(size);
> +    } else {
> +        *list = NULL;
>      }
>  }
>
> -static GenericList *next_list(Visitor *v, GenericList **list, size_t size)
> +static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
>  {
>      StringInputVisitor *siv = to_siv(v);
> -    GenericList **link;
>      Range *r;
>
>      if (!siv->ranges || !siv->cur_range) {
> @@ -167,21 +177,12 @@ static GenericList *next_list(Visitor *v, GenericList 
> **list, size_t size)
>          siv->cur = r->begin;
>      }
>
> -    if (siv->head) {
> -        link = list;
> -        siv->head = false;
> -    } else {
> -        link = &(*list)->next;
> -    }
> -
> -    *link = g_malloc0(size);
> -    return *link;
> +    tail->next = g_malloc0(size);
> +    return tail->next;
>  }
>
>  static void end_list(Visitor *v)
>  {
> -    StringInputVisitor *siv = to_siv(v);
> -    siv->head = true;
>  }
>
>  static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
> @@ -362,6 +363,5 @@ StringInputVisitor *string_input_visitor_new(const char 
> *str)
>      v->visitor.optional = parse_optional;
>
>      v->string = str;
> -    v->head = true;
>      return v;
>  }
> diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
> index 0d44d7e..e27e2df 100644
> --- a/qapi/string-output-visitor.c
> +++ b/qapi/string-output-visitor.c
> @@ -20,7 +20,7 @@
>
>  enum ListMode {
>      LM_NONE,             /* not traversing a list of repeated options */
> -    LM_STARTED,          /* start_list() succeeded */
> +    LM_STARTED,          /* start_list() succeeded with multiple elements */

In opts-visitor.c, the comment says "opts_next_list() ready to be
called".  Any reason for the difference?

>
>      LM_IN_PROGRESS,      /* next_list() has been called.
>                            *
> @@ -48,7 +48,7 @@ enum ListMode {
>
>      LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */
>
> -    LM_END
> +    LM_END,              /* next_list() called, about to see last element. */
>  };
>
>  typedef enum ListMode ListMode;
> @@ -58,7 +58,6 @@ struct StringOutputVisitor
>      Visitor visitor;
>      bool human;
>      GString *string;
> -    bool head;
>      ListMode list_mode;
>      union {
>          int64_t s;
> @@ -266,39 +265,29 @@ static void print_type_number(Visitor *v, const char 
> *name, double *obj,
>  }
>
>  static void
> -start_list(Visitor *v, const char *name, Error **errp)
> +start_list(Visitor *v, const char *name, GenericList **list, size_t size,
> +           Error **errp)
>  {
>      StringOutputVisitor *sov = to_sov(v);
>
>      /* we can't traverse a list in a list */
>      assert(sov->list_mode == LM_NONE);
> -    sov->list_mode = LM_STARTED;
> -    sov->head = true;
> +    /* We don't support visits without a GenericList, yet */

without a list

Do we plan to support them?  If not, scratch "yet".

> +    assert(list);
> +    /* List handling is only needed if there are at least two elements */
> +    if (*list && (*list)->next) {
> +        sov->list_mode = LM_STARTED;
> +    }
>  }
>
> -static GenericList *next_list(Visitor *v, GenericList **list, size_t size)
> +static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
>  {
>      StringOutputVisitor *sov = to_sov(v);
> -    GenericList *ret = NULL;
> -    if (*list) {
> -        if (sov->head) {
> -            ret = *list;
> -        } else {
> -            ret = (*list)->next;
> -        }
> +    GenericList *ret = tail->next;
>
> -        if (sov->head) {
> -            if (ret && ret->next == NULL) {
> -                sov->list_mode = LM_NONE;
> -            }
> -            sov->head = false;
> -        } else {
> -            if (ret && ret->next == NULL) {
> -                sov->list_mode = LM_END;
> -            }
> -        }
> +    if (ret && !ret->next) {
> +        sov->list_mode = LM_END;
>      }
> -
>      return ret;
>  }
>
> @@ -311,8 +300,6 @@ static void end_list(Visitor *v)
>             sov->list_mode == LM_NONE ||
>             sov->list_mode == LM_IN_PROGRESS);
>      sov->list_mode = LM_NONE;
> -    sov->head = true;
> -
>  }
>
>  char *string_output_get_string(StringOutputVisitor *sov)
> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> index 3b2422f..99080db 100644
> --- a/docs/qapi-code-gen.txt
> +++ b/docs/qapi-code-gen.txt
> @@ -912,18 +912,19 @@ Example:
>      void visit_type_UserDefOneList(Visitor *v, const char *name, 
> UserDefOneList **obj, Error **errp)
>      {
>          Error *err = NULL;
> -        GenericList *i, **prev;
> +        UserDefOneList *tail;
> +        size_t size = sizeof(**obj);
>
> -        visit_start_list(v, name, &err);
> +        visit_start_list(v, name, (GenericList **)obj, size, &err);
>          if (err) {
>              goto out;
>          }
> -
> -        for (prev = (GenericList **)obj;
> -             !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL;
> -             prev = &i) {
> -            UserDefOneList *native_i = (UserDefOneList *)i;
> -            visit_type_UserDefOne(v, NULL, &native_i->value, &err);
> +        for (tail = *obj; tail;
> +             tail = (UserDefOneList *)visit_next_list(v, (GenericList 
> *)tail, size)) {
> +            visit_type_UserDefOne(v, NULL, &tail->value, &err);
> +            if (err) {
> +                break;
> +            }
>          }
>
>          visit_end_list(v);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]