CPlay/poolsclosed/pool.c
2025-08-12 03:25:05 +02:00

126 lines
3.1 KiB
C

#include <malloc.h>
#include <stdbool.h>
#include <string.h>
/* See: https://8dcc.github.io/programming/pool-allocator.html */
/* See: https://github.com/8dcc/libpool */
#define CHUNK_SIZE 64
typedef union Chunk Chunk;
union Chunk {
Chunk *next; // When not null, it is used
char arr[CHUNK_SIZE];
};
typedef struct LinkedPtr LinkedPtr;
struct LinkedPtr {
Chunk *ptr;
LinkedPtr *next;
};
typedef struct Pool Pool;
struct Pool {
Chunk *free_chunk; // Pointer to a linked list of free chunks
LinkedPtr *array_starts;
};
Pool *pool_new(size_t pool_size) {
Pool *pool = malloc(sizeof(Pool));
if (pool == NULL)
return NULL;
Chunk *arr = pool->free_chunk = malloc(pool_size * sizeof(Chunk));
if (arr == NULL) {
free(pool);
return NULL;
}
for (size_t i = 0; i < pool_size - 1; i++) arr[i].next = &arr[i + 1];
arr[pool_size - 1].next = NULL;
pool->array_starts = malloc(sizeof(LinkedPtr));
if (pool->array_starts == NULL) {
/* Allocation failed */
free(arr);
free(pool);
return NULL;
}
pool->array_starts->next = NULL;
pool->array_starts->ptr = arr;
return pool;
}
void *pool_alloc(Pool *pool) {
if (pool == NULL || pool->free_chunk == NULL)
return NULL;
// Pop a new one from the free list
Chunk *result = pool->free_chunk;
pool->free_chunk = pool->free_chunk->next;
return result;
}
void pool_free(Pool *pool, void *ptr) {
if (pool == NULL || ptr == NULL)
return;
// This can be done withuot an intermediate ptr
Chunk *freed = ptr;
freed->next = pool->free_chunk;
pool->free_chunk = freed;
}
void pool_close(Pool *pool) {
if (pool == NULL)
return;
LinkedPtr *lptr = pool->array_starts;
while (lptr != NULL) {
LinkedPtr *next = lptr->next;
free(lptr->ptr);
free(lptr);
lptr = next;
}
free(pool);
}
bool pool_resize(Pool *pool, size_t extra_chunk_num) {
if (pool == NULL || extra_chunk_num == 0)
return false;
// Allocate the array of extra chunks that we are trying to add to the pool.
Chunk *extra_chunk_arr = malloc(extra_chunk_num * sizeof(Chunk));
if (extra_chunk_arr == NULL)
return false;
// Link the new chunks together, just like we did when creating the pool.
for (size_t i = 0; i < extra_chunk_num - 1; i++)
extra_chunk_arr[i].next = &extra_chunk_arr[i + 1];
// Prepend the array of extra chunks to the “free chunks” list, just like we
// did when freeing chunks.
extra_chunk_arr[extra_chunk_num - 1].next = pool->free_chunk;
pool->free_chunk = extra_chunk_arr;
// Allocate a new LinkedPtr structure, and store the start of the new chunk
// array in it.
LinkedPtr *array_start = malloc(sizeof(LinkedPtr));
if (array_start == NULL) {
free(extra_chunk_arr);
return false;
}
// Prepend this new LinkedPtr structure to the linked list of “array
// starts”, stored inside the Pool structure.
array_start->ptr = extra_chunk_arr;
array_start->next = pool->array_starts;
pool->array_starts = array_start;
return true;
}