I'm learning C and I am currently implementing a memory pool.
I'm writing it in steps, first I implemented a fixed-sized memory pool then I will try to implement a memory pool with a known size of elements but an unknown number of elements and ultimately try to write a generic one that is just a block of memory that can allocate any size.
The pool is actually a block of memory that is initialized with values that are pointers to the next memory like so:
______________
|0x8 |0x10|NULL|
|____|____|____|
^ ^ ^
0x0 0x8 0x10
I want to know if I'm heading in the right direction. There is the code
allocator.h:
#include <stdlib.h>
typedef struct FixedPoolAllocator FixedPoolAllocator;
FixedPoolAllocator* FixedPoolAllocator_new(size_t nmemb, size_t size);
void* FixedPoolAllocator_alloc(FixedPoolAllocator *allocator);
void FixedPoolAllocator_free(FixedPoolAllocator *allocator, void *ptr);
void FixedPoolAllocator_destroy(FixedPoolAllocator *allocator);
allocator.c:
#include "allocator.h"
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
typedef struct FixedPoolAllocator {
// The currently free space in which a value can be stored
size_t *currentFree;
size_t numOfMembers;
size_t memberSize;
// The actual memory block, It is of type char* so that pointer arithmetic will be easier
char *mem;
} FixedPoolAllocator;
FixedPoolAllocator* FixedPoolAllocator_new(size_t nmemb, size_t size)
{
if (0 == size || 0 == nmemb) return NULL;
FixedPoolAllocator *ret = malloc(sizeof *ret);
if (NULL == ret) return NULL;
// Make sure that the size of each member is atleast size_t so that the members can store a memory address
size_t member_size = MAX(sizeof(size_t), size);
ret->mem = malloc(nmemb * member_size);
if (NULL == ret->mem) return NULL;
// The initial free member
ret->currentFree = (size_t*)ret->mem;
ret->numOfMembers = nmemb;
ret->memberSize = size;
size_t *temp = ret->currentFree;
// Assign each member with the address of the next member
size_t i;
for (i = 0; i < nmemb - 1; i++) {
*temp = (size_t) (ret->mem + (i+1) * member_size);
temp = (size_t*) ((char*)temp + member_size);
}
// The last member points to NULL
*temp = (size_t)NULL;
return ret;
}
void* FixedPoolAllocator_alloc(FixedPoolAllocator *allocator)
{
if (NULL == allocator || NULL == allocator->currentFree) return NULL;
// Return the currently free member and update currentFree to the next member in the list
void *ret = allocator->currentFree;
allocator->currentFree = (size_t*)*allocator->currentFree;
return ret;
}
void FixedPoolAllocator_free(FixedPoolAllocator *allocator, void *ptr)
{
size_t ptr_as_value = (size_t)ptr;
// Assign the pointed value with the current free member
*(size_t*)ptr = (size_t)allocator->currentFree;
// Update the currenty free member to be the one that was just freed
allocator->currentFree = ptr;
}
void FixedPoolAllocator_destroy(FixedPoolAllocator *allocator)
{
free(allocator->mem);
free(allocator);
}
size == 0withif (0 == size || 0 == nmemb) return NULL;versusif (0 == nmemb) return NULL;. I see nothing in code that that prevents use ofsize == 0. \$\endgroup\$