mempool-alloconly.c revision 0881f33a9a8a8e2f0623fad250d9813906fd24ce
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk/* Copyright (c) 2002-2003 Timo Sirainen */
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk/* @UNSAFE: whole file */
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#include "lib.h"
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#include "mempool.h"
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#include <stdlib.h>
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#ifdef HAVE_GC_GC_H
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk# include <gc/gc.h>
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#elif defined (HAVE_GC_H)
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk# include <gc.h>
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#endif
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#define MAX_ALLOC_SIZE SSIZE_T_MAX
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstruct alloconly_pool {
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk struct pool pool;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk int refcount;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk size_t base_size;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk struct pool_block *block;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#ifdef DEBUG
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk const char *name;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#endif
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk};
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstruct pool_block {
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk struct pool_block *prev;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk size_t size;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk size_t left;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk size_t last_alloc_size;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk /* unsigned char data[]; */
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk};
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#define SIZEOF_POOLBLOCK (MEM_ALIGN(sizeof(struct pool_block)))
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#define POOL_BLOCK_DATA(block) \
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk ((char *) (block) + SIZEOF_POOLBLOCK)
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstatic const char *pool_alloconly_get_name(pool_t pool);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstatic void pool_alloconly_ref(pool_t pool);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstatic void pool_alloconly_unref(pool_t pool);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstatic void *pool_alloconly_malloc(pool_t pool, size_t size);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstatic void pool_alloconly_free(pool_t pool, void *mem);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstatic void *pool_alloconly_realloc(pool_t pool, void *mem,
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk size_t old_size, size_t new_size);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstatic void pool_alloconly_clear(pool_t pool);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstatic size_t pool_alloconly_get_max_easy_alloc_size(pool_t pool);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstatic void block_alloc(struct alloconly_pool *pool, size_t size);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstatic struct pool static_alloconly_pool = {
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk pool_alloconly_get_name,
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk pool_alloconly_ref,
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk pool_alloconly_unref,
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk pool_alloconly_malloc,
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk pool_alloconly_free,
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk pool_alloconly_realloc,
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk pool_alloconly_clear,
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk pool_alloconly_get_max_easy_alloc_size,
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk TRUE,
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk FALSE
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk};
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkpool_t pool_alloconly_create(const char *name, size_t size)
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk{
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk struct alloconly_pool apool, *new_apool;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk size_t min_alloc = sizeof(struct alloconly_pool) + SIZEOF_POOLBLOCK;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#ifdef DEBUG
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk min_alloc += strlen(name) + 1;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#endif
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk /* create a fake alloconly_pool so we can call block_alloc() */
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk memset(&apool, 0, sizeof(apool));
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk apool.pool = static_alloconly_pool;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk apool.refcount = 1;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk if (size < min_alloc)
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk size = nearest_power(size + min_alloc);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk block_alloc(&apool, size);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk /* now allocate the actual alloconly_pool from the created block */
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk new_apool = p_new(&apool.pool, struct alloconly_pool, 1);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk *new_apool = apool;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#ifdef DEBUG
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk new_apool->name = p_strdup(&new_apool->pool, name);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#endif
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk /* set base_size so p_clear() doesn't trash alloconly_pool structure. */
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk new_apool->base_size = new_apool->block->size - new_apool->block->left;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk new_apool->block->last_alloc_size = 0;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk return &new_apool->pool;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk}
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstatic void pool_alloconly_destroy(struct alloconly_pool *apool)
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk{
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk void *block;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk /* destroy all but the last block */
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk pool_alloconly_clear(&apool->pool);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk /* destroy the last block */
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk block = apool->block;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#ifdef DEBUG
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk memset(block, 0xde, SIZEOF_POOLBLOCK + apool->block->size);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#endif
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#ifndef USE_GC
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk free(block);
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#endif
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk}
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volkstatic const char *pool_alloconly_get_name(pool_t pool)
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk{
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk struct alloconly_pool *apool = (struct alloconly_pool *) pool;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#ifdef DEBUG
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk return apool->name;
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#else
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk return "alloconly";
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk#endif
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk}
42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0Pascal Volk
static void pool_alloconly_ref(pool_t pool)
{
struct alloconly_pool *apool = (struct alloconly_pool *) pool;
apool->refcount++;
}
static void pool_alloconly_unref(pool_t pool)
{
struct alloconly_pool *apool = (struct alloconly_pool *) pool;
if (--apool->refcount == 0)
pool_alloconly_destroy(apool);
}
static void block_alloc(struct alloconly_pool *apool, size_t size)
{
struct pool_block *block;
i_assert(size > SIZEOF_POOLBLOCK);
if (apool->block != NULL) {
/* each block is at least twice the size of the previous one */
if (size <= apool->block->size)
size += apool->block->size;
size = nearest_power(size);
#ifdef DEBUG
i_warning("Growing pool '%s' with: %"PRIuSIZE_T,
apool->name, size);
#endif
}
#ifndef USE_GC
block = calloc(size, 1);
#else
block = GC_malloc(size);
memset(block, 0, size);
#endif
if (block == NULL)
i_fatal_status(FATAL_OUTOFMEM, "block_alloc(): Out of memory");
block->prev = apool->block;
apool->block = block;
block->size = size - SIZEOF_POOLBLOCK;
block->left = block->size;
}
static void *pool_alloconly_malloc(pool_t pool, size_t size)
{
struct alloconly_pool *apool = (struct alloconly_pool *) pool;
void *mem;
if (size == 0 || size > SSIZE_T_MAX)
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", size);
size = MEM_ALIGN(size);
if (apool->block->left < size) {
/* we need a new block */
block_alloc(apool, size + SIZEOF_POOLBLOCK);
}
mem = POOL_BLOCK_DATA(apool->block) +
(apool->block->size - apool->block->left);
apool->block->left -= size;
apool->block->last_alloc_size = size;
return mem;
}
static void pool_alloconly_free(pool_t pool, void *mem)
{
struct alloconly_pool *apool = (struct alloconly_pool *) pool;
/* we can free only the last allocation */
if (POOL_BLOCK_DATA(apool->block) +
(apool->block->size - apool->block->left -
apool->block->last_alloc_size) == mem) {
memset(mem, 0, apool->block->last_alloc_size);
apool->block->left += apool->block->last_alloc_size;
apool->block->last_alloc_size = 0;
}
}
static int pool_try_grow(struct alloconly_pool *apool, void *mem, size_t size)
{
/* see if we want to grow the memory we allocated last */
if (POOL_BLOCK_DATA(apool->block) +
(apool->block->size - apool->block->left -
apool->block->last_alloc_size) == mem) {
/* yeah, see if we can grow */
if (apool->block->left >= size-apool->block->last_alloc_size) {
/* just shrink the available size */
apool->block->left -=
size - apool->block->last_alloc_size;
apool->block->last_alloc_size = size;
return TRUE;
}
}
return FALSE;
}
static void *pool_alloconly_realloc(pool_t pool, void *mem,
size_t old_size, size_t new_size)
{
struct alloconly_pool *apool = (struct alloconly_pool *) pool;
unsigned char *new_mem;
if (new_size == 0 || new_size > SSIZE_T_MAX)
i_panic("Trying to allocate %"PRIuSIZE_T" bytes", new_size);
if (mem == NULL)
return pool_alloconly_malloc(pool, new_size);
if (new_size <= old_size)
return mem;
new_size = MEM_ALIGN(new_size);
/* see if we can directly grow it */
if (!pool_try_grow(apool, mem, new_size)) {
/* slow way - allocate + copy */
new_mem = pool_alloconly_malloc(pool, new_size);
memcpy(new_mem, mem, old_size);
mem = new_mem;
}
return mem;
}
static void pool_alloconly_clear(pool_t pool)
{
struct alloconly_pool *apool = (struct alloconly_pool *) pool;
struct pool_block *block;
size_t avail_size;
/* destroy all blocks but the oldest, which contains the
struct alloconly_pool allocation. */
while (apool->block->prev != NULL) {
block = apool->block;
apool->block = block->prev;
#ifdef DEBUG
memset(block, 0xde, SIZEOF_POOLBLOCK + block->size);
#endif
#ifndef USE_GC
free(block);
#endif
}
/* clear the first block */
avail_size = apool->block->size - apool->base_size;
memset(PTR_OFFSET(POOL_BLOCK_DATA(apool->block), apool->base_size), 0,
avail_size - apool->block->left);
apool->block->left = avail_size;
apool->block->last_alloc_size = 0;
}
static size_t pool_alloconly_get_max_easy_alloc_size(pool_t pool)
{
struct alloconly_pool *apool = (struct alloconly_pool *) pool;
return apool->block->left;
}