#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++; }