All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: Eric Blake <eblake@redhat.com>
Cc: Michael Roth <mdroth@linux.vnet.ibm.com>,
	Alexander Graf <agraf@suse.de>,
	qemu-devel@nongnu.org, "open list:sPAPR" <qemu-ppc@nongnu.org>,
	marcandre.lureau@redhat.com,
	David Gibson <david@gibson.dropbear.id.au>
Subject: Re: [Qemu-devel] [PATCH v9 34/37] qapi: Simplify semantics of visit_next_list()
Date: Thu, 28 Jan 2016 14:37:41 +0100	[thread overview]
Message-ID: <87si1hakpm.fsf@blackfin.pond.sub.org> (raw)
In-Reply-To: <1453219845-30939-35-git-send-email-eblake@redhat.com> (Eric Blake's message of "Tue, 19 Jan 2016 09:10:42 -0700")

Eric Blake <eblake@redhat.com> 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 (that is, all other list visitors are generated
> in qapi-visit.c, and share the same paradigm based on a qapi
> FooList type).  What's more, the semantics of the list visit are
> somewhat baroque, with the following pseudocode when FooList is
> used:
>
> start()
> prev = head
> while (cur = next(prev)) {
>     visit(cur)

Actually, we pass &cur->value to the element visit.

>     prev = &cur
> }
>
> 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)
> element = *head
> while (element) {
>     visit(element)
>     element = next(element)
> }

@element isn't a list element, it's a list node.  Suggest

  start(head)
  tail = *head
  while (tail) {
      visit(&tail->value)
      tail = next(tail)
  }

Of course, this pseudo-code just screams to be a for-loop instead:

  for (tail = *head; tail; tail = next(tail)) ...

May or may not be an improvement of the real code.  The pseudo-code
should follow the real code.

> 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()).
>
> The signature of visit_start_list() is chosen to match
> visit_start_struct(), with the new parameter after 'name'.
>
> The spapr_drc case requires that visit_start_list() has to pay
> attention to whether visit_next_list() will even be used to
> visit a FooList qapi struct; this is 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 (it required hoisting the
> advance of the current qlist entry out of qmp_input_next_list()
> into qmp_input_get_object()).  But it turned out that the
> string and opts visitors munge enough state during
> visit_next_list() to make those conversions simpler if they
> 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 <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: consistent parameter order, fix qmp_input_get_next_type() to not
> skip list entries
> v7: new patch
> ---
>  hw/ppc/spapr_drc.c           |  2 +-
>  include/qapi/visitor-impl.h  |  5 ++--
>  include/qapi/visitor.h       | 45 +++++++++++++++++--------------
>  qapi/opts-visitor.c          | 32 +++++++++-------------
>  qapi/qapi-dealloc-visitor.c  | 29 +++++---------------
>  qapi/qapi-visit-core.c       |  7 ++---
>  qapi/qmp-input-visitor.c     | 64 +++++++++++++++++++++-----------------------
>  qapi/qmp-output-visitor.c    | 21 +++------------
>  qapi/string-input-visitor.c  | 34 +++++++++++------------
>  qapi/string-output-visitor.c | 36 ++++++++-----------------
>  scripts/qapi-visit.py        | 21 ++++++++-------
>  11 files changed, 126 insertions(+), 170 deletions(-)

Diffstat suggests it's indeed a simplification.

