MINOR: buffer: add a few basic functions for the new API
Here's the list of newly introduced functions :
- b_data(), returning the total amount of data in the buffer (currently i+o)
- b_orig(), returning the origin of the storage area, that is, the place of
position 0.
- b_wrap(), pointer to wrapping point (currently data+size)
- b_size(), returning the size of the buffer
- b_room(), returning the amount of bytes left available
- b_full(), returning true if the buffer is full, otherwise false
- b_stop(), pointer to end of data mark (currently p+i), used to compute
distances or a stop pointer for a loop.
- b_peek(), this one will help make the transition to the new buffer model.
It returns a pointer to a position in the buffer known from an offest
relative to the beginning of the data in the buffer. Thus, we can replace
the following occurrences :
bo_ptr(b) => b_peek(b, 0);
bo_end(b) => b_peek(b, b->o);
bi_ptr(b) => b_peek(b, b->o);
bi_end(b) => b_peek(b, b->i + b->o);
b_ptr(b, ofs) => b_peek(b, b->o + ofs);
- b_head(), pointer to the beginning of data (currently bo_ptr())
- b_tail(), pointer to first free place (currently bi_ptr())
- b_next() / b_next_ofs(), pointer to the next byte, taking wrapping
into account.
- b_dist(), returning the distance between two pointers belonging to a buffer
- b_reset(), which resets the buffer
- b_space_wraps(), indicating if the free space wraps around the buffer
- b_almost_full(), indicating if 3/4 or more of the buffer are used
Some of these are provided with the unchecked variants using the "__"
prefix, or with the "_ofs" suffix indicating they return a relative
position to the buffer's origin instead of a pointer.
Cc: Olivier Houchard <ohouchard@haproxy.com>
diff --git a/include/common/buf.h b/include/common/buf.h
index 4c372de..5e7f3b7 100644
--- a/include/common/buf.h
+++ b/include/common/buf.h
@@ -30,7 +30,6 @@
#include <stdint.h>
-
/* Structure defining a buffer's head */
struct buffer {
char *p; /* buffer's start pointer, separates in and out data */
@@ -40,6 +39,257 @@
char data[0]; /* <size> bytes of stored data */
};
+
+/***************************************************************************/
+/* Functions used to compute offsets and pointers. Most of them exist in */
+/* both wrapping-safe and unchecked ("__" prefix) variants. Some returning */
+/* a pointer are also provided with an "_ofs" suffix when they return an */
+/* offset relative to the storage area. */
+/***************************************************************************/
+
+/* b_orig() : returns the pointer to the origin of the storage, which is the
+ * location of byte at offset zero. This is mostly used by functions which
+ * handle the wrapping by themselves.
+ */
+static inline char *b_orig(const struct buffer *b)
+{
+ return (char *)b->data;
+}
+
+/* b_size() : returns the size of the buffer. */
+static inline size_t b_size(const struct buffer *b)
+{
+ return b->size;
+}
+
+/* b_wrap() : returns the pointer to the wrapping position of the buffer area,
+ * which is by definition the first byte not part of the buffer.
+ */
+static inline char *b_wrap(const struct buffer *b)
+{
+ return (char *)b->data + b->size;
+}
+
+/* b_data() : returns the number of bytes present in the buffer. */
+static inline size_t b_data(const struct buffer *b)
+{
+ return b->i + b->o;
+}
+
+/* b_room() : returns the amount of room left in the buffer */
+static inline size_t b_room(const struct buffer *b)
+{
+ return b->size - b_data(b);
+}
+
+/* b_full() : returns true if the buffer is full. */
+static inline size_t b_full(const struct buffer *b)
+{
+ return !b_room(b);
+}
+
+
+/* b_stop() : returns the pointer to the byte following the end of the buffer,
+ * which may be out of the buffer if the buffer ends on the last byte of the
+ * area.
+ */
+static inline size_t __b_stop_ofs(const struct buffer *b)
+{
+ return b->p - b->data + b->i;
+}
+
+static inline const char *__b_stop(const struct buffer *b)
+{
+ return b->p + b->i;
+}
+
+static inline size_t b_stop_ofs(const struct buffer *b)
+{
+ size_t stop = __b_stop_ofs(b);
+
+ if (stop > b->size)
+ stop -= b->size;
+ return stop;
+}
+
+static inline const char *b_stop(const struct buffer *b)
+{
+ return b->data + b_stop_ofs(b);
+}
+
+
+/* b_peek() : returns a pointer to the data at position <ofs> relative to the
+ * head of the buffer. Will typically point to input data if called with the
+ * amount of output data. The wrapped versions will only support wrapping once
+ * before the beginning or after the end.
+ */
+static inline size_t __b_peek_ofs(const struct buffer *b, size_t ofs)
+{
+ return b->p - b->data + ofs - b->o;
+}
+
+static inline char *__b_peek(const struct buffer *b, size_t ofs)
+{
+ return b->p - b->o + ofs;
+}
+
+static inline size_t b_peek_ofs(const struct buffer *b, size_t ofs)
+{
+ size_t ret = __b_peek_ofs(b, ofs);
+
+ if (ret >= b->size) {
+ /* wraps either up or down */
+ if ((ssize_t)ret < 0)
+ ret += b->size;
+ else
+ ret -= b->size;
+ }
+
+ return ret;
+}
+
+static inline char *b_peek(const struct buffer *b, size_t ofs)
+{
+ return (char *)b->data + b_peek_ofs(b, ofs);
+}
+
+
+/* b_head() : returns the pointer to the buffer's head, which is the location
+ * of the next byte to be dequeued. Note that for buffers of size zero, the
+ * returned pointer may be outside of the buffer or even invalid.
+ */
+static inline size_t __b_head_ofs(const struct buffer *b)
+{
+ return __b_peek_ofs(b, 0);
+}
+
+static inline char *__b_head(const struct buffer *b)
+{
+ return __b_peek(b, 0);
+}
+
+static inline size_t b_head_ofs(const struct buffer *b)
+{
+ return b_peek_ofs(b, 0);
+}
+
+static inline char *b_head(const struct buffer *b)
+{
+ return b_peek(b, 0);
+}
+
+
+/* b_tail() : returns the pointer to the tail of the buffer, which is the
+ * location of the first byte where it is possible to enqueue new data. Note
+ * that for buffers of size zero, the returned pointer may be outside of the
+ * buffer or even invalid.
+ */
+static inline size_t __b_tail_ofs(const struct buffer *b)
+{
+ return __b_peek_ofs(b, b_data(b));
+}
+
+static inline char *__b_tail(const struct buffer *b)
+{
+ return __b_peek(b, b_data(b));
+}
+
+static inline size_t b_tail_ofs(const struct buffer *b)
+{
+ return b_peek_ofs(b, b_data(b));
+}
+
+static inline char *b_tail(const struct buffer *b)
+{
+ return b_peek(b, b_data(b));
+}
+
+
+/* b_next() : for an absolute pointer <p> or a relative offset <o> pointing to
+ * a valid location within buffer <b>, returns either the absolute pointer or
+ * the relative offset pointing to the next byte, which usually is at (p + 1)
+ * unless p reaches the wrapping point and wrapping is needed.
+ */
+static inline size_t b_next_ofs(const struct buffer *b, size_t o)
+{
+ o++;
+ if (o == b->size)
+ o = 0;
+ return o;
+}
+
+static inline char *b_next(const struct buffer *b, const char *p)
+{
+ p++;
+ if (p == b_wrap(b))
+ p = b_orig(b);
+ return (char *)p;
+}
+
+/* b_dist() : returns the distance between two pointers, taking into account
+ * the ability to wrap around the buffer's end. The operation is not defined if
+ * either of the pointers does not belong to the buffer or if their distance is
+ * greater than the buffer's size.
+ */
+static inline size_t b_dist(const struct buffer *b, const char *from, const char *to)
+{
+ ssize_t dist = to - from;
+
+ dist += dist < 0 ? b_size(b) : 0;
+ return dist;
+}
+
+/* b_almost_full() : returns 1 if the buffer uses at least 3/4 of its capacity,
+ * otherwise zero. Buffers of size zero are considered full.
+ */
+static inline int b_almost_full(const struct buffer *b)
+{
+ return b_data(b) >= b_size(b) * 3 / 4;
+}
+
+/* b_space_wraps() : returns non-zero only if the buffer's free space wraps :
+ * [ |oooo| ] => yes
+ * [ |iiii| ] => yes
+ * [ |oooo|iiii| ] => yes
+ * [oooo| ] => no
+ * [ |oooo] => no
+ * [iiii| ] => no
+ * [ |iiii] => no
+ * [oooo|iiii| ] => no
+ * [ |oooo|iiii] => no
+ * [iiii| |oooo] => no
+ * [oo|iiii| |oo] => no
+ * [iiii| |oo|ii] => no
+ * [oooooooooo|iiiiiiiiiii] => no
+ * [iiiiiiiiiiiii|oooooooo] => no
+ *
+ * So the only case where the buffer does not wrap is when there's data either
+ * at the beginning or at the end of the buffer. Thus we have this :
+ * - if (head <= 0) ==> doesn't wrap
+ * - if (tail >= size) ==> doesn't wrap
+ * - otherwise wraps
+ */
+static inline int b_space_wraps(const struct buffer *b)
+{
+ if ((ssize_t)__b_head_ofs(b) <= 0)
+ return 0;
+ if (__b_tail_ofs(b) >= b_size(b))
+ return 0;
+ return 1;
+}
+
+
+/*********************************************/
+/* Functions used to modify the buffer state */
+/*********************************************/
+
+/* b_reset() : resets a buffer. The size is not touched. */
+static inline void b_reset(struct buffer *b)
+{
+ b->o = 0;
+ b->i = 0;
+ b->p = b_orig(b);
+}
#endif /* _COMMON_BUF_H */
diff --git a/include/common/buffer.h b/include/common/buffer.h
index 5e02cad..590078c 100644
--- a/include/common/buffer.h
+++ b/include/common/buffer.h
@@ -379,9 +379,7 @@
if (buf == &buf_empty)
return 0;
- if (!buf->size || buffer_total_space(buf) < buf->size / 4)
- return 1;
- return 0;
+ return b_almost_full(buf);
}
/* Cut the first <n> pending bytes in a contiguous buffer. It is illegal to
@@ -639,15 +637,6 @@
return 1;
}
-/* Resets a buffer. The size is not touched. */
-static inline void b_reset(struct buffer *buf)
-{
- buf->o = 0;
- buf->i = 0;
- buf->p = buf->data;
-
-}
-
/* Allocates a buffer and replaces *buf with this buffer. If no memory is
* available, &buf_wanted is used instead. No control is made to check if *buf
* already pointed to another buffer. The allocated buffer is returned, or