diff --git a/include/proto/buffers.h b/include/proto/buffers.h index 870dadb61..dca3c889a 100644 --- a/include/proto/buffers.h +++ b/include/proto/buffers.h @@ -289,6 +289,15 @@ static inline int buffer_realign(struct buffer *buf) return buffer_max(buf); } +/* + * Return the maximum amount of bytes that can be written into the buffer in + * one call to buffer_feed*(). + */ +static inline int buffer_free_space(struct buffer *buf) +{ + return buffer_max_len(buf) - buf->l; +} + /* * Return the max amount of bytes that can be stuffed into the buffer at once. * Note that this may be lower than the actual buffer size when the free space @@ -314,10 +323,33 @@ static inline int buffer_contig_space(struct buffer *buf) return ret; } +/* + * Same as above but the caller may pass the value of buffer_max_len(buf) if it + * knows it, thus avoiding some calculations. + */ +static inline int buffer_contig_space_with_len(struct buffer *buf, int maxlen) +{ + int ret; + + if (buf->l == 0) { + buf->r = buf->w = buf->lr = buf->data; + ret = maxlen; + } + else if (buf->r > buf->w) { + ret = buf->data + maxlen - buf->r; + } + else { + ret = buf->w - buf->r; + if (ret > maxlen) + ret = maxlen; + } + return ret; +} + /* Return 1 if the buffer has less than 1/4 of its capacity free, otherwise 0 */ static inline int buffer_almost_full(struct buffer *buf) { - if (buffer_contig_space(buf) < buf->size / 4) + if (buffer_free_space(buf) < buf->size / 4) return 1; return 0; } diff --git a/src/buffers.c b/src/buffers.c index 2557fe4a0..c128bb9a3 100644 --- a/src/buffers.c +++ b/src/buffers.c @@ -86,19 +86,24 @@ int buffer_feed2(struct buffer *buf, const char *str, int len) if (len == 0) return -1; - if (len > buffer_max_len(buf)) { - /* we can't write this chunk and will never be able to, because - * it is larger than the buffer's current max size. + max = buffer_max_len(buf); + if (len > max - buf->l) { + /* we can't write this chunk right now because the buffer is + * almost full or because the block is too large. Return the + * available space or -2 if impossible. */ - return -2; + if (len > max) + return -2; + + return max - buf->l; } - max = buffer_contig_space(buf); - + /* OK so the data fits in the buffer in one or two blocks */ + max = buffer_contig_space_with_len(buf, max); + memcpy(buf->r, str, MIN(len, max)); if (len > max) - return max; + memcpy(buf->data, str + max, len - max); - memcpy(buf->r, str, len); buf->l += len; buf->r += len; buf->total += len; @@ -113,8 +118,8 @@ int buffer_feed2(struct buffer *buf, const char *str, int len) buf->flags &= ~BF_OUT_EMPTY; } - if (buf->r == buf->data + buf->size) - buf->r = buf->data; + if (buf->r >= buf->data + buf->size) + buf->r -= buf->size; buf->flags &= ~BF_FULL; if (buf->l >= buffer_max_len(buf))