>
> diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
> index 3b27caa..41f2da0 100644
> --- a/hw/ppc/spapr_drc.c
> +++ b/hw/ppc/spapr_drc.c
> @@ -299,7 +299,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, &err);
>              if (err) {
>                  error_propagate(errp, err);
>                  return;
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 248b1e5..acbe7d6 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -40,9 +40,10 @@ struct Visitor
>      void (*end_implicit_struct)(Visitor *v);
>
>      /* Must be set */
> -    void (*start_list)(Visitor *v, const char *name, Error **errp);
> +    void (*start_list)(Visitor *v, const char *name, GenericList **list,
> +                       Error **errp);
>      /* Must be set */
> -    GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
> +    GenericList *(*next_list)(Visitor *v, GenericList *element, Error **errp);

You rename the parameter here, but not in the implementations.

The parameter isn't a list element, it's a list node.  If we want to
rename it, what about tail?

>      /* Must be set */
>      void (*end_list)(Visitor *v);
>
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index e5dcde4..4638863 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -1,6 +1,7 @@
>  /*
>   * Core Definitions for QAPI Visitor Classes
>   *
> + * Copyright (C) 2015 Red Hat, Inc.

It's 2016 now.  I'd make it 2012-2016.

>   * Copyright IBM, Corp. 2011
>   *
>   * Authors:
> @@ -111,32 +112,36 @@ void visit_end_implicit_struct(Visitor *v);
>  /**
>   * Prepare to visit a list tied to an object key @name.
>   * @name will be NULL if this is visited as part of another list.
> - * After calling this, the elements must be collected until
> - * visit_next_list() returns NULL, then visit_end_list() must be
> - * used to complete the visit.
> - */
> -void visit_start_list(Visitor *v, const char *name, Error **errp);
> -/**
> - * Iterate over a GenericList during a list visit.
> - * @list must not be NULL; on the first call, @list contains the
> - * address of the list head, and on subsequent calls *@list 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.
> + * Input visitors malloc a qapi List struct into *@list,

QAPI

What's a QAPI list struct?  I guess you mean GenericList.

>                                                           or set it to
> + * NULL if there are no elements in the list;

So start_list() now needs to know whether the list is empty.

visit_start_struct() has an "if @obj is not NULL" clause here.  Do we
need the equivalent "if @list is not NULL"?  Ah, you do that further
down!  Can we structure the two comments the same way?

>                                                and output visitors
> + * expect *@list to point to the start of the list, if any.

Perhaps "to the first list node, if any", and give GenericList a better
comment in PATCH 21:

    /*
     * Generic list node
     * Any generated QAPI FOOList struct pointer can be safely cast to
     * GenericList * and dereferenced.
     */

Of course, this actually assumes uniform pointer representation, which
is not guaranteed by the standard.

Should we mention output visitors don't change *@list?  Same for
visit_start_struct(), by the way.

What about the dealloc visitor?

>                                                               On
> + * return, if *@list is non-NULL, the caller should enter a loop
> + * visiting the current element, then using visit_next_list() to
> + * advance to the next element, until that returns NULL; then
> + * visit_end_list() must be used to complete the visit.

For visit_start_struct() this part reads:

                                                             The
 * caller then makes a series of visit calls for each key expected in
 * the object, where those visits set their respective obj parameter
 * to the address of a member of the qapi struct, and follows
 * everything by a call to visit_end_struct() to clean up resources.

Following that pattern here, I get:

   The caller then visits the list elements in turn, where those visits
   get passed the address of the list element within the QAPI list node.
   The caller normally uses visit_next_list() to step through the list.
   When done, it must call visit_end_list() to clean up.

>   *
> - * Note that for some visitors (qapi-dealloc and qmp-output), when a
> - * qapi GenericList linked list is not being used (comparable to when
> - * a NULL obj is used for visit_start_struct()), it is acceptable to
> - * bypass the use of visit_next_list() and just directly call the
> - * appropriate visit_type_*() for each element in between the
> - * visit_start_list() and visit_end_list() calls.
> + * If supported by a visitor, @list can be NULL to indicate that there

by the visitor

> + * is no qapi List struct, and that the upcoming visit calls are
> + * parsing input to or creating output from some other representation;
> + * in this case, visit_next_list() will not be needed, but
> + * visit_end_list() is still mandatory.

You first explain normal usage, then add this paragraph to explain
special usage.  I like that better than the visit_start_struct()
comment, where the special usage comes earlier.

>   *
>   * FIXME: For input visitors, *@list can be assigned here even if
>   * later visits will fail; this can lead to memory leaks if clients
>   * aren't careful.
>   */
> -GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
> +void visit_start_list(Visitor *v, const char *name, GenericList **list,
> +                      Error **errp);
> +/**
> + * Iterate over a GenericList during a list visit.
> + * Before calling this function, the caller should use the appropriate
> + * visit_type_FOO() for the current list element at @element->value, and
> + * check for errors. @element must not be NULL; on the first iteration,
> + * it should be the value in *list after visit_start_list(); on other
> + * calls it should be the previous return value.  This function
> + * returns NULL once there are no further list elements.
> + */

I feel if we get visit_start_list()'s comment right, then this one can
concentrate on what this function does, and leave intended usage of the
start/next/end team to visit_start_list()'s comment.

> +GenericList *visit_next_list(Visitor *v, GenericList *element, Error **errp);
>  /**
>   * Complete the list started earlier.
>   * Must be called after any successful use of visit_start_list(),
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index b469573..c5a7396 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -21,9 +21,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
> @@ -212,35 +211,32 @@ 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, Error **errp)

I'd break this line before Error.

>  {
>      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 */
> +    assert(list);

Aha, this is why you wrote "If supported by a visitor".  The visitors
better document what they support then.

>      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_new0(GenericList, 1);
> +    } else {
> +        *list = NULL;
>      }
>  }
>
>
>  static GenericList *
> -opts_next_list(Visitor *v, GenericList **list, Error **errp)
> +opts_next_list(Visitor *v, GenericList *list, Error **errp)
>  {
>      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;
> @@ -261,7 +257,6 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)
>              g_hash_table_remove(ov->unprocessed_opts, opt->name);
>              return NULL;
>          }
> -        link = &(*list)->next;
>          break;
>      }
>
> @@ -269,8 +264,8 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)
>          abort();
>      }
>
> -    *link = g_malloc0(sizeof **link);
> -    return *link;
> +    list->next = g_new0(GenericList, 1);
> +    return list->next;
>  }
>
>
> @@ -279,8 +274,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;

