MINOR: htx: Deduce the number of used blocks from tail and head values

<head> and <tail> fields are now signed 32-bits integers. For an empty HTX
message, these fields are set to -1. So the field <used> is now useless and can
safely be removed. To know if an HTX message is empty or not, we just compare
<head> against -1 (it also works with <tail>). The function htx_nbblks() has
been added to get the number of used blocks.
diff --git a/include/common/htx.h b/include/common/htx.h
index ba6604b..3c9aa68 100644
--- a/include/common/htx.h
+++ b/include/common/htx.h
@@ -202,21 +202,21 @@
 			  * blocks (blocks and their contents), you need to add size used by blocks,
 			  * i.e. [ used * sizeof(struct htx_blk *) ] */
 
-	uint32_t used;   /* number of blocks in use */
-	uint32_t tail;   /* newest inserted block. -1 if the HTX message is empty */
-	uint32_t head;   /* oldest inserted block. -1 if the HTX message is empty */
+	int32_t tail;   /* newest inserted block. -1 if the HTX message is empty */
+	int32_t head;   /* oldest inserted block. -1 if the HTX message is empty */
+	int32_t first;  /* position of the first block to (re)start the analyse. -1 if unset. */
 
 	uint32_t tail_addr; /* start address of the free space in front of the the blocks table */
 	uint32_t head_addr; /* start address of the free space at the beginning */
 	uint32_t end_addr;  /* end address of the free space at the beginning */
 
-
 	uint64_t extra;  /* known bytes amount remaining to receive */
 	uint32_t flags;  /* HTX_FL_* */
 
-	int32_t  first;  /* position of the first block to (re)start the analyse. -1 if unset. */
+	/* XXX 4 bytes unused */
 
-	struct htx_blk blocks[0]; /* Blocks representing the HTTP message itself */
+	/* Blocks representing the HTTP message itself */
+	struct htx_blk blocks[0] __attribute__((aligned(8)));
 };
 
 
@@ -370,15 +370,12 @@
 	}
 }
 
-/* Returns the position of the oldest entry (head).
- *
- * An signed 32-bits integer is returned to handle -1 case. Blocks position are
- * store on unsigned 32-bits integer, but it is impossible to have so much
- * blocks to overflow a 32-bits signed integer !
+/* Returns the position of the oldest entry (head). It returns a signed 32-bits
+ * integer, -1 means the HTX message is empty.
  */
 static inline int32_t htx_get_head(const struct htx *htx)
 {
-	return (htx->used ? htx->head : -1);
+	return htx->head;
 }
 
 /* Returns the oldest HTX block (head) if the HTX message is not
@@ -401,15 +398,12 @@
 	return (blk ? htx_get_blk_type(blk) : HTX_BLK_UNUSED);
 }
 
-/* Returns the position of the newest entry (tail).
- *
- * An signed 32-bits integer is returned to handle -1 case. Blocks position are
- * store on unsigned 32-bits integer, but it is impossible to have so much
- * blocks to overflow a 32-bits signed integer !
+/* Returns the position of the newest entry (tail).  It returns a signed 32-bits
+ * integer, -1 means the HTX message is empty.
  */
 static inline int32_t htx_get_tail(const struct htx *htx)
 {
-	return (htx->used ? htx->tail : -1);
+	return htx->tail;
 }
 
 /* Returns the newest HTX block (tail) if the HTX message is not
@@ -432,17 +426,11 @@
 	return (blk ? htx_get_blk_type(blk) : HTX_BLK_UNUSED);
 }
 
-/* Returns the position of the first block in the HTX message <htx>. If unset,
- * or if <htx> is empty, -1 is returned.
- *
- * An signed 32-bits integer is returned to handle -1 case. Blocks position are
- * store on unsigned 32-bits integer, but it is impossible to have so much
- * blocks to overflow a 32-bits signed integer !
+/* Returns the position of the first block in the HTX message <htx>. -1 means
+ * the first block is unset or the HTS is empty.
  */
 static inline int32_t htx_get_first(const struct htx *htx)
 {
-	if (!htx->used)
-		return -1;
 	return htx->first;
 }
 
