Improve guf_id_pool
This commit is contained in:
parent
ad884ee1e9
commit
cc0413116d
@ -8,22 +8,76 @@
|
|||||||
#define GUF_ID_POOL_H
|
#define GUF_ID_POOL_H
|
||||||
#include "guf_common.h"
|
#include "guf_common.h"
|
||||||
#include "guf_assert.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
|
#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
|
// // test beg
|
||||||
#define GUF_ID_POOL_IMPL_STATIC
|
#define GUF_ID_POOL_IMPL_STATIC
|
||||||
#define GUF_ID_POOL_T uint32_t
|
#define GUF_ID_POOL_i32
|
||||||
#define GUF_ID_POOL_NAME guf_idpool_u32
|
|
||||||
// // test end
|
// // test end
|
||||||
|
|
||||||
#ifndef GUF_ID_POOL_T
|
#if defined(GUF_ID_POOL_i32)
|
||||||
#error "Must pass GUF_ID_POOL_T"
|
#undef GUF_ID_POOL_i32
|
||||||
#endif
|
#define GUF_ID_POOL_T int32_t
|
||||||
|
#define GUF_ID_POOL_T_MAX INT32_MAX
|
||||||
#ifndef GUF_ID_POOL_NAME
|
#define GUF_ID_POOL_NULL GUF_ID_i32_NULL
|
||||||
#error "Must pass GUF_ID_POOL_NAME"
|
#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
|
#endif
|
||||||
|
|
||||||
#define GUF_ID_POOL_DBUF GUF_CAT(GUF_ID_POOL_NAME, _id_dbuf)
|
#define GUF_ID_POOL_DBUF GUF_CAT(GUF_ID_POOL_NAME, _id_dbuf)
|
||||||
@ -34,11 +88,30 @@
|
|||||||
#define GUF_DBUF_ONLY_TYPES
|
#define GUF_DBUF_ONLY_TYPES
|
||||||
#include "guf_dbuf.h"
|
#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 {
|
typedef struct GUF_ID_POOL_NAME {
|
||||||
GUF_ID_POOL_DBUF free_id_dbuf;
|
GUF_ID_POOL_DBUF free_id_dbuf;
|
||||||
GUF_ID_POOL_T next_id;
|
GUF_ID_POOL_T next_id;
|
||||||
} GUF_ID_POOL_NAME;
|
} 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
|
#endif
|
||||||
|
|
||||||
#if defined(GUF_ID_POOL_IMPL) || defined(GUF_ID_POOL_IMPL_STATIC)
|
#if defined(GUF_ID_POOL_IMPL) || defined(GUF_ID_POOL_IMPL_STATIC)
|
||||||
@ -50,6 +123,16 @@
|
|||||||
#define GUF_DBUF_IMPL_STATIC
|
#define GUF_DBUF_IMPL_STATIC
|
||||||
#include "guf_dbuf.h"
|
#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)
|
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) {
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pool->free_id_dbuf = {0};
|
||||||
GUF_CAT(GUF_ID_POOL_DBUF, _try_init)(&pool->free_id_dbuf, initial_cap, allocator, err);
|
GUF_CAT(GUF_ID_POOL_DBUF, _try_init)(&pool->free_id_dbuf, initial_cap, allocator, err);
|
||||||
if (err && *err != GUF_ERR_NONE) {
|
if (err && *err != GUF_ERR_NONE) {
|
||||||
guf_err_set_or_panic(err, *err, "in guf_idpool_try_init: failed to allocate dbuf");
|
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;
|
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);
|
||||||
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) {
|
if (pool->free_id_dbuf.size > 0) {
|
||||||
GUF_ID_POOL_T id = *GUF_CAT(GUF_ID_POOL_DBUF, _back)(&pool->free_id_dbuf);
|
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_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;
|
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 {
|
} else {
|
||||||
GUF_ID_POOL_T id = pool->next_id;
|
pool->next_id += 1;
|
||||||
pool->next_id += 1; // TODO: handle overflow (need a guf_id_pool_t_max)
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
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)
|
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);
|
||||||
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");
|
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, "in guf_idpool_try_free_id: id < 0 or id >= pool->next_id");
|
||||||
return;
|
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;
|
pool->next_id -= 1;
|
||||||
|
guf_err_set_if_not_null(err, GUF_ERR_NONE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GUF_CAT(GUF_ID_POOL_DBUF, _try_push_val)(&pool->free_id_dbuf, id, err);
|
GUF_CAT(GUF_ID_POOL_DBUF, _try_push_val)(&pool->free_id_dbuf, id, err);
|
||||||
|
|
||||||
if (err && *err != GUF_ERR_NONE) {
|
if (err && *err != GUF_ERR_NONE) {
|
||||||
guf_err_set_or_panic(err, *err, "in guf_idpool_try_free_id: failed to push id");
|
guf_err_set_or_panic(err, *err, "in guf_idpool_try_free_id: failed to push id");
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
guf_err_set_if_not_null(err, GUF_ERR_NONE);
|
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 impl */
|
||||||
|
|
||||||
|
#endif /* end only-types */
|
||||||
|
|
||||||
#undef GUF_ID_POOL_KWRDS
|
#undef GUF_ID_POOL_KWRDS
|
||||||
#undef GUF_ID_POOL_IMPL
|
#undef GUF_ID_POOL_IMPL
|
||||||
#undef GUF_ID_POOL_IMPL_STATIC
|
#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
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user