158 lines
4.7 KiB
C
158 lines
4.7 KiB
C
#include "object_pool.h"
|
|
|
|
#include "../memory/memory.h"
|
|
|
|
typedef union BzObjectIdx {
|
|
struct {
|
|
u16 idx;
|
|
u16 page;
|
|
};
|
|
u32 value;
|
|
} BzObjectIdx;
|
|
|
|
#define INVALID_OBJECT_IDX (UINT32_MAX)
|
|
|
|
static_assert(sizeof(BzObjectIdx) == 4, "");
|
|
|
|
typedef struct BzObjectPool {
|
|
void **pages;
|
|
size_t pageCapacity;
|
|
size_t pageSize;
|
|
size_t objectsPerPage;
|
|
size_t stride;
|
|
|
|
size_t numFree;
|
|
BzObjectIdx firstFree;
|
|
|
|
BzObjectPoolFunc constructor;
|
|
BzObjectPoolFunc destructor;
|
|
} BzObjectPool;
|
|
|
|
static void initializePage(BzObjectPool *pool, u16 pageIdx) {
|
|
BZ_ASSERT(pageIdx < UINT8_MAX);
|
|
// Link free list
|
|
for (size_t i = 0; i < pool->objectsPerPage - 1; i++) {
|
|
BzObjectIdx idx = {.page = pageIdx, .idx = i};
|
|
BzObjectIdx *obj = bzObjectPoolGetObject(pool, idx.value);
|
|
|
|
BzObjectIdx nextIdx = {.page = pageIdx, .idx = i + 1};
|
|
obj->value = nextIdx.value;
|
|
}
|
|
BzObjectIdx lastIdx = {.page = pageIdx, .idx = pool->objectsPerPage - 1};
|
|
BzObjectIdx *lastObj = bzObjectPoolGetObject(pool, lastIdx.value);
|
|
lastObj->value = pool->firstFree.value;
|
|
pool->firstFree = (BzObjectIdx) {.page = pageIdx, .idx = 0};
|
|
|
|
pool->numFree += pool->objectsPerPage;
|
|
}
|
|
static void addNewPage(BzObjectPool *pool) {
|
|
if (pool->pageSize >= pool->pageCapacity) {
|
|
size_t newCapacity = pool->pageCapacity << 1;
|
|
void **newPages = bzResize(pool->pages, sizeof(*newPages) * newCapacity);
|
|
pool->pages = newPages;
|
|
pool->pageCapacity = newCapacity;
|
|
}
|
|
void *newPage = bzAlloc(pool->stride * pool->objectsPerPage);
|
|
BZ_ASSERT(newPage);
|
|
size_t pageIdx = pool->pageSize;
|
|
pool->pages[pageIdx] = newPage;
|
|
pool->pageSize++;
|
|
initializePage(pool, pageIdx);
|
|
}
|
|
|
|
BzObjectPool *bzObjectPoolCreate(const BzObjectPoolDesc *desc) {
|
|
BZ_ASSERT(desc->objectSize > 0);
|
|
|
|
// NOTE: Since object bits are used as a free list
|
|
// when not in use, we must ensure they can hold i32.
|
|
size_t stride = desc->objectSize;
|
|
if (stride < sizeof(i32)) {
|
|
stride = sizeof(i32);
|
|
}
|
|
BZ_ASSERT(desc->objectsPerPage >= 0);
|
|
BZ_ASSERT(desc->objectsPerPage < (2 << 23));
|
|
|
|
size_t objectsPerPage = desc->objectsPerPage;
|
|
if (objectsPerPage == 0) objectsPerPage = 512;
|
|
|
|
size_t pageCapacity = 8;
|
|
void **pages = bzAlloc(sizeof(*pages) * pageCapacity);
|
|
|
|
BzObjectPool *pool = bzAlloc(sizeof(*pool));
|
|
*pool = (BzObjectPool) {
|
|
.pages = pages,
|
|
.pageSize = 0,
|
|
.pageCapacity = pageCapacity,
|
|
.objectsPerPage = objectsPerPage,
|
|
.stride = stride,
|
|
};
|
|
pool->firstFree.value = INVALID_OBJECT_IDX;
|
|
|
|
addNewPage(pool);
|
|
return pool;
|
|
}
|
|
void bzObjectPoolDestroy(BzObjectPool *pool) {
|
|
for (size_t i = 0; i < pool->pageSize; i++) {
|
|
bzFree(pool->pages[i]);
|
|
pool->pages[i] = NULL;
|
|
}
|
|
bzFree(pool->pages);
|
|
pool->pages = NULL;
|
|
bzFree(pool);
|
|
}
|
|
|
|
size_t bzObjectPoolGetObjectSize(BzObjectPool *pool) {
|
|
return pool->stride;
|
|
}
|
|
|
|
size_t bzObjectPoolGetNumFree(BzObjectPool *pool) {
|
|
return pool->numFree;
|
|
}
|
|
void *bzObjectPool(BzObjectPool *pool) {
|
|
if (pool->firstFree.value == INVALID_OBJECT_IDX) {
|
|
addNewPage(pool);
|
|
BZ_ASSERT(pool->firstFree.value != INVALID_OBJECT_IDX);
|
|
}
|
|
BzObjectIdx *object = (BzObjectIdx *) bzObjectPoolGetObject(pool, pool->firstFree.value);
|
|
pool->firstFree = *object;
|
|
|
|
BZ_ASSERT(pool->numFree > 0);
|
|
pool->numFree--;
|
|
return object;
|
|
}
|
|
void *bzObjectPoolGetObject(BzObjectPool *pool, u32 idx) {
|
|
BzObjectIdx objectIdx = {.value = idx};
|
|
BZ_ASSERT(objectIdx.page >= 0 && objectIdx.page < pool->pageSize);
|
|
BZ_ASSERT(objectIdx.idx >= 0 && objectIdx.idx < pool->objectsPerPage);
|
|
|
|
void *page = pool->pages[objectIdx.page];
|
|
return (void *) ((u8 *) page + objectIdx.idx * pool->stride);
|
|
}
|
|
u32 bzObjectPoolGetIdx(BzObjectPool *pool, void *object) {
|
|
u16 pageIdx = pool->pageSize;
|
|
u16 idx = 0;
|
|
|
|
for (size_t i = 0; i < pool->pageSize; i++) {
|
|
void *start = pool->pages[i];
|
|
void *end = (u8 *) start + pool->objectsPerPage * pool->stride;
|
|
|
|
if (object >= start && object < end) {
|
|
pageIdx = i;
|
|
idx = (size_t) object - (size_t) start;
|
|
idx = idx / pool->stride;
|
|
break;
|
|
}
|
|
}
|
|
BZ_ASSERT(pageIdx < pool->pageSize);
|
|
BzObjectIdx objectIdx = {.page = pageIdx, .idx = idx};
|
|
return objectIdx.value;
|
|
}
|
|
void bzObjectPoolRelease(BzObjectPool *pool, void *object) {
|
|
BzObjectIdx objectIdx = {.value = bzObjectPoolGetIdx(pool, object)};
|
|
|
|
void *page = pool->pages[objectIdx.page];
|
|
*(BzObjectIdx *) ((u8 *) page + objectIdx.idx * pool->stride) = pool->firstFree;
|
|
pool->firstFree = objectIdx;
|
|
pool->numFree++;
|
|
}
|