Implement the bucket allocator.  This implementation uses a three-stage
allocation scheme: (1) allocate from the freelist, (2) if that fails, carve
a chunk off of an 8KB block, or (3) just pass the thing directly to
apr_allocator_alloc().  In early testing, this implementation has been
shown to perform better than the existing alternatives.

Reviewed by:  Brian Pane


git-svn-id: https://svn.apache.org/repos/asf/apr/apr-util/trunk@58588 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/buckets/apr_buckets_alloc.c b/buckets/apr_buckets_alloc.c
index a38816d..e77ed09 100644
--- a/buckets/apr_buckets_alloc.c
+++ b/buckets/apr_buckets_alloc.c
@@ -52,33 +52,102 @@
  * <http://www.apache.org/>.
  */
 
-#include <stdlib.h>
 #include "apr_buckets.h"
+#include "apr_allocator.h"
 
-/*
- * XXX: this file will be filled in later
+#define ALLOC_AMT (8192 - APR_MEMNODE_T_SIZE)
+
+typedef struct node_header_t {
+    apr_size_t size;
+    apr_bucket_alloc_t *alloc;
+    apr_memnode_t *memnode;
+    struct node_header_t *next;
+} node_header_t;
+
+#define SIZEOF_NODE_HEADER_T  APR_ALIGN_DEFAULT(sizeof(node_header_t))
+#define SMALL_NODE_SIZE       (APR_BUCKET_ALLOC_SIZE + SIZEOF_NODE_HEADER_T)
+
+/** A list of free memory from which new buckets or private bucket
+ *  structures can be allocated.
  */
-
 struct apr_bucket_alloc_t {
-    int x; /* temporary... some compilers trigger an error on empty structure defs */
+    apr_allocator_t *allocator;
+    node_header_t *freelist;
+    apr_memnode_t *blocks;
 };
 
 APU_DECLARE_NONSTD(apr_bucket_alloc_t *) apr_bucket_alloc_create(apr_pool_t *p)
 {
-    return (apr_bucket_alloc_t *)0xFECCFECC;
+    apr_allocator_t *allocator;
+    apr_bucket_alloc_t *list;
+    apr_memnode_t *block;
+
+    apr_allocator_create(&allocator);
+    block = apr_allocator_alloc(allocator, ALLOC_AMT);
+    list = (apr_bucket_alloc_t *)block->first_avail;
+    list->allocator = allocator;
+    list->freelist = NULL;
+    list->blocks = block;
+    block->first_avail += APR_ALIGN_DEFAULT(sizeof(*list));
+    return list;
 }
 
 APU_DECLARE_NONSTD(void) apr_bucket_alloc_destroy(apr_bucket_alloc_t *list)
 {
+    apr_allocator_t *allocator = list->allocator;
+
+    apr_allocator_free(allocator, list->blocks);
+    apr_allocator_destroy(allocator);
 }
 
 APU_DECLARE_NONSTD(void *) apr_bucket_alloc(apr_size_t size, 
                                             apr_bucket_alloc_t *list)
 {
-    return malloc(size);
+    node_header_t *node;
+    apr_memnode_t *active = list->blocks;
+    char *endp;
+
+    size += SIZEOF_NODE_HEADER_T;
+    if (size <= SMALL_NODE_SIZE) {
+        if (list->freelist) {
+            node = list->freelist;
+            list->freelist = node->next;
+        }
+        else {
+            endp = active->first_avail + SMALL_NODE_SIZE;
+            if (endp >= active->endp) {
+                list->blocks = apr_allocator_alloc(list->allocator, ALLOC_AMT);
+                list->blocks->next = active;
+                active = list->blocks;
+                endp = active->first_avail + SMALL_NODE_SIZE;
+            }
+            node = (node_header_t *)active->first_avail;
+            node->alloc = list;
+            node->memnode = active;
+            node->size = SMALL_NODE_SIZE;
+            active->first_avail = endp;
+        }
+    }
+    else {
+        apr_memnode_t *memnode = apr_allocator_alloc(list->allocator, size);
+        node = (node_header_t *)memnode->first_avail;
+        node->alloc = list;
+        node->memnode = memnode;
+        node->size = size;
+    }
+    return ((char *)node) + SIZEOF_NODE_HEADER_T;
 }
 
-APU_DECLARE_NONSTD(void) apr_bucket_free(void *block)
+APU_DECLARE_NONSTD(void) apr_bucket_free(void *mem)
 {
-    free(block);
+    node_header_t *node = (node_header_t *)((char *)mem - SIZEOF_NODE_HEADER_T);
+    apr_bucket_alloc_t *list = node->alloc;
+
+    if (node->size == SMALL_NODE_SIZE) {
+        node->next = list->freelist;
+        list->freelist = node;
+    }
+    else {
+        apr_allocator_free(list->allocator, node->memnode);
+    }
 }
diff --git a/include/apr_buckets.h b/include/apr_buckets.h
index b1e5dbd..2a91e43 100644
--- a/include/apr_buckets.h
+++ b/include/apr_buckets.h
@@ -84,8 +84,8 @@
  * @{ 
  */
 
-/** default bucket buffer size */
-#define APR_BUCKET_BUFF_SIZE 8192
+/** default bucket buffer size - 8KB minus room for memory allocator headers */
+#define APR_BUCKET_BUFF_SIZE 8000
 
 typedef enum {
     APR_BLOCK_READ,   /* block until data becomes available */
@@ -614,6 +614,28 @@
     apr_pool_t *readpool;
 };
 
+typedef union apr_bucket_structs apr_bucket_structs;
+/**
+ * A union of all bucket structures so we know what
+ * the max size is.
+ */
+union apr_bucket_structs {
+    apr_bucket      b;
+    apr_bucket_heap heap;
+    apr_bucket_pool pool;
+#if APR_HAS_MMAP
+    apr_bucket_mmap mmap;
+#endif
+    apr_bucket_file file;
+};
+
+/**
+ * The amount that apr_bucket_alloc() should allocate in the common case.
+ * Note: this is twice as big as apr_bucket_structs to allow breathing
+ * room for third-party bucket types.
+ */
+#define APR_BUCKET_ALLOC_SIZE  APR_ALIGN_DEFAULT(2*sizeof(apr_bucket_structs))
+
 /*  *****  Bucket Brigade Functions  *****  */
 /**
  * Create a new bucket brigade.  The bucket brigade is originally empty.