On 6/9/23 04:17, Eric Blake wrote:
> Splitting the parse of a 20-byte structured reply header across two
> source files (16 bytes overlapping with simple replies in
> states-reply.c, the remaining 4 bytes in states-reply-structured.c) is
> confusing.  The upcoming addition of extended headers will reuse the
> payload parsing portion of structured replies, but will parse its
> 32-byte header completely in states-reply.c.  So it is better to have
> a single file in charge of parsing all three header sizes (once the
> third type is introduced), where we then farm out to the payload
> parsers.

"Farm out" is a transitive phrasal verb, but we don't have an object for
it. "Fan out" perhaps? (Or add an object.)

> 
> To do that, rename REPLY.CHECK_SIMPLE_OR_STRUCTURED_REPLY to
> REPLY.CHECK_REPLY_MAGIC and have it pick up the setup from
> REPLY.STRUCTURED_REPLY.START, rename REPLY

The last REPLY is superfluous

> REPLY.STRUCTURED_REPLY.RECV_REMAINING to
> REPLY.RECV_STRUCTURED_REMAINING across files, and merge
> REPLY.STRUCTURED_REPLY.CHECK into what would otherwise be the
> now-empty REPLY.STRUCTURED_REPLY.START.

A different way to describe the same restructuring (I needed this
wording for explaining the code movement to myself):

- We have (prepare, receive, parse) triplets.

- Before the patch, our starter triplet is (REPLY.START,
REPLY.RECV_REPLY, REPLY.CHECK_SIMPLE_OR_STRUCTURED_REPLY). These are all
in "states-reply.c". In the last component of that triplet (the "parse"
component), we branch either to SIMPLE_REPLY.START
(states-reply-simple.c) for actual parsing, or we start another triplet,
for the remaining bytes of a structured reply:
(REPLY.STRUCTURED_REPLY.START, REPLY.STRUCTURED_REPLY.RECV_REMAINING,
REPLY.STRUCTURED_REPLY.CHECK); these are all in

- The patch effectively redistributes the second triplet. The prepare
stage REPLY.STRUCTURED_REPLY.START is merged into the last (parse) stage
of the first triplet, i.e., into REPLY.CHECK_SIMPLE_OR_STRUCTURED_REPLY
(and it is renamed to REPLY.CHECK_REPLY_MAGIC). The receive stage of the
second triplet is moved to "states-reply.c", and renamed to
REPLY.RECV_STRUCTURED_REMAINING. The last (parse) stage of the second
triplet is kept in "states-reply-structured.c", but renamed to
REPLY.STRUCTURED_REPLY.START.

- Thus "states-reply-structured.c" loses two states (prepare and
receive); the first is absorbed by "states-reply.c" into an existing
state, and the second is gained by "states-reply.c" as a new state.

I agree this is an improvement, because now we finish reading all reply
headers in a single C file.

> 
> Signed-off-by: Eric Blake <ebl...@redhat.com>
> ---
>  generator/state_machine.ml          | 29 ++++++++-----------
>  generator/states-reply.c            | 43 ++++++++++++++++++++++-------
>  generator/states-reply-structured.c | 26 -----------------
>  3 files changed, 44 insertions(+), 54 deletions(-)
> 
> diff --git a/generator/state_machine.ml b/generator/state_machine.ml
> index 126159b9..b5485aec 100644
> --- a/generator/state_machine.ml
> +++ b/generator/state_machine.ml
> @@ -769,8 +769,15 @@ and
> 
>    State {
>      default_state with
> -    name = "CHECK_SIMPLE_OR_STRUCTURED_REPLY";
> -    comment = "Check if the reply is a simple or structured reply";
> +    name = "CHECK_REPLY_MAGIC";
> +    comment = "Check if the reply has expected magic";
> +    external_events = [];
> +  };

So this remains in "states-reply.c" and absorbs the original prep logic
of REPLY.STRUCTURED_REPLY.START, from "states-reply-structured.c".

> +
> +  State {
> +    default_state with
> +    name = "RECV_STRUCTURED_REMAINING";
> +    comment = "Receiving the remaining part of a structured reply header";
>      external_events = [];
>    };
> 