@@ -469,17 +457,10 @@
 
 /* Returns the position of block immediately before the one pointed by <pos>. If
  * the message is empty or if <pos> is the position of the head, -1 returned.
- *
- * An signed 32-bits integer is returned to handle -1 case. Blocks position are
- * store on unsigned 32-bits integer, but it is impossible to have so much
- * blocks to overflow a 32-bits signed integer !
  */
 static inline int32_t htx_get_prev(const struct htx *htx, uint32_t pos)
 {
-	int32_t head;
-
-	head = htx_get_head(htx);
-	if (head == -1 || pos == head)
+	if (htx->head == -1 || pos == htx->head)
 		return -1;
 	return (pos - 1);
 }
@@ -498,14 +479,10 @@
 
 /* Returns the position of block immediately after the one pointed by <pos>. If
  * the message is empty or if <pos> is the position of the tail, -1 returned.
- *
- * An signed 32-bits integer is returned to handle -1 case. Blocks position are
- * store on unsigned 32-bits integer, but it is impossible to have so much
- * blocks to overflow a 32-bits signed integer !
  */
 static inline int32_t htx_get_next(const struct htx *htx, uint32_t pos)
 {
-	if (!htx->used || pos == htx->tail)
+	if (htx->tail == -1 || pos == htx->tail)
 		return -1;
 	return (pos + 1);
 
@@ -655,7 +632,10 @@
 /* Returns the space used by metadata in <htx>. */
 static inline uint32_t htx_meta_space(const struct htx *htx)
 {
-	return (htx->used * sizeof(htx->blocks[0]));
+	if (htx->tail == -1)
+		return 0;
+
+	return ((htx->tail + 1 - htx->head) * sizeof(htx->blocks[0]));
 }
 
 /* Returns the space used (payload + metadata) in <htx> */
@@ -707,11 +687,11 @@
 /* Resets an HTX message */
 static inline void htx_reset(struct htx *htx)
 {
-	htx->data = htx->used = htx->tail = htx->head  = 0;
+	htx->tail = htx->head  = htx->first = -1;
+	htx->data = 0;
 	htx->tail_addr = htx->head_addr = htx->end_addr = 0;
 	htx->extra = 0;
 	htx->flags = HTX_FL_NONE;
-	htx->first = -1;
 }
 
 /* Returns the available room for raw data in buffer <buf> once HTX overhead is
@@ -773,7 +753,7 @@
 /* Update <buf> accordingly to the HTX message <htx> */
 static inline void htx_to_buf(struct htx *htx, struct buffer *buf)
 {
-	if (!htx->used && !(htx->flags & (HTX_FL_PARSING_ERROR|HTX_FL_UPGRADE))) {
+	if ((htx->head == -1) && !(htx->flags & (HTX_FL_PARSING_ERROR|HTX_FL_UPGRADE))) {
 		htx_reset(htx);
 		b_set_data(buf, 0);
 	}
@@ -786,7 +766,7 @@
  */
 static inline int htx_is_empty(const struct htx *htx)
 {
-	return !htx->used;
+	return (htx->head == -1);
 }
 
 /* Returns 1 if the message is not empty, otherwise it returns 0. Note that it
@@ -794,9 +774,17 @@
  */
 static inline int htx_is_not_empty(const struct htx *htx)
 {
-	return htx->used;
+	return (htx->head != -1);
 }
 
+/* Returns the number of used blocks in the HTX message <htx>. Note that it is
+ * illegal to call this function with htx == NULL. Note also blocks of type
+ * HTX_BLK_UNUSED are part of used blocks.
+ */
+static inline int htx_nbblks(const struct htx *htx)
+{
+	return ((htx->head != -1) ? (htx->tail + 1 - htx->head) : 0);
+}
 /* For debugging purpose */
 static inline const char *htx_blk_type_str(enum htx_blk_type type)
 {
@@ -820,7 +808,8 @@
 	int32_t pos;
 
 	fprintf(stderr, "htx:%p [ size=%u - data=%u - used=%u - wrap=%s - extra=%llu]\n",
-		htx, htx->size, htx->data, htx->used, (!htx->head_addr) ? "NO" : "YES",
+		htx, htx->size, htx->data, htx_nbblks(htx),
+		(!htx->head_addr) ? "NO" : "YES",
 		(unsigned long long)htx->extra);
 	fprintf(stderr, "\tfirst=%d - head=%u, tail=%u\n",
 		htx->first, htx->head, htx->tail);
diff --git a/src/http_htx.c b/src/http_htx.c
index 0e788c1..13c1453 100644
--- a/src/http_htx.c
+++ b/src/http_htx.c
@@ -77,7 +77,7 @@
 		goto return_hdr;
 	}
 
-	if (!htx->used)
+	if (htx_is_empty(htx))
 		return 0;
 
 	for (blk = htx_get_first_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
@@ -431,7 +431,7 @@
 	v = htx_get_blk_value(htx, blk);
 	if (len == v.len) {
 		blk = htx_remove_blk(htx, blk);
-		if (blk || !htx->used) {
+		if (blk || htx_is_empty(htx)) {
 			ctx->blk = blk;
 			ctx->value = ist2(NULL, 0);
 			ctx->lws_before = ctx->lws_after = 0;
diff --git a/src/htx.c b/src/htx.c
index bdd10ba..bbf1f8f 100644
--- a/src/htx.c
+++ b/src/htx.c
@@ -13,7 +13,7 @@
 #include <common/chunk.h>
 #include <common/htx.h>
 
-struct htx htx_empty = { .size = 0, .data = 0, .used = 0 };
+struct htx htx_empty = { .size = 0, .data = 0, .head  = -1, .tail = -1, .first = -1 };
 
 /* Defragments an HTX message. It removes unused blocks and unwraps the payloads
  * part. A temporary buffer is used to do so. This function never fails. if
@@ -30,7 +30,7 @@
 	uint32_t addr, blksz;
 	int32_t first = -1;
 
-	if (!htx->used)
+	if (htx->head == -1)
 		return NULL;
 
 	blkpos = -1;
@@ -64,7 +64,6 @@
 
 	}
 
-	htx->used = new;
 	htx->first = first;
 	htx->head = 0;
 	htx->tail = new - 1;
@@ -116,16 +115,14 @@
 static struct htx_blk *htx_reserve_nxblk(struct htx *htx, uint32_t blksz)
 {
 	struct htx_blk *blk;
-	uint32_t used, tail;
-	uint32_t headroom, tailroom;
+	uint32_t tail, headroom, tailroom;
 
 	if (blksz > htx_free_data_space(htx))
 		return NULL; /* full */
 
-	if (!htx->used) {
+	if (htx->head == -1) {
 		/* Empty message */
 		htx->head = htx->tail = htx->first = 0;
-		htx->used = 1;
 		blk = htx_get_blk(htx, htx->tail);
 		blk->addr = 0;
 		htx->data = blksz;
@@ -141,11 +138,10 @@
 	 */
 	tail = htx->tail + 1;
 	if (sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, tail) >= htx->tail_addr)
-		used = htx->used + 1;
-	else if (tail > htx->used) {
+		;
+	else if (htx->head > 0) {
 		htx_defrag_blks(htx);
 		tail = htx->tail + 1;
-		used = htx->used + 1;
 		BUG_ON(sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, tail) < htx->tail_addr);
 	}
 	else
@@ -182,14 +178,12 @@
 		/* need to defragment the message before inserting upfront */
 		htx_defrag(htx, NULL);
 		tail = htx->tail + 1;
-		used = htx->used + 1;
 		blk = htx_get_blk(htx, tail);
 		blk->addr = htx->tail_addr;
 		htx->tail_addr += blksz;
 	}
 
 	htx->tail  = tail;
-	htx->used  = used;
 	htx->data += blksz;
 	/* Set first position if not already set */
 	if (htx->first == -1)
@@ -226,6 +220,8 @@
 	uint32_t sz, tailroom, headroom;
 	int ret = 3;
 
+	BUG_ON(htx->head == -1);
+
 	headroom = (htx->end_addr - htx->head_addr);
 	tailroom = sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, htx->tail) - htx->tail_addr;
 	BUG_ON((int32_t)headroom < 0);
@@ -328,8 +324,10 @@
 	enum htx_blk_type type;
 	uint32_t pos, addr, sz;
 
+	BUG_ON(htx->head == -1);
+
 	/* This is the last block in use */
-	if (htx->used == 1) {
+	if (htx->head == htx->tail) {
 		htx_reset(htx);
 		return NULL;
 	}
@@ -347,14 +345,12 @@
 	/* There is at least 2 blocks, so tail is always > 0 */
 	if (pos == htx->head) {
 		/* move the head forward */
-		htx->used--;
 		htx->head++;
 	}
 	else if (pos == htx->tail) {
 		/* remove the tail. this was the last inserted block so
 		 * return NULL. */
 		htx->tail--;
-		htx->used--;
 		blk = NULL;
 		goto end;
 	}
@@ -364,7 +360,7 @@
 	if (pos == htx->first)
 		htx->first = (blk ? htx_get_blk_pos(htx, blk) : -1);
 
-	if (htx->used == 1) {
+	if (htx->head == htx->tail) {
 		/* If there is just one block in the HTX message, free space can
 		 * be ajusted. This operation could save some defrags. */
 		struct htx_blk *lastblk = htx_get_blk(htx, htx->tail);
@@ -474,7 +470,7 @@
 	void *ptr;
 	uint32_t len, sz, tailroom, headroom;
 
-	if (!htx->used)
+	if (htx->head == -1)
 		goto add_new_block;
 
 	/* Not enough space to store data */
@@ -929,7 +925,7 @@
 	uint32_t sz, room;
 	int32_t len = data.len;
 
-	if (!htx->used)
+	if (htx->head == -1)
 		goto add_new_block;
 
 	/* Not enough space to store data */
diff --git a/src/mux_h1.c b/src/mux_h1.c
index 365c5ea..009afb0 100644
--- a/src/mux_h1.c
+++ b/src/mux_h1.c
@@ -1543,7 +1543,7 @@
 	 * the HTX blocks.
 	 */
 	if (!b_data(&h1c->obuf)) {
-		if (chn_htx->used == 1 &&
+		if (htx_nbblks(chn_htx) == 1 &&
 		    htx_get_blk_type(blk) == HTX_BLK_DATA &&
 		    htx_get_blk_value(chn_htx, blk).len == count) {
 			void *old_area = h1c->obuf.area;
diff --git a/src/mux_h2.c b/src/mux_h2.c
index ab781dd..7866c78 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -4936,7 +4936,7 @@
 	 * from the HTX blocks.
 	 */
 	if (unlikely(fsize == count &&
-	             htx->used == 1 && type == HTX_BLK_DATA &&
+	             htx_nbblks(htx) == 1 && type == HTX_BLK_DATA &&
 	             fsize <= h2s->mws && fsize <= h2c->mws && fsize <= h2c->mfs)) {
 		void *old_area = mbuf->area;
 
diff --git a/src/stream.c b/src/stream.c
index a5c5f45..78c2288 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -3344,7 +3344,7 @@
 
 			chunk_appendf(&trash,
 				      "      htx=%p flags=0x%x size=%u data=%u used=%u wrap=%s extra=%llu\n",
-				      htx, htx->flags, htx->size, htx->data, htx->used,
+				      htx, htx->flags, htx->size, htx->data, htx_nbblks(htx),
 				      (htx->tail >= htx->head) ? "NO" : "YES",
 				      (unsigned long long)htx->extra);
 		}
@@ -3383,7 +3383,7 @@
 
 			chunk_appendf(&trash,
 				      "      htx=%p flags=0x%x size=%u data=%u used=%u wrap=%s extra=%llu\n",
-				      htx, htx->flags, htx->size, htx->data, htx->used,
+				      htx, htx->flags, htx->size, htx->data, htx_nbblks(htx),
 				      (htx->tail >= htx->head) ? "NO" : "YES",
 				      (unsigned long long)htx->extra);
 		}