Hi,

Brandon Williams wrote:

> The current pkt-line API encodes the status of a pkt-line read in the
> length of the read content.  An error is indicated with '-1', a flush
> with '0' (which can be confusing since a return value of '0' can also
> indicate an empty pkt-line), and a positive integer for the length of
> the read content otherwise.  This doesn't leave much room for allowing
> the addition of additional special packets in the future.
>
> To solve this introduce 'packet_read_with_status()' which reads a packet
> and returns the status of the read encoded as an 'enum packet_status'
> type.  This allows for easily identifying between special and normal
> packets as well as errors.  It also enables easily adding a new special
> packet in the future.

Makes sense, thanks.  Using an enum return value is less opaque, too.

[...]
> --- a/pkt-line.c
> +++ b/pkt-line.c
> @@ -280,28 +280,33 @@ static int packet_length(const char *linelen)
>       return (val < 0) ? val : (val << 8) | hex2chr(linelen + 2);
>  }
>  
> -int packet_read(int fd, char **src_buf, size_t *src_len,
> -             char *buffer, unsigned size, int options)
> +enum packet_read_status packet_read_with_status(int fd, char **src_buffer, 
> size_t *src_len,
> +                                             char *buffer, unsigned size, 
> int *pktlen,
> +                                             int options)

This function definition straddles two worlds: it is line-wrapped as
though there are a limited number of columns, but it goes far past 80
columns.

Can "make style" or a similar tool take care of rewrapping it?


>  {
> -     int len, ret;
> +     int len;
>       char linelen[4];
>  
> -     ret = get_packet_data(fd, src_buf, src_len, linelen, 4, options);
> -     if (ret < 0)
> -             return ret;
> +     if (get_packet_data(fd, src_buffer, src_len, linelen, 4, options) < 0)
> +             return PACKET_READ_EOF;
> +

EOF is indeed the only error that get_packet_data can return.

Could be worth a doc comment on get_packet_data to make that clearer.
It's not too important since it's static, though.

>       len = packet_length(linelen);
> -     if (len < 0)
> +
> +     if (len < 0) {
>               die("protocol error: bad line length character: %.4s", linelen);
> -     if (!len) {
> +     } else if (!len) {
>               packet_trace("0000", 4, 0);
> -             return 0;
> +             return PACKET_READ_FLUSH;

The advertised change. Makes sense.

[...]
> -     if (len >= size)
> +     if ((unsigned)len >= size)
>               die("protocol error: bad line length %d", len);

The comparison is safe since we just checked that len >= 0.

Is there some static analysis that can make this kind of operation
easier?

[...]
> @@ -309,7 +314,31 @@ int packet_read(int fd, char **src_buf, size_t *src_len,
>  
>       buffer[len] = 0;
>       packet_trace(buffer, len, 0);
> -     return len;
> +     *pktlen = len;
> +     return PACKET_READ_NORMAL;
> +}
> +
> +int packet_read(int fd, char **src_buffer, size_t *src_len,
> +             char *buffer, unsigned size, int options)
> +{
> +     enum packet_read_status status;
> +     int pktlen;
> +
> +     status = packet_read_with_status(fd, src_buffer, src_len,
> +                                      buffer, size, &pktlen,
> +                                      options);
> +     switch (status) {
> +     case PACKET_READ_EOF:
> +             pktlen = -1;
> +             break;
> +     case PACKET_READ_NORMAL:
> +             break;
> +     case PACKET_READ_FLUSH:
> +             pktlen = 0;
> +             break;
> +     }
> +
> +     return pktlen;

nit: can simplify by avoiding the status temporary:

        int pktlen;

        switch (packet_read_with_status(...)) {
        case PACKET_READ_EOF:
                return -1;
        case PACKET_READ_FLUSH:
                return 0;
        case PACKET_READ_NORMAL:
                return pktlen;
        }

As a bonus, that lets static analyzers check that the cases are
exhaustive.  (On the other hand, C doesn't guarantee that an enum can
only have the values listed as enumerators.  Did we end up figuring
out a way to handle that, beyond always including a 'default: BUG()'?)

> --- a/pkt-line.h
> +++ b/pkt-line.h
> @@ -65,6 +65,21 @@ int write_packetized_from_buf(const char *src_in, size_t 
> len, int fd_out);
>  int packet_read(int fd, char **src_buffer, size_t *src_len, char
>               *buffer, unsigned size, int options);
>  
> +/*
> + * Read a packetized line into a buffer like the 'packet_read()' function but
> + * returns an 'enum packet_read_status' which indicates the status of the 
> read.
> + * The number of bytes read will be assigined to *pktlen if the status of the
> + * read was 'PACKET_READ_NORMAL'.
> + */
> +enum packet_read_status {
> +     PACKET_READ_EOF = -1,
> +     PACKET_READ_NORMAL,
> +     PACKET_READ_FLUSH,
> +};

nit: do any callers treat the return value as a number?  It would be
less magical if the numbering were left to the compiler (0, 1, 2).

Thanks,
Jonathan

Reply via email to