Here is code for a simple suballocator in an OOP-ish version of C that I call
Instance Model Programming.
The full parser code that this is a part of can be found
here.
It is quite efficient for allocating large numbers of small variable sized things like strings or structs. The big overhead in malloc/free occurs when lots of freeing is done. This reduces that overhead in the case where fine grained freeing is not needed.
/*************************************************************************
memory.h
*************************************************************************/
#ifndef _MEMORY_H
#define _MEMORY_H
#define PRIVATE static
#define PUBLIC
#define INSTANCE_METHOD static
// ------------------------------------------------------------------------
// memory_t
//
// A pseudo-class to avoid global function names and enable initialization.
// reentrant.
// ------------------------------------------------------------------------
#undef SELF
#define SELF struct _memory
typedef struct _memory
{
void *memory;
void *first_chunk;
int start;
int end;
int chunk;
void * (*allocate) ( SELF *id, int size );
void * (*save) ( SELF *id, void *, int );
void (*release) ( SELF *id );
} memory_t;
#define allocate(self,a) (*self).allocate(self,a)
#define save(self,a,b) (*self).save(self,a,b)
#define release(self) (*self).release(self)
PUBLIC
void
InitializeMemory ( memory_t *memory );
#endif
/*************************************************************************
memory.c
*************************************************************************/
#include
#include
#include
#include "memory.h"
// -----------------------------------------------------------------------
// set_new_chunk
//
// allocate another chunk of memory to the struct, zeroed out
// -----------------------------------------------------------------------
PRIVATE
void
set_new_chunk ( memory_t *d )
{
void **next,
**last = d->memory;
int chunk = d->chunk;
size_t size = sizeof ( double );
next = calloc ( (chunk / size), size ); // double boundary
if ( ! next ) {
puts ("memory_t: out of memory");
exit(1);
}
d->start = sizeof ( void * ); // leave room for "next" pointer
d->memory = next;
*last = next; // point to this chunk
}
// -----------------------------------------------------------------------
// allocate
//
// return pointer to allocated memory of length size, zeroed out
// -----------------------------------------------------------------------
INSTANCE_METHOD
void *
(allocate) ( memory_t *d,
int size )
{
void *new_item;
if ( size > d->chunk - sizeof ( void * ) ) {
printf ( "memory_t: max memory chunk size is %d bytes\n",
d->chunk - sizeof ( void * ) );
exit(1);
}
if ( d->start + size > d->end ) {
set_new_chunk ( d );
}
new_item = (char *) d->memory + d->start;
d->start += size;
return new_item;
}
// -----------------------------------------------------------------------
// save
//
// save item of length size in memory, return pointer to it
// -----------------------------------------------------------------------
INSTANCE_METHOD
void *
(save) ( memory_t *d,
void *item,
int size )
{
void *new_item;
if ( size > d->chunk - sizeof ( void * ) ) {
printf ( "memory_t: max memory chunk size is %d bytes\n",
d->chunk - sizeof ( void * ) );
exit(1);
}
if ( d->start + size > d->end ) {
set_new_chunk ( d );
}
new_item = (char *) d->memory + d->start;
memcpy ( new_item, item, size );
d->start += size;
return new_item;
}
// -----------------------------------------------------------------------
// release
// -----------------------------------------------------------------------
INSTANCE_METHOD
void
(release) ( memory_t *d )
{
register
void **next = d->first_chunk;
void *memory;
while ( next ) {
memory = next;
next = *next;
free ( memory );
}
}
// -----------------------------------------------------------------------
// InitializeMemory
// -----------------------------------------------------------------------
PUBLIC
void
InitializeMemory ( register
memory_t *memory )
{
register
void **next;
size_t size = sizeof ( double );
int chunk = 64 * 1024;
memory->start = sizeof ( void * ); // next pointer is first bytes
memory->chunk = chunk;
memory->end = chunk - 1;
next = calloc ( (chunk / size), size ); // double boundary
if ( ! next ) {
puts ("memory_t: no memory available");
exit(1);
}
memory->first_chunk = next;
memory->memory = next;
memory->allocate = allocate;
memory->save = save;
memory->release = release;
}