This is effectively moved from "states-reply-structured.c" (originally
REPLY.STRUCTURED_REPLY.RECV_REMAINING) to "states-reply.c".

> @@ -804,28 +811,14 @@ and
>    };
>  ]
> 
> -(* Receiving a structured reply from the server.
> +(* Receiving a structured reply payload from the server.
>   * Implementation: generator/states-reply-structured.c
>   *)
>  and structured_reply_state_machine = [
>    State {
>      default_state with
>      name = "START";
> -    comment = "Prepare to receive the remaining part of a structured reply";
> -    external_events = [];
> -  };
> -

Yes, moved & merged into CHECK_REPLY_MAGIC.

> -  State {
> -    default_state with
> -    name = "RECV_REMAINING";
> -    comment = "Receiving the remaining part of a structured reply";
> -    external_events = [];
> -  };

Yes, moved and renamed to RECV_STRUCTURED_REMAINING.

> -
> -  State {
> -    default_state with
> -    name = "CHECK";
> -    comment = "Parse a structured reply from the server";
> +    comment = "Start parsing a structured reply payload from the server";
>      external_events = [];
>    };
> 

Stays in "states-reply-structured.c", renamed from CHECK to START.

> diff --git a/generator/states-reply.c b/generator/states-reply.c
> index 2c77658b..87f17bae 100644
> --- a/generator/states-reply.c
> +++ b/generator/states-reply.c
> @@ -64,11 +64,11 @@  REPLY.START:
>    ssize_t r;
> 
>    /* We read all replies initially as if they are simple replies, but
> -   * check the magic in CHECK_SIMPLE_OR_STRUCTURED_REPLY below.  This
> -   * works because the structured_reply header is larger, and because
> -   * the last member of a simple reply, cookie, is coincident between
> -   * the two structs (an intentional design decision in the NBD spec
> -   * when structured replies were added).
> +   * check the magic in CHECK_REPLY_MAGIC below.  This works because
> +   * the structured_reply header is larger, and because the last
> +   * member of a simple reply, cookie, is coincident between the two
> +   * structs (an intentional design decision in the NBD spec when
> +   * structured replies were added).
>     */
>    STATIC_ASSERT (offsetof (struct nbd_handle, sbuf.simple_reply.cookie) ==
>                   offsetof (struct nbd_handle, 
> sbuf.sr.structured_reply.cookie),

OK, only the state name is updated, and the comment is reflowed.

> @@ -113,11 +113,11 @@  REPLY.RECV_REPLY:
>    switch (recv_into_rbuf (h)) {
>    case -1: SET_NEXT_STATE (%.DEAD); return 0;
>    case 1: SET_NEXT_STATE (%.READY); return 0;
> -  case 0: SET_NEXT_STATE (%CHECK_SIMPLE_OR_STRUCTURED_REPLY);
> +  case 0: SET_NEXT_STATE (%CHECK_REPLY_MAGIC);
>    }
>    return 0;

OK, state rename.

> 
> - REPLY.CHECK_SIMPLE_OR_STRUCTURED_REPLY:
> + REPLY.CHECK_REPLY_MAGIC:
>    struct command *cmd;
>    uint32_t magic;
>    uint64_t cookie;

Ditto.

> @@ -127,7 +127,18 @@  REPLY.CHECK_SIMPLE_OR_STRUCTURED_REPLY:
>      SET_NEXT_STATE (%SIMPLE_REPLY.START);
>    }
>    else if (magic == NBD_STRUCTURED_REPLY_MAGIC) {
> -    SET_NEXT_STATE (%STRUCTURED_REPLY.START);
> +    /* We've only read the bytes that fill simple_reply.  The
> +     * structured_reply is longer, so prepare to read the remaining
> +     * bytes.  We depend on the memory aliasing in union sbuf to
> +     * overlay the two reply types.
> +     */
> +    STATIC_ASSERT (sizeof h->sbuf.simple_reply ==
> +                   offsetof (struct nbd_structured_reply, length),
> +                   simple_structured_overlap);
> +    assert (h->rbuf == (char *)&h->sbuf + sizeof h->sbuf.simple_reply);
> +    h->rlen = sizeof h->sbuf.sr.structured_reply;
> +    h->rlen -= sizeof h->sbuf.simple_reply;

This is being absorbed from REPLY.STRUCTURED_REPLY.START. The comment's
wording is slightly updated. OK.

The code is identical.

> +    SET_NEXT_STATE (%RECV_STRUCTURED_REMAINING);

Having dealt with the "prepare" stage right here, we can now read the
remaining bytes; that gets renamed from
REPLY.STRUCTURED_REPLY.RECV_REMAINING, and moved to this file.

>    }
>    else {
>      /* We've probably lost synchronization. */
> @@ -141,8 +152,9 @@  REPLY.CHECK_SIMPLE_OR_STRUCTURED_REPLY:
>      return 0;
>    }
> 
> -  /* NB: This works for both simple and structured replies because the
> -   * handle (our cookie) is stored at the same offset.  See the
> +  /* NB: This works for both simple and structured replies, even
> +   * though we haven't finished reading the structured header yet,
> +   * because the cookie is stored at the same offset.  See the
>     * STATIC_ASSERT above in state REPLY.START that confirmed this.
>     */
>    h->chunks_received++;

Good!

> @@ -157,6 +169,17 @@  REPLY.CHECK_SIMPLE_OR_STRUCTURED_REPLY:
>    h->reply_cmd = cmd;
>    return 0;
> 
> + REPLY.RECV_STRUCTURED_REMAINING:
> +  switch (recv_into_rbuf (h)) {
> +  case -1: SET_NEXT_STATE (%.DEAD); return 0;
> +  case 1:
> +    save_reply_state (h);
> +    SET_NEXT_STATE (%.READY);
> +    return 0;
> +  case 0: SET_NEXT_STATE (%STRUCTURED_REPLY.START);
> +  }
> +  return 0;
> +
>   REPLY.FINISH_COMMAND:
>    struct command *prev_cmd, *cmd;
>    uint64_t cookie;

Moved nearly identically from REPLY.STRUCTURED_REPLY.RECV_REMAINING. The
state itself is renamed, and the state we jump to (for parsing) is also
renamed. Good.

"Nearly" identically: the code movement sneakily fixes a whitespace
error in the original! :) Double space after "case 0:".


> diff --git a/generator/states-reply-structured.c 
> b/generator/states-reply-structured.c
> index 205a236d..3a7a03fd 100644
> --- a/generator/states-reply-structured.c
> +++ b/generator/states-reply-structured.c
> @@ -46,32 +46,6 @@ structured_reply_in_bounds (uint64_t offset, uint32_t 
> length,
> 
>  STATE_MACHINE {
>   REPLY.STRUCTURED_REPLY.START:
> -  /* We've only read the bytes that fill simple_reply.  The
> -   * structured_reply is longer, so read the remaining part.  We
> -   * depend on the memory aliasing in union sbuf to overlay the two
> -   * reply types.
> -   */
> -  STATIC_ASSERT (sizeof h->sbuf.simple_reply ==
> -                 offsetof (struct nbd_structured_reply, length),
> -                 simple_structured_overlap);
> -  assert (h->rbuf == (char *)&h->sbuf + sizeof h->sbuf.simple_reply);
> -  h->rlen = sizeof h->sbuf.sr.structured_reply;
> -  h->rlen -= sizeof h->sbuf.simple_reply;
> -  SET_NEXT_STATE (%RECV_REMAINING);
> -  return 0;
> -
> - REPLY.STRUCTURED_REPLY.RECV_REMAINING:
> -  switch (recv_into_rbuf (h)) {
> -  case -1: SET_NEXT_STATE (%.DEAD); return 0;
> -  case 1:
> -    save_reply_state (h);
> -    SET_NEXT_STATE (%.READY);
> -    return 0;
> -  case 0:  SET_NEXT_STATE (%CHECK);
> -  }
> -  return 0;
> -
> - REPLY.STRUCTURED_REPLY.CHECK:
>    struct command *cmd = h->reply_cmd;
>    uint16_t flags, type;
>    uint32_t length;

Yes.

Hellishly difficult to review, but correct, as far as I can tell, and
certainly an improvement for the codebase.

Reviewed-by: Laszlo Ersek <ler...@redhat.com>

I'll continue with the rest of the series sometime later.

Thanks,
Laszlo
_______________________________________________
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs

Reply via email to