]> git.ozlabs.org Git - ccan/blobdiff - ccan/block_pool/block_pool.c
Added module block_pool
[ccan] / ccan / block_pool / block_pool.c
diff --git a/ccan/block_pool/block_pool.c b/ccan/block_pool/block_pool.c
new file mode 100644 (file)
index 0000000..0959e73
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+        Copyright (c) 2009  Joseph A. Adams
+        All rights reserved.
+        
+        Redistribution and use in source and binary forms, with or without
+        modification, are permitted provided that the following conditions
+        are met:
+        1. Redistributions of source code must retain the above copyright
+           notice, this list of conditions and the following disclaimer.
+        2. Redistributions in binary form must reproduce the above copyright
+           notice, this list of conditions and the following disclaimer in the
+           documentation and/or other materials provided with the distribution.
+        3. The name of the author may not be used to endorse or promote products
+           derived from this software without specific prior written permission.
+        
+        THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+        IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+        OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+        IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+        INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+        NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+        DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+        THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+        (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+        THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "block_pool.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+//must be a power of 2
+#define BLOCK_SIZE 4096
+
+struct block {
+       size_t remaining;
+       size_t size;
+       char *data;
+};
+
+struct block_pool {
+       size_t count;
+       size_t alloc; //2^n - 1, where n is an integer > 1
+       struct block *block;
+       
+       //blocks are arranged in a max-heap by the .remaining field
+       // (except the root block does not percolate down until it is filled)
+};
+
+static int destructor(struct block_pool *bp) {
+       struct block *block = bp->block;
+       size_t d = bp->count;
+       
+       for (;d--;block++)
+               free(block->data);
+       free(bp->block);
+       
+       return 0;
+}
+
+struct block_pool *block_pool_new(void *ctx) {
+       struct block_pool *bp = talloc(ctx, struct block_pool);
+       talloc_set_destructor(bp, destructor);
+       
+       bp->count = 0;
+       bp->alloc = 7;
+       bp->block = malloc(bp->alloc * sizeof(struct block));
+       
+       return bp;
+}
+
+static void *new_block(struct block *b, size_t needed) {
+       b->size = (needed+(BLOCK_SIZE-1)) & ~(BLOCK_SIZE-1);
+       b->remaining = b->size - needed;
+       b->data = malloc(b->size);
+       return b->data;
+}
+
+//for the first block, keep the memory usage low in case it's the only block.
+static void *new_block_tiny(struct block *b, size_t needed) {
+       if (needed < 256)
+               b->size = 256;
+       else
+               b->size = (needed+(BLOCK_SIZE-1)) & ~(BLOCK_SIZE-1);
+       b->remaining = b->size - needed;
+       b->data = malloc(b->size);
+       return b->data;
+}
+
+static void *try_block(struct block *b, size_t size, size_t align) {
+       size_t offset = b->size - b->remaining;
+       offset = (offset+align) & ~align;
+       
+       if (b->size-offset >= size) {
+               //good, we can use this block
+               void *ret = b->data + offset;
+               b->remaining = b->size-offset-size;
+               
+               return ret;
+       }
+       
+       return NULL;
+}
+
+#define L(node) (node+node+1)
+#define R(node) (node+node+2)
+#define P(node) ((node-1)>>1)
+
+#define V(node) (bp->block[node].remaining)
+
+static void percolate_down(struct block_pool *bp, size_t node) {
+       size_t child = L(node);
+       struct block tmp;
+       
+       //get the maximum child
+       if (child >= bp->count)
+               return;
+       if (child+1 < bp->count && V(child+1) > V(child))
+               child++;
+       
+       if (V(child) <= V(node))
+               return;
+       
+       tmp = bp->block[node];
+       bp->block[node] = bp->block[child];
+       bp->block[child] = tmp;
+       
+       percolate_down(bp, child);
+}
+
+//note:  percolates up to either 1 or 2 as a root
+static void percolate_up(struct block_pool *bp, size_t node) {
+       size_t parent = P(node);
+       struct block tmp;
+       
+       if (node<3 || V(parent) >= V(node))
+               return;
+       
+       tmp = bp->block[node];
+       bp->block[node] = bp->block[parent];
+       bp->block[parent] = tmp;
+       
+       percolate_up(bp, parent);
+}
+
+void *block_pool_alloc_align(struct block_pool *bp, size_t size, size_t align) {
+       void *ret;
+       
+       if (align)
+               align--;
+       
+       //if there aren't any blocks, make a new one
+       if (!bp->count) {
+               bp->count = 1;
+               return new_block_tiny(bp->block, size);
+       }
+       
+       //try the root block
+       ret = try_block(bp->block, size, align);
+       if (ret)
+               return ret;
+       
+       //root block is filled, percolate down and try the biggest one
+       percolate_down(bp, 0);
+       ret = try_block(bp->block, size, align);
+       if (ret)
+               return ret;
+       
+       //the biggest wasn't big enough; we need a new block
+       if (bp->count >= bp->alloc) {
+               //make room for another block
+               bp->alloc += bp->alloc;
+               bp->alloc++;
+               bp->block = realloc(bp->block, bp->alloc * sizeof(struct block));
+       }
+       ret = new_block(bp->block+(bp->count++), size);
+       
+       //fix the heap after adding the new block
+       percolate_up(bp, bp->count-1);
+       
+       return ret;
+}
+
+#undef L
+#undef R
+#undef P
+#undef V
+
+char *block_pool_strdup(struct block_pool *bp, const char *str) {
+       size_t size = strlen(str)+1;
+       char *ret = block_pool_alloc_align(bp, size, 1);
+       
+       memcpy(ret, str, size);
+       return ret;
+}