You can use a block list and a pair of buckets of various sizes.
So:
typedef struct { union{ SLIST_ENTRY entry; void* list; }; byte mem[]; } mem_block; typedef struct { SLIST_HEADER root; } mem_block_list; #define BUCKET_COUNT 4 #define BLOCKS_TO_ALLOCATE 16 static mem_block_list Buckets[BUCKET_COUNT]; void init_buckets() { for( int i = 0; i < BUCKET_COUNT; ++i ) { InitializeSListHead( &Buckets[i].root ); for( int j = 0; j < BLOCKS_TO_ALLOCATE; ++j ) { mem_block* p = (mem_block*) malloc( sizeof( mem_block ) + (0x1 << BUCKET_COUNT) * 0x8 ); InterlockedPushEntrySList( &Buckets[i].root, &p->entry ); } } } void* balloc( size_t size ) { for( int i = 0; i < BUCKET_COUNT; ++i ) { if( size <= (0x1 << i) * 0x8 ) { mem_block* p = (mem_block*) InterlockedPopEntrySList( &Buckets[i].root ); p->list = &Buckets[i]; } } return 0; // block to large } void bfree( void* p ) { mem_block* block = (mem_block*) (((byte*)p) - sizeof( block->entry )); InterlockedPushEntrySList( ((mem_block_list*)block)->root, &block->entry ); }
SLIST_ENTRY, InterlockedPushEntrySList, InterlockedPopEntrySList, InitializeSListHead - these are functions for operations with unprotected singly linked lists under Win32. Use the appropriate operations for other operating systems.
Disadvantages:
sizeof( SLIST_ENTRY ) overhead sizeof( SLIST_ENTRY )- Buckets can only grow once at the beginning, after which you can run out of memory and ask for OS / other buckets. (Other buckets lead to fragmentation)
- This example is too simple and needs to be expanded to handle more cases.
Christopher
source share