Only slight simplification for this visitor: we lose LM_STARTED.

> diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
> index a89e6d1..839049e 100644
> --- a/qapi/qapi-dealloc-visitor.c
> +++ b/qapi/qapi-dealloc-visitor.c
> @@ -20,7 +20,6 @@
>  typedef struct StackEntry
>  {
>      void *value;
> -    bool is_list_head;
>      QTAILQ_ENTRY(StackEntry) node;
>  } StackEntry;
>
> @@ -41,10 +40,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);
>  }
>
> @@ -92,31 +87,19 @@ static void qapi_dealloc_end_implicit_struct(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, Error **errp)
>  {
>      QapiDeallocVisitor *qov = to_qov(v);
>      qapi_dealloc_push(qov, NULL);

Do we still need to push/pop for a list?

If yes, can we push list instead of NULL?  Pointers always become more
complicated when they can be null...

>  }
>
> -static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp,
> +static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *list,
>                                             Error **errp)
>  {
> -    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 = list->next;
> +    g_free(list);
> +    return next;
>  }

Okay, this is actually a more worthwhile simplification.

>
>  static void qapi_dealloc_end_list(Visitor *v)
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 9506a02..f391a70 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -67,12 +67,13 @@ void visit_end_implicit_struct(Visitor *v)
>      }
>  }
>
> -void visit_start_list(Visitor *v, const char *name, Error **errp)
> +void visit_start_list(Visitor *v, const char *name, GenericList **list,
> +                      Error **errp)
>  {
> -    v->start_list(v, name, errp);
> +    v->start_list(v, name, list, errp);
>  }
>
> -GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
> +GenericList *visit_next_list(Visitor *v, GenericList *list, Error **errp)
>  {
>      assert(list);
>      return v->next_list(v, list, errp);
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index f256d9e..82f9333 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -44,16 +44,20 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
>                                       const char *name,
>                                       bool consume)
>  {
> -    QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj;
> +    StackObject *so = &qiv->stack[qiv->nb_stack - 1];
> +    QObject *qobj = so->obj;
>
>      if (qobj) {
>          if (name && qobject_type(qobj) == QTYPE_QDICT) {
> -            if (qiv->stack[qiv->nb_stack - 1].h && consume) {
> -                g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name);
> +            if (so->h && consume) {
> +                g_hash_table_remove(so->h, name);
> +            }
> +            qobj = qdict_get(qobject_to_qdict(qobj), name);
> +        } else if (so->entry) {
> +            qobj = qlist_entry_obj(so->entry);
> +            if (consume) {
> +                so->entry = qlist_next(so->entry);
>              }
> -            return qdict_get(qobject_to_qdict(qobj), name);
> -        } else if (qiv->stack[qiv->nb_stack - 1].entry) {
> -            return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
>          }
>      }
>

Much easier to read if split into two steps:

1. Capture the pointer to the top of the stack in a variable

@@ -44,16 +44,17 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
                                      const char *name,
                                      bool consume)
 {
-    QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj;
+    StackObject *so = &qiv->stack[qiv->nb_stack - 1];
+    QObject *qobj = so->obj;
 
     if (qobj) {
         if (name && qobject_type(qobj) == QTYPE_QDICT) {
-            if (qiv->stack[qiv->nb_stack - 1].h && consume) {
-                g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name);
+            if (so->h && consume) {
+                g_hash_table_remove(so->h, name);
             }
             return qdict_get(qobject_to_qdict(qobj), name);
-        } else if (qiv->stack[qiv->nb_stack - 1].entry) {
-            return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
+        } else if (so->entry) {
+            return qlist_entry_obj(so->entry);
         }
     }
 
2. Make the actual change

@@ -52,9 +52,12 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
             if (so->h && consume) {
                 g_hash_table_remove(so->h, name);
             }
-            return qdict_get(qobject_to_qdict(qobj), name);
+            qobj = qdict_get(qobject_to_qdict(qobj), name);
         } else if (so->entry) {
-            return qlist_entry_obj(so->entry);
+            qobj = qlist_entry_obj(so->entry);
+            if (consume) {
+                so->entry = qlist_next(so->entry);
+            }
         }
     }
 
     return qobj;
 
