All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: David Hildenbrand <david@redhat.com>
Cc: Eduardo Habkost <ehabkost@redhat.com>,
	"Michael S . Tsirkin" <mst@redhat.com>,
	Michael Roth <mdroth@linux.vnet.ibm.com>,
	qemu-devel@nongnu.org, Igor Mammedov <imammedo@redhat.com>,
	"Dr . David Alan Gilbert" <dgilbert@redhat.com>,
	David Gibson <david@gibson.dropbear.id.au>
Subject: Re: [Qemu-devel] [PATCH v3 1/7] qapi: use qemu_strtoi64() in parse_str
Date: Thu, 08 Nov 2018 10:13:30 +0100	[thread overview]
Message-ID: <878t24ort1.fsf@dusky.pond.sub.org> (raw)
In-Reply-To: <bfebc5b0-cebd-8a83-3106-effba5332094@redhat.com> (David Hildenbrand's message of "Thu, 8 Nov 2018 09:54:08 +0100")

David Hildenbrand <david@redhat.com> writes:

>>> Would it be valid to do something like this (skipping elements without a
>>> proper visit_type_int)
>>>
>>> visit_start_list();
>>> visit_next_list();  more input, returns "there's more"
>>> visit_next_list();  parses "1-3,", buffers 2-3, skips over 1
>>> visit_type_int();   returns 2
>>> ...
>> 
>> Excellent question!
>> 
>> Here's the relevant part of visit_start_list()'s contract in visitor.h:
>> 
>>  * After visit_start_list() succeeds, the caller may visit its members
>>  * 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 with the
>>  * same @list to clean up, even if intermediate visits fail.  See the
>>  * examples above.
>> 
>> So, you *may* visit members, and you *must* call visit_end_list().
>> 
>> But what are "real" and "virtual" visits?  Again, the contract:
>> 
>>  * @list must be non-NULL for a real walk, in which case @size
>>  * determines how much memory an input or clone visitor will allocate
>>  * into *@list (at least sizeof(GenericList)).  Some visitors also
>>  * allow @list to be NULL for a virtual walk, in which case @size is
>>  * ignored.
>> 
>> I'm not sure whether I just decreased or increased global confusion ;)
>
> I was reading over these comments and got slightly confused :)
>
>> 
>> The file comment explains:
>> 
>>  * The QAPI schema defines both a set of C data types, and a QMP wire
>>  * format.  QAPI objects can contain references to other QAPI objects,
>>  * resulting in a directed acyclic graph.  QAPI also generates visitor
>>  * functions to walk these graphs.  This file represents the interface
>>  * for doing work at each node of a QAPI graph; it can also be used
>>  * for a virtual walk, where there is no actual QAPI C struct.
>> 
>> A real walk with an output visitor works like this (error handling
>> omitted for now):
>> 
>>     Error *err = NULL;
>>     intList *tail;
>>     size_t size = sizeof(**obj);
>> 
>>     // fetch list's head into *obj, start the list in output
>>     visit_start_list(v, name, (GenericList **)obj, size, &err);
>> 
>>     // iterate over list's tails
>>     // below the hood, visit_next_list() iterates over the GenericList
>>     for (tail = *obj; tail;
>>          tail = (intList *)visit_next_list(v, (GenericList *)tail, size)) {
>>         // visit current tail's first member, add it to output
>>         visit_type_int(v, NULL, &tail->value, &err);
>>     }
>> 
>>     // end the list in output
>>     visit_end_list(v, (void **)obj);
>> 
>> The exact same code works for a real walk with an input visitor, where
>> visit_next_list() iterates over the *input* and builds up the
>> GenericList.  Compare qobject_input_next_list() and
>> qobject_output_next_list().
>> 
>> The code above is a straight copy of generated visit_type_intList() with
>> error handling cut and comments added.
>> 
>> A real walk works on a QAPI-generated C type.  QAPI generates a real
>> walk for each such type.  Open-coding a real walk would senselessly
>> duplicate the generated one, so we don't.  Thus, a real walk always
>> visits each member.
>> 
>> Okay, I lied: it visits each member until it runs into an error and
>> bails out.  See generated visit_type_intList() in
>> qapi/qapi-builtin-visit.c.
>> 
>> A virtual walk doesn't work with a GenericList *.  Calling
>> visit_next_list() makes no sense then.  visitor.h gives this example of
>> a virtual walk:
>
> Alright, so we must not support virtual walks. But supporting it would
> not harm :)

