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