I'd call the new variable tos (top of stack) instead of so.  Needs a
matching rename in qmp_input_next_list().

Note that @consume is true unless we're called from
qmp_input_get_next_type().  What is it supposed to do?  Can it be true
in the place where you add a use?

Since we're cleaning up the function anyway in step 1, I'd be tempted to
reduce its nesting:

    if (!qobj) {
        return NULL;
    } else if (name && qobject_type(qobj) == QTYPE_QDICT) {
        if (tos->h && consume) {
            g_hash_table_remove(tos->h, name);
        }
        return qdict_get(qobject_to_qdict(qobj), name);
    } else if (tos->entry) {
        return qlist_entry_obj(tos->entry);
    } else {
        return qobj;
    }

Immediately begs the question what the four cases mean.  Unobvious
enough to justify comments, I think.

What's the stack's contents?  You cleaned that up and documented it in
qmp-output-visitor.c.  Same treatment here?

> @@ -66,7 +70,8 @@ static void qdict_add_key(const char *key, QObject *obj, void *opaque)
>      g_hash_table_insert(h, (gpointer) key, NULL);
>  }
>
> -static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
> +static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj,
> +                           const QListEntry *entry, Error **errp)
>  {
>      GHashTable *h;
>
       if (qiv->nb_stack >= QIV_STACK_SIZE) {
           error_setg(errp, "An internal buffer overran");
           return;
       }

Aside: this is stupid, realloc() exists.  It's less stupid than the
qmp-output-visitor.c's tail queue, though.

> @@ -76,7 +81,7 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
>      }
>
>      qiv->stack[qiv->nb_stack].obj = obj;
> -    qiv->stack[qiv->nb_stack].entry = NULL;
> +    qiv->stack[qiv->nb_stack].entry = entry;
>      qiv->stack[qiv->nb_stack].h = NULL;
>
>      if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
> @@ -138,7 +143,7 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
>          return;
>      }
>
> -    qmp_input_push(qiv, qobj, &err);
> +    qmp_input_push(qiv, qobj, NULL, &err);
>      if (err) {
>          error_propagate(errp, err);
>          return;

The stack entry for a struct being visited has obj = its source QDict,
entry = NULL.

> @@ -158,10 +163,12 @@ static void qmp_input_start_implicit_struct(Visitor *v, 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, 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) {
>          error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
> @@ -169,37 +176,28 @@ static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
>          return;
>      }
>
> -    qmp_input_push(qiv, qobj, errp);
> +    entry = qlist_first(qobject_to_qlist(qobj));
> +    qmp_input_push(qiv, qobj, entry, errp);

The stack entry for a list being visited has obj = its source QList,
entry = NULL.

> +    if (list) {
> +        if (entry) {
> +            *list = g_new0(GenericList, 1);
> +        } else {
> +            *list = NULL;
> +        }
> +    }
>  }
>
> -static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
> +static GenericList *qmp_input_next_list(Visitor *v, GenericList *list,
>                                          Error **errp)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
> -    GenericList *entry;
>      StackObject *so = &qiv->stack[qiv->nb_stack - 1];
> -    bool first;
>
> -    if (so->entry == NULL) {
> -        so->entry = qlist_first(qobject_to_qlist(so->obj));
> -        first = true;
> -    } else {
> -        so->entry = qlist_next(so->entry);
> -        first = false;
> -    }
> -
> -    if (so->entry == NULL) {
> +    if (!so->entry) {
>          return NULL;
>      }
> -
> -    entry = g_malloc0(sizeof(*entry));
> -    if (first) {
> -        *list = entry;
> -    } else {
> -        (*list)->next = entry;
> -    }
> -
> -    return entry;
> +    list->next = g_new0(GenericList, 1);
> +    return list->next;
>  }
>
>
> @@ -375,7 +373,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
>      v->visitor.optional = qmp_input_optional;
>      v->visitor.get_next_type = qmp_input_get_next_type;
>
> -    qmp_input_push(v, obj, NULL);
> +    qmp_input_push(v, obj, NULL, NULL);