Hmm, let me check something... aha!  Both string-input-visitor.h and
string-output-visitor.h have this comment:

 * The string input visitor does not implement support for visiting
 * QAPI structs, alternates, null, or arbitrary QTypes.  It also
 * requires a non-null list argument to visit_start_list().

The last sentence means virtual walks are not supported.  We owe thanks
to Eric Blake for his commit d9f62dde130 :)

>> 
>>  * Thus, a virtual walk corresponding to '{ "list": [1, 2] }' looks
>>  * like:
>>  *
>>  * <example>
>>  *  Visitor *v;
>>  *  Error *err = NULL;
>>  *  int value;
>>  *
>>  *  v = FOO_visitor_new(...);
>>  *  visit_start_struct(v, NULL, NULL, 0, &err);
>>  *  if (err) {
>>  *      goto out;
>>  *  }
>>  *  visit_start_list(v, "list", NULL, 0, &err);
>>  *  if (err) {
>>  *      goto outobj;
>>  *  }
>>  *  value = 1;
>>  *  visit_type_int(v, NULL, &value, &err);
>>  *  if (err) {
>>  *      goto outlist;
>>  *  }
>>  *  value = 2;
>>  *  visit_type_int(v, NULL, &value, &err);
>>  *  if (err) {
>>  *      goto outlist;
>>  *  }
>>  * outlist:
>>  *  visit_end_list(v, NULL);
>>  *  if (!err) {
>>  *      visit_check_struct(v, &err);
>>  *  }
>>  * outobj:
>>  *  visit_end_struct(v, NULL);
>>  * out:
>>  *  error_propagate(errp, err);
>>  *  visit_free(v);
>> 
>> Why could this be useful?
>> 
>> 1. With the QObject input visitor, it's an alternative way to
>>    destructure a QObject into a bunch of C variables.  The "obvious" way
>>    would be calling the QObject accessors.  By using the visitors you
>>    get much the error checking code for free.  YMMV.
>> 
>> 2. With the QObject output visitor, it's an alternative way to build up
>>    a QObject.  The "obvious" way would be calling the constructors
>>    directly.
>> 
>> 3. With the string input / output visitors, it's a way to parse / format
>>    string visitor syntax without having to construct the C type first.
>> 
>> Right now, we have no virtual list walks outside tests.  We do have
>> virtual struct walks outside tests.
>> 
>>> Or mixing types
>>>
>>> visit_start_list();
>>> visit_next_list();
>>> visit_type_int64();
>>> visit_next_list();
>>> visit_type_uint64();
>> 
>> Another excellent question.
>> 
>> QAPI can only express homogeneous lists, i.e. lists of some type T.
>> 
>> The generated visit_type_TList call the same visit_type_T() for all list
>> members.
>
> Okay, my point would be: Somebody coding its own visit code should not
> assume this to work.
>
>> 
>> QAPI type 'any' is the top type, but visit_type_anyList() still calls
>> visit_type_any() for all list members.
>> 
>> Virtual walks could of course do anything.  Since they don't exist
>> outside tests, we can outlaw doing things that cause us trouble.
>> 
>> The virtual walks we currently have in tests/ seem to walk only
>> homogeneous lists, i.e. always call the same visit_type_T().
>
> Okay, so bailing out if types are switched (e.g. just about to report a
> range of uin64_t and somebody asks for a int64_t) is valid.

I think that would be okay.

>> 
>>> IOW, can I assume that after every visit_next_list(), visit_type_X is
>>> called, and that X remains the same for one pass over the list?
>> 
>> As far as I can tell, existing code is just fine with that assumption.
>> We'd have to write it into the contract, though.
>> 
>>> Also, I assume it is supposed to work like this
>>>
>>> visit_start_list();
>>> visit_next_list();  more input, returns "there's more"
>>> visit_type_int();   returns 1 (parses 1-3, buffers 1-3)
>>> visit_type_int();   returns 1
>>> visit_type_int();   returns 1
>>> visit_next_list();  more input, unbuffers 1
>>> visit_type_int();   returns 2
>>>
>>> So unbuffering actually happens on visit_next_list()?
>> 
>> I believe this violates the contract.
>> 
>> If it's a real walk, the contract wants you to call visit_next_list()
>> between subsequent visit_type_int().
>> 
>> If it's a virtual walk, calling visit_next_list() makes no sense.
>> 
>> More questions?
>> 
>
> Thanks for the excessive answer! I think that should be enough to come
> up with a RFC of a *rewrite* of the string input visitor :)

