#include "config.h"
#include "system.h"
#include "alloc-pool.h"
#include "hashtab.h"
#define align_eight(x) (((x+7) >> 3) << 3)
typedef struct allocation_object_def
{
#ifdef ENABLE_CHECKING
ALLOC_POOL_ID_TYPE id;
#endif
union
{
char data[1];
char *align_p;
HOST_WIDEST_INT align_i;
long double align_ld;
} u;
} allocation_object;
#define ALLOCATION_OBJECT_PTR_FROM_USER_PTR(X) \
((allocation_object *) (((char *) (X)) \
- offsetof (allocation_object, u.data)))
#define USER_PTR_FROM_ALLOCATION_OBJECT_PTR(X) \
((void *) (((allocation_object *) (X))->u.data))
#ifdef ENABLE_CHECKING
static ALLOC_POOL_ID_TYPE last_id;
#endif
#ifdef GATHER_STATISTICS
struct alloc_pool_descriptor
{
const char *name;
int allocated;
int created;
int peak;
int current;
};
static htab_t alloc_pool_hash;
static hashval_t
hash_descriptor (const void *p)
{
const struct alloc_pool_descriptor *d = p;
return htab_hash_pointer (d->name);
}
static int
eq_descriptor (const void *p1, const void *p2)
{
const struct alloc_pool_descriptor *d = p1;
return d->name == p2;
}
static struct alloc_pool_descriptor *
alloc_pool_descriptor (const char *name)
{
struct alloc_pool_descriptor **slot;
if (!alloc_pool_hash)
alloc_pool_hash = htab_create (10, hash_descriptor, eq_descriptor, NULL);
slot = (struct alloc_pool_descriptor **)
htab_find_slot_with_hash (alloc_pool_hash, name,
htab_hash_pointer (name),
1);
if (*slot)
return *slot;
*slot = xcalloc (sizeof (**slot), 1);
(*slot)->name = name;
return *slot;
}
#endif
alloc_pool
create_alloc_pool (const char *name, size_t size, size_t num)
{
alloc_pool pool;
size_t pool_size, header_size;
#ifdef GATHER_STATISTICS
struct alloc_pool_descriptor *desc;
#endif
gcc_assert (name);
if (size < sizeof (alloc_pool_list))
size = sizeof (alloc_pool_list);
size = align_eight (size);
#ifdef ENABLE_CHECKING
size += offsetof (allocation_object, u.data);
#endif
gcc_assert (num);
pool_size = sizeof (struct alloc_pool_def);
pool = xmalloc (pool_size);
pool->name = name;
#ifdef GATHER_STATISTICS
desc = alloc_pool_descriptor (name);
desc->created++;
#endif
pool->elt_size = size;
pool->elts_per_block = num;
header_size = align_eight (sizeof (struct alloc_pool_list_def));
pool->block_size = (size * num) + header_size;
pool->free_list = NULL;
pool->elts_allocated = 0;
pool->elts_free = 0;
pool->blocks_allocated = 0;
pool->block_list = NULL;
#ifdef ENABLE_CHECKING
last_id++;
if (last_id == 0)
last_id++;
pool->id = last_id;
#endif
return (pool);
}
void
free_alloc_pool (alloc_pool pool)
{
alloc_pool_list block, next_block;
#ifdef GATHER_STATISTICS
struct alloc_pool_descriptor *desc = alloc_pool_descriptor (pool->name);
#endif
gcc_assert (pool);
for (block = pool->block_list; block != NULL; block = next_block)
{
next_block = block->next;
free (block);
#ifdef GATHER_STATISTICS
desc->current -= pool->block_size;
#endif
}
#ifdef ENABLE_CHECKING
memset (pool, 0xaf, sizeof (*pool));
#endif
free (pool);
}
void
free_alloc_pool_if_empty (alloc_pool *pool)
{
if ((*pool)->elts_free == (*pool)->elts_allocated)
{
free_alloc_pool (*pool);
*pool = NULL;
}
}
void *
pool_alloc (alloc_pool pool)
{
alloc_pool_list header;
char *block;
#ifdef GATHER_STATISTICS
struct alloc_pool_descriptor *desc = alloc_pool_descriptor (pool->name);
desc->allocated+=pool->elt_size;
#endif
gcc_assert (pool);
if (!pool->free_list)
{
size_t i;
alloc_pool_list block_header;
block = XNEWVEC (char, pool->block_size);
block_header = (alloc_pool_list) block;
block += align_eight (sizeof (struct alloc_pool_list_def));
#ifdef GATHER_STATISTICS
desc->current += pool->block_size;
if (desc->peak < desc->current)
desc->peak = desc->current;
#endif
block_header->next = pool->block_list;
pool->block_list = block_header;
for (i = 0; i < pool->elts_per_block; i++, block += pool->elt_size)
{
#ifdef ENABLE_CHECKING
((allocation_object *) block)->id = 0;
#endif
header = (alloc_pool_list) USER_PTR_FROM_ALLOCATION_OBJECT_PTR (block);
header->next = pool->free_list;
pool->free_list = header;
}
pool->elts_allocated += pool->elts_per_block;
pool->elts_free += pool->elts_per_block;
pool->blocks_allocated += 1;
}
header = pool->free_list;
pool->free_list = header->next;
pool->elts_free--;
#ifdef ENABLE_CHECKING
ALLOCATION_OBJECT_PTR_FROM_USER_PTR (header)->id = pool->id;
#endif
return ((void *) header);
}
void
pool_free (alloc_pool pool, void *ptr)
{
alloc_pool_list header;
gcc_assert (ptr);
#ifdef ENABLE_CHECKING
memset (ptr, 0xaf, pool->elt_size - offsetof (allocation_object, u.data));
gcc_assert (pool->id == ALLOCATION_OBJECT_PTR_FROM_USER_PTR (ptr)->id);
ALLOCATION_OBJECT_PTR_FROM_USER_PTR (ptr)->id = 0;
#else
gcc_assert (pool->elts_free < pool->elts_allocated);
#endif
header = (alloc_pool_list) ptr;
header->next = pool->free_list;
pool->free_list = header;
pool->elts_free++;
}
#ifdef GATHER_STATISTICS
struct output_info
{
int count;
int size;
};
static int
print_statistics (void **slot, void *b)
{
struct alloc_pool_descriptor *d = (struct alloc_pool_descriptor *) *slot;
struct output_info *i = (struct output_info *) b;
if (d->allocated)
{
fprintf (stderr, "%-21s %6d %10d %10d %10d\n", d->name,
d->created, d->allocated, d->peak, d->current);
i->size += d->allocated;
i->count += d->created;
}
return 1;
}
#endif
void
dump_alloc_pool_statistics (void)
{
#ifdef GATHER_STATISTICS
struct output_info info;
if (!alloc_pool_hash)
return;
fprintf (stderr, "\nAlloc-pool Kind Pools Allocated Peak Leak\n");
fprintf (stderr, "-------------------------------------------------------------\n");
info.count = 0;
info.size = 0;
htab_traverse (alloc_pool_hash, print_statistics, &info);
fprintf (stderr, "-------------------------------------------------------------\n");
fprintf (stderr, "%-20s %7d %10d\n",
"Total", info.count, info.size);
fprintf (stderr, "-------------------------------------------------------------\n");
#endif
}