The stack entry for the root value being visited has obj = its source
QObject, entry = NULL.

>      qobject_incref(obj);
>
>      return v;
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index 5376948..913f378 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -21,7 +21,6 @@
>  typedef struct QStackEntry
>  {
>      QObject *value;
> -    bool is_list_head;
>      QTAILQ_ENTRY(QStackEntry) node;
>  } QStackEntry;
>
> @@ -51,9 +50,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);
>  }
>
> @@ -122,7 +118,8 @@ static void qmp_output_end_struct(Visitor *v)
>      qmp_output_pop(qov, 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, Error **errp)
>  {
>      QmpOutputVisitor *qov = to_qov(v);
>      QList *list = qlist_new();
> @@ -131,20 +128,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 *list,
>                                           Error **errp)
>  {
> -    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 list->next;
>  }
>
>  static void qmp_output_end_list(Visitor *v)

A simple one, for a change.

> diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
> index 610c233..582a62a 100644
> --- a/qapi/string-input-visitor.c
> +++ b/qapi/string-input-visitor.c
> @@ -23,8 +23,6 @@ struct StringInputVisitor
>  {
>      Visitor visitor;
>
> -    bool head;
> -
>      GList *ranges;
>      GList *cur_range;
>      int64_t cur;
> @@ -123,11 +121,19 @@ error:
>  }
>
>  static void
> -start_list(Visitor *v, const char *name, Error **errp)
> +start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
>  {
>      StringInputVisitor *siv = to_siv(v);
> +    Error *err = NULL;
>
> -    parse_str(siv, errp);
> +    /* We don't support visits without a GenericList, yet */
> +    assert(list);
> +
> +    parse_str(siv, &err);
> +    if (err) {
> +        error_propagate(errp, err);
> +        return;
> +    }

Should you set *@list = NULL on error?

Does this handle parse_str() error correctly before your patch?

>
>      siv->cur_range = g_list_first(siv->ranges);
>      if (siv->cur_range) {
> @@ -135,14 +141,16 @@ start_list(Visitor *v, const char *name, Error **errp)
>          if (r) {
>              siv->cur = r->begin;
>          }
> +        *list = g_new0(GenericList, 1);
> +    } else {
> +        *list = NULL;
>      }
>  }