You're welcome!  I love great questions, they make me *think*.

Besides, if something's worth doing, it's probably worth overdoing ;)

  reply	other threads:[~2018-11-08  9:13 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-10-23 15:22 [Qemu-devel] [PATCH v3 0/7] qapi/range/memory-device: fixes and cleanups David Hildenbrand
2018-10-23 15:23 ` [Qemu-devel] [PATCH v3 1/7] qapi: use qemu_strtoi64() in parse_str David Hildenbrand
2018-10-25 14:37   ` David Gibson
2018-10-31 14:40   ` Markus Armbruster
2018-10-31 16:47     ` David Hildenbrand
2018-10-31 17:55       ` Markus Armbruster
2018-10-31 18:01         ` David Hildenbrand
2018-11-05 15:37           ` Markus Armbruster
2018-11-05 15:53             ` David Hildenbrand
2018-11-05 16:48               ` Eric Blake
2018-11-06  9:20                 ` David Hildenbrand
2018-11-05 20:43               ` Markus Armbruster
2018-11-06  9:19                 ` David Hildenbrand
2018-11-07 15:29                   ` Markus Armbruster
2018-11-07 20:02                     ` David Hildenbrand
2018-11-08  8:39                       ` Markus Armbruster
2018-11-08  8:54                         ` David Hildenbrand
2018-11-08  9:13                           ` Markus Armbruster [this message]
2018-11-08 13:05                             ` David Hildenbrand
2018-11-08 14:36                               ` Markus Armbruster
2018-11-08 14:46                                 ` David Hildenbrand
2018-11-08 14:42                               ` Eric Blake
2018-11-08 14:49                                 ` David Hildenbrand
2018-10-23 15:23 ` [Qemu-devel] [PATCH v3 2/7] qapi: correctly parse uint64_t values from strings David Hildenbrand
2018-10-25 14:41   ` David Gibson
2018-10-26 12:48     ` David Hildenbrand
2018-10-31 14:32   ` Markus Armbruster
2018-10-31 14:44     ` Markus Armbruster
2018-10-31 17:19       ` David Hildenbrand
2018-10-31 17:18     ` David Hildenbrand
2018-11-04  3:27       ` David Gibson
2018-10-23 15:23 ` [Qemu-devel] [PATCH v3 3/7] range: pass const pointer where possible David Hildenbrand
2018-10-25 14:41   ` David Gibson
2018-10-23 15:23 ` [Qemu-devel] [PATCH v3 4/7] range: add some more functions David Hildenbrand
2018-11-01 10:00   ` Igor Mammedov
2018-11-01 10:29     ` David Hildenbrand
2018-11-01 11:05       ` Igor Mammedov
2018-11-05 10:30         ` David Hildenbrand
2018-10-23 15:23 ` [Qemu-devel] [PATCH v3 5/7] memory-device: use QEMU_IS_ALIGNED David Hildenbrand
2018-10-25 14:44   ` David Gibson
2018-10-23 15:23 ` [Qemu-devel] [PATCH v3 6/7] memory-device: avoid overflows on very huge devices David Hildenbrand
2018-10-25 14:44   ` David Gibson
2018-10-25 14:45   ` Igor Mammedov
2018-10-23 15:23 ` [Qemu-devel] [PATCH v3 7/7] memory-device: rewrite address assignment using ranges David Hildenbrand
2018-11-12 14:07   ` David Hildenbrand
2018-11-13 12:26   ` Igor Mammedov
2018-11-13 12:41     ` David Hildenbrand
2018-11-13 13:01   ` [Qemu-devel] [PATCH v4] " David Hildenbrand
2018-10-31 10:14 ` [Qemu-devel] [PATCH v3 0/7] qapi/range/memory-device: fixes and cleanups David Hildenbrand
2018-10-31 19:43   ` Eduardo Habkost

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=878t24ort1.fsf@dusky.pond.sub.org \
    --to=armbru@redhat.com \
    --cc=david@gibson.dropbear.id.au \
    --cc=david@redhat.com \
    --cc=dgilbert@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=imammedo@redhat.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=mst@redhat.com \
    --cc=qemu-devel@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.