diff src/lib/mempool-allocfree.c @ 0:3b1985cbc908 HEAD

Initial revision
author Timo Sirainen <tss@iki.fi>
date Fri, 09 Aug 2002 12:15:38 +0300
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/mempool-allocfree.c	Fri Aug 09 12:15:38 2002 +0300
@@ -0,0 +1,843 @@
+/*
+ mempool-allocfree.c : Memory pool manager for custom alloc+free
+
+    Copyright (c) 2001-2002 Timo Sirainen
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+
+/* TODO:
+
+    - p_free() could check better that it's freeing a properly allocated
+      memory from pool it was told to.
+
+    - try to keep optimal pool sizes ie. keep them in the size they're
+      using 90% of the time
+    - add statistics functions that print/return the memory usage
+    - don't free() anything, save them to unused-list and reuse later.
+*/
+
+/* extensive debugging */
+/* #define POOL_DEBUG */
+
+/* always clear the memory area that has been free'd. for debugging mostly */
+/* #define POOL_FREE_CLEAR 0xd0 */
+
+/* Save the used pool block for each memory allocation, so you can be sure
+   that the memory is being free'd from correct pool. Also, with this option
+   we don't need to compare pointers which makes this code fully ANSI-C
+   compatible :) */
+#define POOL_SAVE_BLOCK
+
+#include "lib.h"
+#include "mempool.h"
+
+#include <stdlib.h>
+
+#define is_pool(pool) ((pool) != NULL && (pool)->magic == 0xbeef)
+
+#define check_pool(pool) \
+	if (!is_pool(pool)) \
+		i_panic("Trying to use invalid memory pool, aborting")
+
+#define MEM_FREE_BIT 0x80000000
+
+#define is_mem_free(size) (((size) & MEM_FREE_BIT) != 0)
+#define mem_size(size) ((size) & ~MEM_FREE_BIT)
+
+#define mem2int(_mem, _int) \
+	memcpy(&(_int), ((unsigned char *) (_mem)), sizeof(int));
+
+#define int2mem(_mem, _int) \
+	memcpy(((unsigned char *) (_mem)), &(_int), sizeof(int));
+
+#ifdef POOL_SAVE_BLOCK
+#  define ALLOC_EXTRA_SIZE sizeof(PoolBlock *)
+#else
+#  define ALLOC_EXTRA_SIZE 0
+#endif
+
+/* max. number of bytes to even try to allocate. This is done just to avoid
+   allocating less memory than was actually requested because of integer
+   overflows. -128 is much more than is actually needed. */
+#define MAX_ALLOC_SIZE (UINT_MAX - 128)
+
+typedef struct {
+        /* total number of bytes in data[] */
+	unsigned int size;
+        /* free data position at data+free_index, none if >= size */
+	unsigned int free_index;
+        /* largest data[] size (not including int size) */
+        unsigned int largest_free_space;
+
+	/* unsigned int size - if the last bit is set the data block is used
+                               if 0, the rest of the block is free
+	   unsigned char data[]
+	   unsigned int size
+           ... */
+        unsigned char data[1];
+} PoolBlock;
+
+#define BLOCK_INCREMENT_COUNT 5
+typedef struct {
+	struct Pool pool;
+
+	unsigned short magic; /* 0xbeef */
+	int refcount;
+
+	int num_blocks, used_blocks;
+        int first_block_with_space; /* -1 = all used */
+	PoolBlock **blocks; /* num_blocks size */
+
+	char name[1]; /* human readable name for blocks - used in statistics.
+	                 variable size */
+} AllocfreePool;
+
+static struct Pool static_allocfree_pool;
+
+static void pool_allocfree_free(Pool pool, void *mem);
+
+static void pool_block_create(AllocfreePool *apool, unsigned int size)
+{
+	PoolBlock *block;
+        int alloc_size;
+
+	i_assert(size > sizeof(int));
+
+	if (apool->used_blocks >= apool->num_blocks) {
+                apool->num_blocks += BLOCK_INCREMENT_COUNT;
+
+		alloc_size = sizeof(PoolBlock *) * apool->num_blocks;
+		apool->blocks = apool->blocks == NULL ? malloc(alloc_size) :
+			realloc(apool->blocks, alloc_size);
+		if (apool->blocks == NULL) {
+			i_panic("pool_block_create(): "
+				"Out of memory when reallocating %d bytes",
+				alloc_size);
+		}
+	}
+
+        alloc_size = sizeof(PoolBlock)-1 + size;
+	block = malloc(alloc_size);
+	if (block == NULL) {
+		i_panic("pool_block_create(): "
+			"Out of memory when allocating %d bytes", alloc_size);
+	}
+
+	block->size = size;
+	block->free_index = 0;
+	block->largest_free_space = size-sizeof(int)-ALLOC_EXTRA_SIZE;
+        memset(block->data, 0, sizeof(int));
+
+	if (apool->first_block_with_space == -1)
+                apool->first_block_with_space = apool->used_blocks;
+	apool->blocks[apool->used_blocks] = block;
+	apool->used_blocks++;
+}
+
+static int pool_block_get_pos(AllocfreePool *apool, PoolBlock *block)
+{
+	int pos;
+
+	for (pos = 0; pos < apool->used_blocks; pos++) {
+		if (apool->blocks[pos] == block)
+			return pos;
+	}
+
+        return -1;
+}
+
+static void pool_reset_first_free_block(AllocfreePool *apool)
+{
+	int pos;
+
+	pos = apool->first_block_with_space;
+	i_assert(pos >= 0);
+
+	apool->first_block_with_space = -1;
+	for (; pos < apool->used_blocks; pos++) {
+		if (apool->blocks[pos]->largest_free_space > 0) {
+			apool->first_block_with_space = pos;
+                        break;
+		}
+	}
+}
+
+static void pool_block_destroy(AllocfreePool *apool, PoolBlock *block)
+{
+	int pos;
+
+        pos = pool_block_get_pos(apool, block);
+	if (pos == -1)
+		return;
+
+	memmove(apool->blocks+pos, apool->blocks+pos+1,
+		sizeof(PoolBlock *) * (apool->num_blocks-pos-1));
+        apool->used_blocks--;
+
+	if (apool->first_block_with_space > pos)
+		apool->first_block_with_space--;
+	else if (apool->first_block_with_space == pos)
+		pool_reset_first_free_block(apool);
+        free(block);
+}
+
+static PoolBlock *pool_block_find(AllocfreePool *apool, void *mem)
+{
+        PoolBlock *block;
+	int pos;
+
+#ifdef POOL_SAVE_BLOCK
+	memcpy(&block, (unsigned char *) mem - sizeof(PoolBlock *),
+	       sizeof(PoolBlock *));
+
+	for (pos = 0; pos < apool->used_blocks; pos++) {
+		if (apool->blocks[pos] == block)
+                        return block;
+	}
+#else
+
+	/* NOTE: this may be a portability problem, since it compares
+	   unrelated pointers. */
+	for (pos = 0; pos < apool->used_blocks; pos++) {
+                block = apool->blocks[pos];
+
+		if ((unsigned char *) mem >= block->data &&
+		    (unsigned char *) mem < block->data + block->size)
+                        return block;
+	}
+#endif
+
+        return NULL;
+}
+
+#ifdef POOL_DEBUG
+static void pool_block_dump(PoolBlock *block)
+{
+	unsigned char *mem;
+	unsigned int holesize, holesize_real;
+
+	printf("Block: size %d\n", block->size);
+
+	mem = block->data;
+	mem2int(mem, holesize);
+	while (holesize > 0) {
+		holesize_real = mem_size(holesize);
+		printf(" - %u %s\n", holesize_real,
+		       is_mem_free(holesize) ? " (free)" : "");
+
+		mem += holesize_real + sizeof(int);
+		if (mem-block->data == block->size)
+			break;
+
+		if (mem-block->data > block->size)
+			i_panic("pool_block_alloc() : corrupted pool");
+
+		mem2int(mem, holesize);
+	}
+}
+
+static void pool_dump(AllocfreePool *apool)
+{
+	int i;
+
+	printf("Dumping pool: %s\n", apool->name);
+
+	for (i = 0; i < apool->used_blocks; i++)
+                pool_block_dump(apool->blocks[i]);
+}
+#endif
+
+static void *pool_block_alloc(AllocfreePool *apool, PoolBlock *block,
+			      unsigned int size)
+{
+	unsigned char *mem, *alloc;
+	unsigned int largest, holesize, holesize_real, avail_size;
+        unsigned int alloc_index, next_free_index;
+
+#ifdef POOL_DEBUG
+        printf("pool_block_alloc(%s, %d)\n", apool->name, size);
+#endif
+
+        size += ALLOC_EXTRA_SIZE;
+
+	/* search for first large enough free space in the block.
+	   remember the largest free block so far so if we use the
+	   current largest, we don't have to scan the largest one
+	   from the beginning. */
+        largest = 0;
+	mem = block->data + block->free_index;
+	mem2int(mem, holesize);
+	while (holesize > 0) {
+		holesize_real = mem_size(holesize);
+		if (holesize_real + sizeof(int)*2 > block->size)
+			i_panic("pool_block_alloc() : corrupted pool");
+
+		if (is_mem_free(holesize)) {
+			if (size <= holesize_real)
+				break;
+
+			if (holesize_real-ALLOC_EXTRA_SIZE > largest)
+				largest = holesize_real-ALLOC_EXTRA_SIZE;
+		}
+
+		mem += holesize_real + sizeof(int);
+		mem2int(mem, holesize);
+	}
+
+	avail_size = mem_size(holesize);
+	if (avail_size == 0) {
+                /* rest of the block is free */
+		if ((int) (mem-block->data)+size >= block->size) {
+			i_panic("pool_block_alloc(): not enough space "
+				"in block (should have been)");
+		}
+
+		avail_size = block->size - (int) (mem-block->data) -
+			sizeof(int);
+	} else if (avail_size-ALLOC_EXTRA_SIZE != block->largest_free_space) {
+		/* we didn't use the largest free space in block,
+		   so we don't need to scan the rest of the data to
+		   search it */
+                largest = block->largest_free_space;
+	}
+
+	if (avail_size < size+sizeof(int)*3) {
+                /* don't bother leaving small holes in the pool */
+		size = avail_size;
+	}
+
+#ifdef POOL_DEBUG
+        if (size == avail_size)
+                printf(" - pool is now full\n");
+#endif
+
+        /* [i.....] -> [iXXi..] (i = space size, X = data, . = empty space) */
+	int2mem(mem, size);
+	alloc = mem + sizeof(int);
+	alloc_index = (int) (mem-block->data);
+
+#ifdef POOL_SAVE_BLOCK
+        memcpy(alloc, &block, sizeof(PoolBlock *));
+        alloc += sizeof(PoolBlock *);
+#endif
+
+        /* set mem point to beginning of next hole */
+	mem += size + sizeof(int);
+
+	if (size < avail_size) {
+                /* we didn't use the whole space, mark it unused */
+		avail_size = holesize == 0 ? 0 : MEM_FREE_BIT |
+			(avail_size - size - sizeof(int));
+		int2mem(mem, avail_size);
+		next_free_index = (int) (mem-block->data);
+	} else if (block->free_index == alloc_index) {
+		/* we used the first free hole, get the next one */
+		next_free_index = block->size;
+		while (mem < block->data+block->size) {
+			mem2int(mem, holesize);
+			if (holesize == 0) {
+				next_free_index = (int) (mem-block->data);
+				break;
+			}
+
+			holesize_real = mem_size(holesize);
+			if (is_mem_free(holesize)) {
+				next_free_index =
+					(int) (mem-block->data);
+				break;
+			}
+
+			mem += holesize_real + sizeof(int);
+		}
+	} else {
+		/* just to suppress compiler warnings */
+		next_free_index = 0;
+	}
+
+         /* update the first free space index */
+	if (block->free_index == alloc_index)
+		block->free_index = next_free_index;
+
+	if (holesize > 0 && largest < block->largest_free_space) {
+		/* we just used the largest space in the block,
+		   we'll need to find the next largest one and it
+		   could be after the space we just used */
+		while (mem < block->data+block->size) {
+			mem2int(mem, holesize);
+			if (holesize == 0)
+                                break;
+
+			holesize_real = mem_size(holesize);
+			if (holesize_real +
+			    sizeof(int)*2 > block->size)
+				i_panic("pool_block_alloc() : corrupted pool");
+
+			if (is_mem_free(holesize) &&
+			    holesize_real-ALLOC_EXTRA_SIZE > largest)
+				largest = holesize_real-ALLOC_EXTRA_SIZE;
+
+			mem += holesize_real + sizeof(int);
+		}
+	}
+
+	if (holesize == 0 && mem < block->data+block->size) {
+                /* rest of the block is free */
+		holesize = block->size - (int) (mem-block->data + sizeof(int));
+		if (holesize-ALLOC_EXTRA_SIZE > largest)
+			largest = holesize-ALLOC_EXTRA_SIZE;
+	}
+
+	block->largest_free_space = largest;
+	if (largest == 0)
+		pool_reset_first_free_block(apool);
+
+	return alloc;
+}
+
+/* Returns the previous block for "mem", or NULL if either there's no
+   previous block or if previous block is before any of the free blocks. */
+static unsigned char *
+pool_block_prev(PoolBlock *block, unsigned char *find_mem)
+{
+	unsigned char *mem, *next_mem;
+        unsigned int holesize;
+
+	if (block->free_index >= (unsigned int) (find_mem-block->data))
+		return NULL;
+
+	mem = block->data + block->free_index;
+	mem2int(mem, holesize);
+	while (holesize > 0) {
+		next_mem = mem + mem_size(holesize) + sizeof(int);
+		if (next_mem == find_mem)
+			return mem;
+
+                mem = next_mem;
+		mem2int(mem, holesize);
+	}
+
+        return NULL;
+}
+
+static void pool_block_free(AllocfreePool *apool, PoolBlock *block,
+			    unsigned char *mem)
+{
+	unsigned char *next_mem, *prev_mem;
+	unsigned int holesize, next_holesize, avail_space, prevsize;
+	unsigned int next_free_index;
+        int pos;
+
+#ifdef POOL_SAVE_BLOCK
+        mem -= sizeof(PoolBlock *);
+#endif
+        mem -= sizeof(int);
+	mem2int(mem, holesize);
+
+#ifdef POOL_DEBUG
+	printf("pool_block_free(%s, %u)\n", apool->name, mem_size(holesize));
+#endif
+
+	if ((holesize & MEM_FREE_BIT) != 0 ||
+	    mem_size(holesize) + sizeof(int) > block->size)
+		i_panic("pool_block_free() : corrupted pool");
+
+	if ((int) (mem-block->data) + holesize + sizeof(int) >= block->size) {
+                /* this is the last space in block */
+                holesize = 0;
+	} else {
+		next_mem = ((unsigned char *) mem) + holesize + sizeof(int);
+		mem2int(next_mem, next_holesize);
+
+		if (next_holesize == 0) {
+			/* last used space - combine to free space at end */
+                        holesize = 0;
+		} else if (!is_mem_free(next_holesize)) {
+			/* mark the space free */
+			holesize |= MEM_FREE_BIT;
+		} else {
+			/* combine the two free spaces */
+			holesize = (mem_size(holesize) + sizeof(int) +
+				    mem_size(next_holesize)) | MEM_FREE_BIT;
+		}
+	}
+	int2mem(mem, holesize);
+
+        /* if previous block is free, we can combine the free space */
+	prev_mem = pool_block_prev(block, mem);
+	if (prev_mem != NULL) {
+		mem2int(prev_mem, prevsize);
+		if (is_mem_free(prevsize)) {
+			if (holesize != 0) {
+				holesize = (mem_size(holesize) + sizeof(int) +
+					    mem_size(prevsize)) | MEM_FREE_BIT;
+			}
+			int2mem(prev_mem, holesize);
+                        mem = prev_mem;
+		}
+	}
+
+        /* update largest free space size */
+	avail_space = holesize != 0 ? mem_size(holesize) :
+		block->size - (int) (mem-block->data + sizeof(int));
+
+#ifdef POOL_FREE_CLEAR
+	memset(mem + sizeof(int), POOL_FREE_CLEAR, avail_space);
+#elif defined (POOL_SAVE_BLOCK)
+	memset(mem + sizeof(int), 0, sizeof(PoolBlock *));
+#endif
+
+	if (block->largest_free_space < avail_space-ALLOC_EXTRA_SIZE)
+		block->largest_free_space = avail_space-ALLOC_EXTRA_SIZE;
+
+        /* update the first free space index */
+        next_free_index = (int) (mem-block->data);
+	if (block->free_index > next_free_index)
+		block->free_index = next_free_index;
+
+        /* update pool's first block with free space index */
+	pos = pool_block_get_pos(apool, block);
+	if (apool->first_block_with_space < 0 ||
+	    pos < apool->first_block_with_space)
+		apool->first_block_with_space = pos;
+
+	if (holesize == 0 && mem == block->data) {
+		/* FIXME: the block is completely unused, if there's more
+		   than two empty blocks, free them */
+	}
+}
+
+Pool pool_allocfree_create(const char *name, unsigned int size)
+{
+        AllocfreePool *apool;
+
+	i_assert(size > sizeof(int));
+
+	apool = calloc(sizeof(AllocfreePool) + strlen(name), 1);
+	if (apool == NULL)
+		i_panic("pool_create(): Out of memory");
+
+	apool->pool = static_allocfree_pool;
+	apool->magic = 0xbeef;
+        apool->refcount = 1;
+
+        apool->first_block_with_space = -1;
+	pool_block_create(apool, nearest_power(size));
+
+	strcpy(apool->name, name);
+	return &apool->pool;
+}
+
+#ifdef POOL_CHECK_LEAKS
+static const char *get_leak_string(const unsigned char *mem, int size)
+{
+	int i;
+
+	mem += sizeof(int);
+
+#ifdef POOL_SAVE_BLOCK
+        mem += sizeof(PoolBlock *);
+        size -= sizeof(PoolBlock *);
+#endif
+
+	for (i = 0; i < size; i++) {
+		if (mem[i] == '\0')
+			return (const char *) mem;
+
+		if ((mem[i] & 0x7f) < 32)
+                        break;
+	}
+
+        return NULL;
+}
+
+static const char *pool_block_count_leaks(PoolBlock *block, int *leak_count,
+					  int *leak_size)
+{
+        const char *leak_string;
+	unsigned char *mem;
+	unsigned int holesize, holesize_real;
+
+        leak_string = NULL;
+
+	mem = block->data;
+	mem2int(mem, holesize);
+	while (holesize > 0) {
+		holesize_real = mem_size(holesize);
+		if (!is_mem_free(holesize)) {
+			(*leak_count)++;
+			*leak_size += holesize_real;
+
+			if (leak_string == NULL) {
+				leak_string = get_leak_string(mem,
+							      holesize_real);
+			}
+		}
+
+		if (holesize_real + sizeof(int)*2 > block->size)
+			i_panic("pool_block_count_leaks() : corrupted pool");
+
+		mem += holesize_real + sizeof(int);
+		mem2int(mem, holesize);
+	}
+
+        return leak_string;
+}
+
+static void pool_check_leaks(AllocfreePool *apool)
+{
+        const char *leak_string;
+	int i, leak_count, leak_size;
+
+        leak_string = NULL;
+        leak_count = leak_size = 0;
+	for (i = 0; i < apool->used_blocks; i++) {
+                PoolBlock *block = apool->blocks[i];
+
+		if (block->free_index < block->size) {
+			leak_string = pool_block_count_leaks(block, &leak_count,
+							     &leak_size);
+		}
+	}
+
+	if (leak_count > 0) {
+		i_warning("Pool '%s' leaked %d allocs with "
+			  "total size of %d (%s)",
+			  apool->name, leak_count, leak_size,
+			  leak_string == NULL ? "" : leak_string);
+	}
+}
+#endif
+
+static void pool_destroy(AllocfreePool *apool)
+{
+        check_pool(apool);
+
+#ifdef POOL_CHECK_LEAKS
+        pool_check_leaks(apool);
+#endif
+
+	while (apool->used_blocks > 0)
+		pool_block_destroy(apool, apool->blocks[0]);
+	free(apool->blocks);
+        free(apool->name);
+	free(apool);
+}
+
+static void pool_allocfree_ref(Pool pool)
+{
+        AllocfreePool *apool = (AllocfreePool *) pool;
+
+	apool->refcount++;
+}
+
+static void pool_allocfree_unref(Pool pool)
+{
+        AllocfreePool *apool = (AllocfreePool *) pool;
+
+	if (--apool->refcount == 0)
+                pool_destroy(apool);
+}
+
+static void *pool_allocfree_malloc(Pool pool, unsigned int size)
+{
+        AllocfreePool *apool = (AllocfreePool *) pool;
+        PoolBlock *block;
+	void *mem;
+        int i, allocsize;
+
+	if (size == 0)
+		return NULL;
+
+	if (size > MAX_ALLOC_SIZE)
+		i_panic("Trying to allocate too much memory");
+
+	/* allocate only aligned amount of memory so alignment comes
+	   always properly */
+	size = (size + MEM_ALIGN-1) & ~(MEM_ALIGN-1);
+
+        check_pool(apool);
+
+	/* check if there's enough space in one of the existing blocks */
+        block = NULL;
+	for (i = 0; i < apool->used_blocks; i++) {
+		if (apool->blocks[i]->largest_free_space >= size) {
+                        block = apool->blocks[i];
+                        break;
+		}
+	}
+
+	if (block == NULL) {
+		/* create new block to pool */
+		allocsize = 2*apool->blocks[apool->used_blocks-1]->size;
+		if (allocsize-sizeof(int)-ALLOC_EXTRA_SIZE < size)
+			allocsize = size*2 + sizeof(int) + ALLOC_EXTRA_SIZE;
+
+		pool_block_create(apool, nearest_power(allocsize));
+                block = apool->blocks[apool->used_blocks-1];
+	}
+
+	mem = pool_block_alloc(apool, block, size);
+	memset(mem, 0, size);
+
+#ifdef POOL_DEBUG
+	pool_dump(apool);
+#endif
+        return mem;
+}
+
+static void *pool_allocfree_realloc(Pool pool, void *mem, unsigned int size)
+{
+        AllocfreePool *apool = (AllocfreePool *) pool;
+        unsigned char *mem_size_pos, *oldmem;
+	unsigned int memsize;
+
+	if (size == 0) {
+		pool_allocfree_free(pool, mem);
+		return NULL;
+	}
+
+	if (mem == NULL)
+                return pool_allocfree_malloc(pool, size);
+
+        check_pool(apool);
+
+        mem_size_pos = (unsigned char *) mem - sizeof(int);
+#ifdef POOL_SAVE_BLOCK
+	mem_size_pos -= sizeof(PoolBlock *);
+#endif
+
+        mem2int(mem_size_pos, memsize);
+	if (memsize == size)
+                return mem;
+
+	/* FIXME: shrinking could be done more efficiently, also growing
+	   might be able to check if it can extend it's current allocation */
+        oldmem = mem;
+	mem = pool_allocfree_malloc(pool, size);
+	memcpy(mem, oldmem, memsize < size ? memsize : size);
+        pool_allocfree_free(pool, oldmem);
+
+#ifdef POOL_DEBUG
+	pool_dump(apool);
+#endif
+
+	if (size > memsize)
+		memset((char *) mem + memsize, 0, size-memsize);
+        return mem;
+}
+
+static void *pool_allocfree_realloc_min(Pool pool, void *mem,
+					unsigned int size)
+{
+        unsigned char *mem_size_pos;
+	unsigned int memsize;
+
+	if (mem == NULL)
+                return pool_allocfree_malloc(pool, size);
+
+        mem_size_pos = (unsigned char *) mem - sizeof(int);
+#ifdef POOL_SAVE_BLOCK
+	mem_size_pos -= sizeof(PoolBlock *);
+#endif
+
+	mem2int(mem_size_pos, memsize);
+	if (size <= memsize)
+                return mem;
+
+	return pool_allocfree_realloc(pool, mem, size);
+}
+
+static void pool_allocfree_free(Pool pool, void *mem)
+{
+        AllocfreePool *apool = (AllocfreePool *) pool;
+	PoolBlock *block;
+
+        if (mem == NULL)
+                return;
+
+        check_pool(apool);
+
+	block = pool_block_find(apool, mem);
+	if (block == NULL)
+		i_panic("pool_allocfree_free(): invalid memory address");
+
+        pool_block_free(apool, block, mem);
+
+#ifdef POOL_DEBUG
+	pool_dump(apool);
+#endif
+}
+
+static void pool_allocfree_clear(Pool pool)
+{
+        AllocfreePool *apool = (AllocfreePool *) pool;
+	int i;
+
+	apool->first_block_with_space = 0;
+
+	for (i = 0; i < apool->used_blocks; i++) {
+		PoolBlock *block = apool->blocks[i];
+
+		block->free_index = 0;
+		block->largest_free_space = block->size -
+			sizeof(int) - ALLOC_EXTRA_SIZE;
+		memset(block->data, 0, sizeof(int));
+	}
+}
+
+static struct Pool static_allocfree_pool = {
+	pool_allocfree_ref,
+	pool_allocfree_unref,
+
+	pool_allocfree_malloc,
+	pool_allocfree_free,
+
+	pool_allocfree_realloc,
+	pool_allocfree_realloc_min,
+
+	pool_allocfree_clear
+};
+
+#ifdef POOL_DEBUG
+#include <stdlib.h>
+
+void mempool_test(void)
+{
+	Pool p;
+        void *arr[100];
+	int i, j;
+
+	memset(arr, 0, sizeof(arr));
+
+	p = pool_create("temp", 32);
+	for (i = 0; i < 10000; i++) {
+		arr[rand()%100] = p_malloc(p, 4*(rand()%10+1));
+
+		if (rand()%3 == 1) {
+			for (j = 0; j < 100; j++) {
+				if (arr[j] != NULL)
+					p_free_and_null(p, arr[j]);
+			}
+		}
+	}
+}
+#endif