+/**
+ * Create a new buffer pool.
+ *
+ * \param name The name of the new buffer pool.
+ * \param area_size The size in bytes of the pool area.
+ *
+ * \return An opaque pointer to the newly created buffer pool. It must be
+ * passed to btr_pool_free() after it is no longer used to deallocate all
+ * resources.
+ */
+struct btr_pool *btr_pool_new(const char *name, size_t area_size)
+{
+ struct btr_pool *btrp;
+
+ PARA_INFO_LOG("%s, %zu bytes\n", name, area_size);
+ btrp = para_malloc(sizeof(*btrp));
+ btrp->area_start = para_malloc(area_size);
+ btrp->area_end = btrp->area_start + area_size;
+ btrp->rhead = btrp->area_start;
+ btrp->whead = btrp->area_start;
+ btrp->name = para_strdup(name);
+ return btrp;
+}
+
+/**
+ * Deallocate resources used by a buffer pool.
+ *
+ * \param btrp A pointer obtained via btr_pool_new().
+ */
+void btr_pool_free(struct btr_pool *btrp)
+{
+ if (!btrp)
+ return;
+ free(btrp->area_start);
+ free(btrp->name);
+ free(btrp);
+}
+
+/**
+ * Return the size of the buffer pool area.
+ *
+ * \param btrp The buffer pool.
+ *
+ * \return The same value which was passed during creation time to
+ * btr_pool_new().
+ */
+size_t btr_pool_size(struct btr_pool *btrp)
+{
+ return btrp->area_end - btrp->area_start;
+}
+
+static size_t btr_pool_filled(struct btr_pool *btrp)
+{
+ if (!btrp->whead)
+ return btr_pool_size(btrp);
+ if (btrp->rhead <= btrp->whead)
+ return btrp->whead - btrp->rhead;
+ return btr_pool_size(btrp) - (btrp->rhead - btrp->whead);
+}
+
+/**
+ * Get the number of unused bytes in the buffer pool.
+ *
+ * \param btrp The pool.
+ *
+ * \return The number of bytes that can currently be allocated.
+ *
+ * Note that in general the returned number of bytes is not available as a
+ * single contiguous buffer. Use btr_pool_available() to obtain the length of
+ * the largest contiguous buffer that can currently be allocated from the
+ * buffer pool.
+ */
+size_t btr_pool_unused(struct btr_pool *btrp)
+{
+ return btr_pool_size(btrp) - btr_pool_filled(btrp);
+}
+
+/*
+ * Return maximal size available for one read. This is
+ * smaller than the value returned by btr_pool_unused().
+ */
+static size_t btr_pool_available(struct btr_pool *btrp)
+{
+ if (!btrp->whead)
+ return 0;
+ if (btrp->rhead <= btrp->whead)
+ return btrp->area_end - btrp->whead;
+ return btrp->rhead - btrp->whead;
+}
+
+/**
+ * Obtain the current write head.
+ *
+ * \param btrp The buffer pool.
+ * \param result The write head is returned here.
+ *
+ * \return The maximal amount of bytes that may be written to the returned
+ * buffer.
+ */
+size_t btr_pool_get_buffer(struct btr_pool *btrp, char **result)
+{
+ if (result)
+ *result = btrp->whead;
+ return btr_pool_available(btrp);
+}
+
+/**
+ * Get references to buffers pointing to free space of the buffer pool area.
+ *
+ * \param btrp The buffer pool.
+ * \param iov The scatter array.
+ *
+ * \return Zero if the buffer pool is full, one if the free space of the buffer
+ * pool area is available as a single contiguous buffer, two if the free space
+ * consists of two buffers. If this function returns the value n, then n
+ * elements of \a iov are initialized.
+ */
+int btr_pool_get_buffers(struct btr_pool *btrp, struct iovec iov[2])
+{
+ size_t sz, unused;
+ char *buf;
+
+ sz = btr_pool_get_buffer(btrp, &buf);
+ if (sz == 0)
+ return 0;
+ iov[0].iov_len = sz;
+ iov[0].iov_base = buf;
+ unused = btr_pool_unused(btrp);
+ if (sz == unused)
+ return 1;
+ iov[1].iov_len = unused - sz;
+ iov[1].iov_base = btrp->area_start;
+ return 2;
+}
+
+/**
+ * Mark a part of the buffer pool area as allocated.
+ *
+ * \param btrp The buffer pool.
+ * \param size The amount of bytes to be allocated.
+ *
+ * This is usually called after the caller wrote to the buffer obtained by
+ * btr_pool_get_buffer().
+ */
+static void btr_pool_allocate(struct btr_pool *btrp, size_t size)
+{
+ char *end;
+
+ if (size == 0)
+ return;
+ assert(size <= btr_pool_available(btrp));
+ end = btrp->whead + size;
+ assert(end <= btrp->area_end);
+
+ if (end == btrp->area_end) {
+ PARA_DEBUG_LOG("%s: end of pool area reached\n", btrp->name);
+ end = btrp->area_start;
+ }
+ if (end == btrp->rhead) {
+ PARA_DEBUG_LOG("%s btrp buffer full\n", btrp->name);
+ end = NULL; /* buffer full */
+ }
+ btrp->whead = end;
+}
+
+static void btr_pool_deallocate(struct btr_pool *btrp, size_t size)
+{
+ char *end = btrp->rhead + size;
+
+ if (size == 0)
+ return;
+ assert(end <= btrp->area_end);
+ assert(size <= btr_pool_filled(btrp));
+ if (end == btrp->area_end)
+ end = btrp->area_start;
+ if (!btrp->whead)
+ btrp->whead = btrp->rhead;
+ btrp->rhead = end;
+ if (btrp->rhead == btrp->whead)
+ btrp->rhead = btrp->whead = btrp->area_start;
+}
+