Improve guf_id_pool

This commit is contained in:
jun 2025-03-17 22:49:36 +01:00
parent ad884ee1e9
commit cc0413116d

View File

@ -8,22 +8,76 @@
#define GUF_ID_POOL_H
#include "guf_common.h"
#include "guf_assert.h"
#define GUF_ID_i32_NULL (-1)
#define GUF_ID_i16_NULL (-1)
#define GUF_ID_i8_NULL (-1)
#define GUF_ID_u32_NULL (UINT32_MAX)
#define GUF_ID_u16_NULL (UINT16_MAX)
#define GUF_ID_u8_NULL (UINT8_MAX)
#endif
// cf. https://github.com/erincatto/box2d/blob/main/src/id_pool.c (last-retrieved 2025-03-11)
/*
ID-pool which allows for allocating and freeing unique IDs (useful when implementing object pools):
cf. https://github.com/erincatto/box2d/blob/main/src/id_pool.c (last-retrieved 2025-03-17)
*/
// // test beg
#define GUF_ID_POOL_IMPL_STATIC
#define GUF_ID_POOL_T uint32_t
#define GUF_ID_POOL_NAME guf_idpool_u32
#define GUF_ID_POOL_i32
// // test end
#ifndef GUF_ID_POOL_T
#error "Must pass GUF_ID_POOL_T"
#endif
#ifndef GUF_ID_POOL_NAME
#error "Must pass GUF_ID_POOL_NAME"
#if defined(GUF_ID_POOL_i32)
#undef GUF_ID_POOL_i32
#define GUF_ID_POOL_T int32_t
#define GUF_ID_POOL_T_MAX INT32_MAX
#define GUF_ID_POOL_NULL GUF_ID_i32_NULL
#ifndef GUF_ID_POOL_NAME
#define GUF_ID_POOL_NAME guf_idpool_i32
#endif
#elif defined(GUF_ID_POOL_i16)
#undef GUF_ID_POOL_i16
#define GUF_ID_POOL_T int16_t
#define GUF_ID_POOL_T_MAX INT16_MAX
#define GUF_ID_POOL_NULL GUF_ID_i16_NULL
#ifndef GUF_ID_POOL_NAME
#define GUF_ID_POOL_NAME guf_idpool_i16
#endif
#elif defined(GUF_ID_POOL_i8)
#undef GUF_ID_POOL_i8
#define GUF_ID_POOL_T int8_t
#define GUF_ID_POOL_T_MAX INT8_MAX
#define GUF_ID_POOL_NULL GUF_ID_i8_NULL
#ifndef GUF_ID_POOL_NAME
#define GUF_ID_POOL_NAME guf_idpool_i8
#endif
#elif defined(GUF_ID_POOL_u32)
#undef GUF_ID_POOL_u32
#define GUF_ID_POOL_T uint32_t
#define GUF_ID_POOL_T_MAX (UINT32_MAX - 1)
#define GUF_ID_POOL_NULL GUF_ID_u32_NULL
#ifndef GUF_ID_POOL_NAME
#define GUF_ID_POOL_NAME guf_idpool_u32
#endif
#elif defined(GUF_ID_POOL_u16)
#undef GUF_ID_POOL_u16
#define GUF_ID_POOL_T uint16_t
#define GUF_ID_POOL_T_MAX (UINT16_MAX - 1)
#define GUF_ID_POOL_NULL GUF_ID_u16_NULL
#ifndef GUF_ID_POOL_NAME
#define GUF_ID_POOL_NAME guf_idpool_u16
#endif
#elif defined(GUF_ID_POOL_u8)
#undef GUF_ID_POOL_u8
#define GUF_ID_POOL_T uint8_t
#define GUF_ID_POOL_T_MAX (UINT8_MAX - 1)
#define GUF_ID_POOL_NULL GUF_ID_u8_NULL
#ifndef GUF_ID_POOL_NAME
#define GUF_ID_POOL_NAME guf_idpool_u8
#endif
#else
#error "No GUF_ID_POOL_i defined"
#endif
#define GUF_ID_POOL_DBUF GUF_CAT(GUF_ID_POOL_NAME, _id_dbuf)
@ -34,11 +88,30 @@
#define GUF_DBUF_ONLY_TYPES
#include "guf_dbuf.h"
#ifndef GUF_ID_POOL_IMPL
#ifdef GUF_ID_POOL_ONLY_TYPES
#undef GUF_ID_POOL_ONLY_TYPES
typedef struct GUF_ID_POOL_NAME {
GUF_ID_POOL_DBUF free_id_dbuf;
GUF_ID_POOL_T next_id;
} GUF_ID_POOL_NAME;
#else
#ifndef GUF_ID_POOL_IMPL
typedef struct GUF_ID_POOL_NAME {
GUF_ID_POOL_DBUF free_id_dbuf; // Stores all the freed ids to reuse.
GUF_ID_POOL_T next_id; // The next-id to assign in case we don't have any freed ids to reuse.
} GUF_ID_POOL_NAME;
GUF_ID_POOL_NAME *GUF_CAT(GUF_ID_POOL_NAME, _try_init)(GUF_ID_POOL_NAME *pool, ptrdiff_t initial_cap, guf_allocator *allocator, guf_err *err);
GUF_ID_POOL_NAME *GUF_CAT(GUF_ID_POOL_NAME, _init)(GUF_ID_POOL_NAME *pool, ptrdiff_t initial_cap, guf_allocator *allocator);
void GUF_CAT(GUF_ID_POOL_NAME, _free)(GUF_ID_POOL_NAME *pool, void *ctx);
GUF_ID_POOL_T GUF_CAT(GUF_ID_POOL_NAME, _try_alloc_id)(GUF_ID_POOL_NAME *pool, guf_err *err);
GUF_ID_POOL_T GUF_CAT(GUF_ID_POOL_NAME, _alloc_id)(GUF_ID_POOL_NAME *pool);
void GUF_CAT(GUF_ID_POOL_NAME, _try_free_id)(GUF_ID_POOL_NAME *pool, GUF_ID_POOL_T id, guf_err *err);
void GUF_CAT(GUF_ID_POOL_NAME, _free_id)(GUF_ID_POOL_NAME *pool, GUF_ID_POOL_T id);
#endif
#if defined(GUF_ID_POOL_IMPL) || defined(GUF_ID_POOL_IMPL_STATIC)
@ -50,6 +123,16 @@
#define GUF_DBUF_IMPL_STATIC
#include "guf_dbuf.h"
static bool GUF_CAT(GUF_ID_POOL_NAME, _is_valid)(const GUF_ID_POOL_NAME *pool)
{
if (!pool) {
return false;
}
bool valid_next_id = (pool->next_id == GUF_ID_POOL_NULL) || (pool->next_id >= 0 && pool->next_id <= GUF_ID_POOL_T_MAX);
bool valid_bufsize = ((pool->free_id_dbuf.size <= (ptrdiff_t)pool->next_id) || (pool->next_id == GUF_ID_POOL_NULL)) && (size_t)pool->free_id_dbuf.size <= (size_t)GUF_ID_POOL_T_MAX + 1;
return valid_next_id && valid_bufsize && GUF_CAT(GUF_ID_POOL_DBUF, _valid)(&pool->free_id_dbuf) && pool->free_id_dbuf.size >= 0;
}
GUF_ID_POOL_NAME *GUF_CAT(GUF_ID_POOL_NAME, _try_init)(GUF_ID_POOL_NAME *pool, ptrdiff_t initial_cap, guf_allocator *allocator, guf_err *err)
{
if (!pool) {
@ -63,6 +146,7 @@ GUF_ID_POOL_NAME *GUF_CAT(GUF_ID_POOL_NAME, _try_init)(GUF_ID_POOL_NAME *pool, p
return NULL;
}
pool->free_id_dbuf = {0};
GUF_CAT(GUF_ID_POOL_DBUF, _try_init)(&pool->free_id_dbuf, initial_cap, allocator, err);
if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, "in guf_idpool_try_init: failed to allocate dbuf");
@ -75,50 +159,103 @@ GUF_ID_POOL_NAME *GUF_CAT(GUF_ID_POOL_NAME, _try_init)(GUF_ID_POOL_NAME *pool, p
return pool;
}
GUF_ID_POOL_T GUF_CAT(GUF_ID_POOL_NAME, _alloc_id)(GUF_ID_POOL_NAME *pool)
GUF_ID_POOL_NAME *GUF_CAT(GUF_ID_POOL_NAME, _init)(GUF_ID_POOL_NAME *pool, ptrdiff_t initial_cap, guf_allocator *allocator)
{
return GUF_CAT(GUF_ID_POOL_NAME, _try_init)(pool, initial_cap, allocator, NULL);
}
void GUF_CAT(GUF_ID_POOL_NAME, _free)(GUF_ID_POOL_NAME *pool, void *ctx)
{
(void)ctx;
if (!pool) {
return;
}
GUF_ASSERT(GUF_CAT(GUF_ID_POOL_NAME, _is_valid)(pool));
GUF_CAT(GUF_ID_POOL_DBUF, _free)(&pool->free_id_dbuf, NULL);
pool->free_id_dbuf = {0};
pool->next_id = 0;
}
GUF_ID_POOL_T GUF_CAT(GUF_ID_POOL_NAME, _try_alloc_id)(GUF_ID_POOL_NAME *pool, guf_err *err)
{
GUF_ASSERT(pool);
GUF_ASSERT(pool->free_id_dbuf.size >= 0);
GUF_ASSERT(GUF_CAT(GUF_ID_POOL_NAME, _is_valid)(pool));
if (pool->free_id_dbuf.size > 0) {
GUF_ID_POOL_T id = *GUF_CAT(GUF_ID_POOL_DBUF, _back)(&pool->free_id_dbuf);
GUF_CAT(GUF_ID_POOL_DBUF, _pop)(&pool->free_id_dbuf);
GUF_ASSERT(id != GUF_ID_POOL_NULL && id <= GUF_ID_POOL_T_MAX);
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return id;
}
GUF_ID_POOL_T id = pool->next_id;
if (id == GUF_ID_POOL_T_MAX || id == GUF_ID_POOL_NULL) {
pool->next_id = GUF_ID_POOL_NULL;
} else {
GUF_ID_POOL_T id = pool->next_id;
pool->next_id += 1; // TODO: handle overflow (need a guf_id_pool_t_max)
return id;
pool->next_id += 1;
}
if (id == GUF_ID_POOL_NULL) {
guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, "in guf_idpool_try_alloc_id: Out of ids.");
} else {
guf_err_set_if_not_null(err, GUF_ERR_NONE);
}
return id;
}
GUF_ID_POOL_T GUF_CAT(GUF_ID_POOL_NAME, _alloc_id)(GUF_ID_POOL_NAME *pool)
{
return GUF_CAT(GUF_ID_POOL_NAME, _try_alloc_id)(pool, NULL);
}
void GUF_CAT(GUF_ID_POOL_NAME, _try_free_id)(GUF_ID_POOL_NAME *pool, GUF_ID_POOL_T id, guf_err *err)
{
GUF_ASSERT(pool);
GUF_ASSERT(pool->next_id > 0);
GUF_ASSERT(GUF_CAT(GUF_ID_POOL_NAME, _is_valid)(pool));
if (id < 0 || id >= pool->next_id) {
if (id == GUF_ID_POOL_NULL) { // ID is NULL.
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, "in guf_idpool_try_free_id: id is NULL");
return;
} else if (id < 0 || id >= pool->next_id) { // ID is beyond the allocated range.
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, "in guf_idpool_try_free_id: id < 0 or id >= pool->next_id");
return;
} else if (pool->next_id == 0) { // There haven't been any IDs allocated yet.
GUF_ASSERT(pool->free_id_dbuf.size == 0);
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, "in guf_idpool_try_free_id: pool->next_id == 0");
return;
}
if (id == pool->next_id - 1) {
// NOTE: https://github.com/erincatto/box2d/issues/893 (last-retrieved 2025-03-17)
if (id == pool->next_id - 1) { // Optimisation in case we remove the largest allocated id so far
pool->next_id -= 1;
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return;
}
GUF_CAT(GUF_ID_POOL_DBUF, _try_push_val)(&pool->free_id_dbuf, id, err);
if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, "in guf_idpool_try_free_id: failed to push id");
return;
} else {
guf_err_set_if_not_null(err, GUF_ERR_NONE);
}
}
void GUF_CAT(GUF_ID_POOL_NAME, _free_id)(GUF_ID_POOL_NAME *pool, GUF_ID_POOL_T id)
{
GUF_CAT(GUF_ID_POOL_NAME, _try_free_id)(pool, id, NULL);
}
#endif /* end impl */
#endif /* end only-types */
#undef GUF_ID_POOL_KWRDS
#undef GUF_ID_POOL_IMPL
#undef GUF_ID_POOL_IMPL_STATIC
#undef GUF_ID_POOL_T
#undef GUF_ID_POOL_T_MAX
#undef GUF_ID_POOL_NULL
#undef GUF_ID_POOL_NAME