MINOR: ncbuf: optimize storage for the last gap
Relax the constraint for gap storage when this is the last block.
ncb_blk API functions will consider that if a gap is stored near the end
of the buffer, without the space to store its header, the gap will cover
entirely the buffer end.
For these special cases, the gap size/data size are not write/read
inside the gap to prevent an overflow. Such a gap is designed in
functions as "reduced gap" and will be flagged with the value
NCB_BK_F_FIN.
This should reduce the rejection on future add operation when receiving
data in-order. Without reduced gap handling, an insertion would be
rejected if it covers only partially the last buffer bytes, which can be
a very common case.
diff --git a/include/haproxy/ncbuf-t.h b/include/haproxy/ncbuf-t.h
index d2b1c10..538f33e 100644
--- a/include/haproxy/ncbuf-t.h
+++ b/include/haproxy/ncbuf-t.h
@@ -54,6 +54,12 @@
* | -------- GAP-SZ -------------- > | --- DATA-SZ --->
*
* This means that a gap must be at least big enough to store two sizes.
+ * However, there is an optimization when the last block of the buffer is a
+ * gap. In this case, there is no minimal size for this block. If the gap is
+ * too small, the two sizes won't be stored in it. This block is considered
+ * to be a reduced gap. The block API will detect such a gap if stored at an
+ * offset near the end of the buffer.
+ *
*/
#include <stdint.h>
diff --git a/src/ncbuf.c b/src/ncbuf.c
index 445c144..f50e725 100644
--- a/src/ncbuf.c
+++ b/src/ncbuf.c
@@ -18,13 +18,14 @@
#define NCB_BLK_NULL ((struct ncb_blk){ .st = NULL })
#define NCB_BK_F_GAP 0x01 /* block represents a gap */
+#define NCB_BK_F_FIN 0x02 /* special reduced gap present at the end of the buffer */
struct ncb_blk {
char *st; /* first byte of the block */
char *end; /* first byte after this block */
- char *sz_ptr; /* pointer to size element */
+ char *sz_ptr; /* pointer to size element - NULL for reduced gap */
ncb_sz_t sz; /* size of the block */
- ncb_sz_t sz_data; /* size of the data following the block - only valid for GAP */
+ ncb_sz_t sz_data; /* size of the data following the block - invalid for reduced GAP */
ncb_sz_t off; /* offset of block in buffer */
char flag;
@@ -80,6 +81,12 @@
return off;
}
+/* Returns true if a gap cannot be inserted at <off> : a reduced gap must be used. */
+static int ncb_off_reduced(const struct ncbuf *b, ncb_sz_t off)
+{
+ return off + NCB_GAP_MIN_SZ > ncb_size(b);
+}
+
/* Returns true if <blk> is the special NULL block. */
static int ncb_blk_is_null(const struct ncb_blk blk)
{
@@ -127,10 +134,21 @@
blk.flag = ~prev.flag & NCB_BK_F_GAP;
if (blk.flag & NCB_BK_F_GAP) {
- blk.sz_ptr = ncb_peek(buf, blk.off + NCB_GAP_SZ_OFF);
- blk.sz = ncb_read_off(buf, blk.sz_ptr);
- BUG_ON_HOT(blk.sz < NCB_GAP_MIN_SZ);
- blk.sz_data = ncb_read_off(buf, ncb_peek(buf, blk.off + NCB_GAP_SZ_DATA_OFF));
+ if (ncb_off_reduced(buf, blk.off)) {
+ blk.flag |= NCB_BK_F_FIN;
+ blk.sz_ptr = NULL;
+ blk.sz = ncb_size(buf) - blk.off;
+ blk.sz_data = 0;
+
+ /* A reduced gap can only be the last block. */
+ BUG_ON_HOT(!ncb_blk_is_last(buf, blk));
+ }
+ else {
+ blk.sz_ptr = ncb_peek(buf, blk.off + NCB_GAP_SZ_OFF);
+ blk.sz = ncb_read_off(buf, blk.sz_ptr);
+ blk.sz_data = ncb_read_off(buf, ncb_peek(buf, blk.off + NCB_GAP_SZ_DATA_OFF));
+ BUG_ON_HOT(blk.sz < NCB_GAP_MIN_SZ);
+ }
}
else {
blk.sz_ptr = ncb_peek(buf, prev.off + NCB_GAP_SZ_DATA_OFF);