If it does, then this must be the reason you have to bail out on error.

>
>  static GenericList *
> -next_list(Visitor *v, GenericList **list, Error **errp)
> +next_list(Visitor *v, GenericList *list, Error **errp)
>  {
>      StringInputVisitor *siv = to_siv(v);
> -    GenericList **link;
>      Range *r;
>
>      if (!siv->ranges || !siv->cur_range) {
> @@ -166,22 +174,13 @@ next_list(Visitor *v, GenericList **list, Error **errp)
>          siv->cur = r->begin;
>      }
>
> -    if (siv->head) {
> -        link = list;
> -        siv->head = false;
> -    } else {
> -        link = &(*list)->next;
> -    }
> -
> -    *link = g_malloc0(sizeof **link);
> -    return *link;
> +    list->next = g_new0(GenericList, 1);
> +    return list->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,
> @@ -361,6 +360,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 fd917a4..7209d80 100644
> --- a/qapi/string-output-visitor.c
> +++ b/qapi/string-output-visitor.c
> @@ -57,7 +57,6 @@ struct StringOutputVisitor
>      Visitor visitor;
>      bool human;
>      GString *string;
> -    bool head;
>      ListMode list_mode;
>      union {
>          int64_t s;
> @@ -265,40 +264,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, 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 */
> +    assert(list);
> +    /* List handling is only needed if there are at least two elements */
> +    if (*list && (*list)->next) {
> +        sov->list_mode = LM_STARTED;
> +    }

Contradicts the comment next to LM_STARTED: /* start_list() succeeded */

Why do you need to stay in state LM_NONE for shorter lists now?


>  }
>
>  static GenericList *
> -next_list(Visitor *v, GenericList **list, Error **errp)
> +next_list(Visitor *v, GenericList *list, Error **errp)
>  {
>      StringOutputVisitor *sov = to_sov(v);
> -    GenericList *ret = NULL;
> -    if (*list) {
> -        if (sov->head) {
> -            ret = *list;
> -        } else {
> -            ret = (*list)->next;
> -        }
> +    GenericList *ret = list->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;
>  }

What does state LM_END mean?  It has no comment...

>
> @@ -312,8 +300,6 @@ 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)

Unless I'm blind, no next_list() implementation sets an error.  Drop
parameter errp and simplify?

> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 8039b97..6016734 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -125,20 +125,23 @@ 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 *elt;

This isn't an element, it's a list node.  I'd call it tail.

Sure changing its type from GenericList to the specific one saves casts?

>
> -    visit_start_list(v, name, &err);
> +    visit_start_list(v, name, (GenericList **)obj, &err);
>      if (err) {
>          goto out;
>      }
> -
> -    for (prev = (GenericList **)obj;
> -         !err && (i = visit_next_list(v, prev, &err)) != NULL;
> -         prev = &i) {
> -        %(c_name)s *native_i = (%(c_name)s *)i;
> -        visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
> +    elt = *obj;
> +    while (elt) {
> +        visit_type_%(c_elt_type)s(v, NULL, &elt->value, &err);
> +        if (err) {
> +            break;
> +        }
> +        elt = (%(c_name)s *)visit_next_list(v, (GenericList *)elt, &err);
> +        if (err) {
> +            break;
> +        }
>      }

With a simplified next_list(), this could be a nice for-loop:

       for (tail = *obj; tail; tail = visit_next_list(v, tail, &err)) ...

> -
>      visit_end_list(v);
>  out:
>      error_propagate(errp, err);

  reply	other threads:[~2016-01-28 13:37 UTC|newest]

Thread overview: 128+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling Eric Blake
2016-01-20  9:02   ` Markus Armbruster
2016-01-20 15:55     ` Eric Blake
2016-01-21  6:21       ` Markus Armbruster
2016-01-21 17:12         ` Eric Blake
2016-01-21 17:29         ` Daniel P. Berrange
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 02/37] qapi: Avoid use of misnamed DO_UPCAST() Eric Blake
2016-01-20 10:04   ` Markus Armbruster
2016-01-20 15:59     ` Eric Blake
2016-01-21  6:22       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 03/37] qapi: Drop dead dealloc visitor variable Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 04/37] hmp: Improve use of qapi visitor Eric Blake
2016-01-20 13:05   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 05/37] vl: " Eric Blake
2016-01-20 13:43   ` Markus Armbruster
2016-01-20 16:18     ` Eric Blake
2016-01-21  6:45       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 06/37] balloon: " Eric Blake
2016-01-20 14:09   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 07/37] qapi: Improve generated event " Eric Blake
2016-01-20 15:19   ` Markus Armbruster
2016-01-20 17:10     ` Eric Blake
2016-01-21  7:16       ` Markus Armbruster
2016-01-26 23:40       ` Eric Blake
2016-01-28 22:51     ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 08/37] qapi: Track all failures between visit_start/stop Eric Blake
2016-01-20 16:03   ` Markus Armbruster
2016-01-20 17:15     ` Eric Blake
2016-01-21  7:19       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 09/37] qapi: Prefer type_int64 over type_int in visitors Eric Blake
2016-01-20 17:07   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 10/37] qapi: Make all visitors supply uint64 callbacks Eric Blake
2016-01-20 17:29   ` Markus Armbruster
2016-01-20 18:10     ` Eric Blake
2016-01-21  8:56       ` Markus Armbruster
2016-01-21 17:22         ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 11/37] qapi: Consolidate visitor small integer callbacks Eric Blake
2016-01-20 17:34   ` Markus Armbruster
2016-01-20 18:17     ` Eric Blake
2016-01-21  9:05       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 12/37] qapi: Don't cast Enum* to int* Eric Blake
2016-01-20 18:08   ` Markus Armbruster
2016-01-20 19:58     ` Eric Blake
2016-01-21  9:08       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 13/37] qom: Use typedef for Visitor Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 14/37] qapi: Swap visit_* arguments for consistent 'name' placement Eric Blake
2016-01-20 18:28   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 15/37] qom: Swap 'name' next to visitor in ObjectPropertyAccessor Eric Blake
2016-01-20 18:49   ` Markus Armbruster
2016-01-20 20:54     ` Eric Blake
2016-01-21  9:18       ` Markus Armbruster
2016-01-21  9:46         ` Kevin Wolf
2016-01-21 10:04           ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 16/37] qapi: Swap 'name' in visit_* callbacks to match public API Eric Blake
2016-01-20 18:55   ` Markus Armbruster
2016-01-20 21:01     ` Eric Blake
2016-01-21  9:19       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 17/37] qapi: Drop unused 'kind' for struct/enum visit Eric Blake
2016-01-20 18:59   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 18/37] qapi: Drop unused error argument for list and implicit struct Eric Blake
2016-01-20 19:03   ` Markus Armbruster
2016-01-20 21:58     ` Eric Blake
2016-01-21  9:47       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 19/37] qmp: Fix reference-counting of qnull on empty output visit Eric Blake
2016-01-21 10:27   ` Markus Armbruster
2016-01-21 13:19     ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 20/37] qmp: Don't abuse stack to track qmp-output root Eric Blake
2016-01-21 13:58   ` Markus Armbruster
2016-01-29  3:06     ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 21/37] qapi: Document visitor interfaces, add assertions Eric Blake
2016-01-21 20:08   ` Markus Armbruster
2016-01-22  0:30     ` Eric Blake
2016-01-22 12:18       ` Markus Armbruster
2016-02-10  0:23         ` Eric Blake
2016-02-10  7:38           ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 22/37] qapi: Add visit_type_null() visitor Eric Blake
2016-01-22 17:00   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 23/37] qmp: Support explicit null during input visit Eric Blake
2016-01-22 17:12   ` Markus Armbruster
2016-02-01 23:52     ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 24/37] qmp: Tighten output visitor rules Eric Blake
2016-01-22 19:11   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 25/37] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
2016-01-22 19:15   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 26/37] qapi: Simplify excess input reporting in input visitors Eric Blake
2016-01-22 19:24   ` Markus Armbruster
2016-01-22 19:37     ` Eric Blake
2016-01-25  9:27       ` Markus Armbruster
2016-01-25 10:43         ` Laszlo Ersek
2016-01-27 13:54   ` [Qemu-devel] [PATCH 0/3] qapi-visit: Unify struct and union visit Markus Armbruster
2016-01-27 13:54     ` [Qemu-devel] [PATCH 1/3] qapi-visit: Simplify how we visit common union members Markus Armbruster
2016-01-27 21:48       ` Eric Blake
2016-01-27 13:54     ` [Qemu-devel] [PATCH 2/3] qapi-visit: Clean up code generated around visit_end_union() Markus Armbruster
2016-01-27 14:02       ` Eric Blake
2016-01-27 14:46         ` Markus Armbruster
2016-01-27 13:54     ` [Qemu-devel] [PATCH 3/3] qapi-visit: Unify struct and union visit Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 27/37] qapi: Add type.is_empty() helper Eric Blake
2016-01-25 14:15   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 28/37] qapi: Fix command with named empty argument type Eric Blake
2016-01-25 15:03   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 29/37] qapi: Eliminate empty visit_type_FOO_fields Eric Blake
2016-01-25 17:04   ` Markus Armbruster
2016-02-17  4:57     ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 30/37] qapi: Canonicalize missing object to :empty Eric Blake
2016-01-25 19:04   ` Markus Armbruster
2016-01-26 16:29     ` Markus Armbruster
2016-01-27  8:00       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 31/37] qapi-visit: Unify struct and union visit Eric Blake
2016-01-27 14:12   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 32/37] qapi: Rework deallocation of partial struct Eric Blake
2016-01-27 16:41   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 33/37] qapi: Split visit_end_struct() into pieces Eric Blake
2016-01-27 17:20   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 34/37] qapi: Simplify semantics of visit_next_list() Eric Blake
2016-01-28 13:37   ` Markus Armbruster [this message]
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 35/37] qapi: Change visit_type_FOO() to no longer return partial objects Eric Blake
2016-01-28 15:24   ` Markus Armbruster
2016-01-28 17:05     ` Eric Blake
2016-01-29 12:03       ` Markus Armbruster
2016-01-29 15:13         ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 36/37] RFC: qapi: Adjust layout of FooList types Eric Blake
2016-01-28 15:34   ` Markus Armbruster
2016-01-28 17:23     ` Eric Blake
2016-01-29  8:19       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 37/37] qapi: Update docs to match recent generator changes Eric Blake
2016-01-28 15:37   ` Markus Armbruster
2016-01-28 17:56 ` [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87si1hakpm.fsf@blackfin.pond.sub.org \
    --to=armbru@redhat.com \
    --cc=agraf@suse.de \
    --cc=david@gibson.dropbear.id.au \
    --cc=eblake@redhat.com \
    --cc=marcandre.lureau@redhat.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-ppc@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.