Update: Implement becs3.h in C89

This commit is contained in:
Rodolfo Barcelli Jo 2026-01-21 17:15:51 +08:00
parent 8d0f9d83e4
commit 01edc3f866
4 changed files with 298 additions and 2 deletions

View file

@ -5,7 +5,9 @@ CompileFlags:
"-Werror",
"-DBECS_IMPLEMENTATION=1",
"-DMAX_ENTITIES=100",
"-DNUM_COMPONENTS=2"]
"-DNUM_COMPONENTS=2",
"-xc",
"-std=c89"]
Diagnostics:
Suppress: [
"-Wunused-function",

7
.editorconfig Normal file
View file

@ -0,0 +1,7 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4

287
becs3.h Normal file
View file

@ -0,0 +1,287 @@
/*
* Usage:
* Define BECS_IMPLEMENTATION in exactly _one_ file before including this file
*
* Eg:
* #define BECS_IMPLEMENTATION
* #include "becs.h"
*/
#ifndef BECS_H
#define BECS_H
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdbool.h>
#ifdef BECS_SLOW
#include <assert.h>
#else
#define assert(expression)
#endif /* BECS_SLOW */
typedef struct BECS_Arena BECS_Arena;
typedef struct BECS_ComponentPool BECS_ComponentPool;
typedef struct BECS_ECS BECS_ECS;
typedef uint32_t BECS_Entity;
typedef uint32_t BECS_ComponentID;
BECS_Entity BECS_EntityCreate(BECS_ECS *ecs);
void BECS_EntityDestroy(BECS_ECS *ecs, BECS_Entity entity);
bool BECS_IsEntityAlive(BECS_ECS *ecs, BECS_Entity entity);
BECS_ComponentID BECS_ComponentRegister(BECS_ECS *ecs, size_t componentSize, size_t maximumComponentCount);
void *BECS_ComponentAdd(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId);
void BECS_ComponentRemove(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId);
void* BECS_ComponentGet(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId);
bool BECS_ComponentHas(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId);
#ifdef BECS_IMPLEMENTATION
#ifndef BECS_MAX_ENTITIES
#define BECS_MAX_ENTITIES 4096
#endif /* BECS_MAX_ENTITIES */
#ifndef BECS_MAX_COMPONENTS
#define BECS_MAX_COMPONENTS 64
#endif /* BECS_MAX_COMPONENTS */
#define BECS_NULL_ENTITY
#define BECS_INVALID_COMPONENT_ID UINT32_MAX
typedef uint64_t BECS_Signature;
struct BECS_Arena {
uintptr_t nextAllocation;
size_t capacity;
void *memory;
};
struct BECS_ComponentPool {
size_t unitSize;
size_t capacity;
size_t count;
uint32_t sparseArray[BECS_MAX_ENTITIES];
void *denseArray;
BECS_Entity *denseEntityMapping;
};
struct BECS_ECS {
BECS_Arena arena;
/* Entity Management */
uint32_t entityCount;
BECS_Signature entitySignatures[BECS_MAX_COMPONENTS];
bool aliveEntites[BECS_MAX_COMPONENTS];
BECS_Entity freeEntities[BECS_MAX_COMPONENTS];
uint32_t freeEntityCount;
/* Component Management */
uint32_t componentTypeCount;
BECS_ComponentPool componentPools[BECS_MAX_COMPONENTS];
};
/* Signature Helpers */
static inline BECS_Signature BECS_SignatureSet(BECS_Signature sig, BECS_ComponentID componentId)
{
return sig | (1ULL << componentId);
}
static inline bool ecs_signature_has(BECS_Signature sig, BECS_ComponentID component_id)
{
return (sig & (1ULL << component_id)) != 0;
}
static inline bool ecs_signature_matches(BECS_Signature entitySig, BECS_Signature querySig)
{
return (entitySig & querySig) == querySig;
}
/* Memory Arena */
BECS_Arena BECS_CreateArena(void *memory, size_t size)
{
uintptr_t baseOffset = 64 - ((uintptr_t)memory % 64);
BECS_Arena arena = {
baseOffset,
size,
memory
};
return arena;
}
static void *BECS_AllocateMemory(BECS_Arena *arena, size_t size)
{
size_t totalSizeBytes = size;
uintptr_t nextAllocOffset = arena->nextAllocation + ((64 - (arena->nextAllocation % 64)) & 63);
if (nextAllocOffset + totalSizeBytes <= arena->capacity)
{
arena->nextAllocation = nextAllocOffset + totalSizeBytes;
return (void *)((uintptr_t)arena->memory + (uintptr_t)nextAllocOffset);
}
else
{
return NULL;
}
}
/* Component Pool Backend */
static void BECS_ComponentPoolInit
(BECS_Arena *arena, BECS_ComponentPool *pool ,size_t componentSize, size_t capacity)
{
pool->unitSize = componentSize;
pool->capacity = capacity;
pool->count = 0;
pool->denseArray = BECS_AllocateMemory(arena, componentSize * capacity);
pool->denseEntityMapping = (BECS_Entity *)BECS_AllocateMemory(arena, sizeof(BECS_Entity) * capacity);
for (uint32_t i = 0; i < BECS_MAX_ENTITIES; i++)
{
pool->sparseArray[i] = UINT32_MAX;
}
}
static void *BECS_ComponentPoolAdd(BECS_ComponentPool *pool, BECS_Entity entity)
{
uint32_t index = pool->count;
pool->count++;
pool->denseEntityMapping[index] = entity;
pool->sparseArray[entity] = index;
return (uint8_t *)pool->denseArray + (index * pool->unitSize);
}
static void BECS_ComponentPoolRemove(BECS_ComponentPool *pool, BECS_Entity entity)
{
uint32_t index = pool->sparseArray[entity];
if (index == UINT32_MAX)
{
return;
}
uint32_t last_index = pool->count - 1;
BECS_Entity last_entity = pool->denseEntityMapping[last_index];
if (index != last_index)
{
void *dst = (uint8_t *)pool->denseArray + (index * pool->unitSize);
void *src = (uint8_t *)pool->denseArray + (last_index * pool->unitSize);
memcpy(dst, src, pool->unitSize);
pool->denseEntityMapping[index] = last_entity;
pool->sparseArray[last_entity] = index;
}
pool->sparseArray[entity] = UINT32_MAX;
}
static void *BECS_ComponentPoolGet(BECS_ComponentPool *pool, BECS_Entity entity)
{
uint32_t index = pool->sparseArray[entity];
if (index == UINT32_MAX)
{
return NULL;
}
return (uint8_t *)pool->denseArray + (index * pool->unitSize);
}
static bool BECS_ComponentPoolHas(BECS_ComponentPool *pool, BECS_Entity entity)
{
return pool->sparseArray[entity] != UINT32_MAX;
}
/* World API Functions */
/* TODO: Implement Initialization Functions
BECS_ECS BECS_InitECS(BECS_Arena *arena)
{
NOTE: Make use of memset here
}
void BECS_ResetECS(BECS_ECS *ecs)
{
NOTE: Make use of memset here
}
*/
/* Entity API Functions */
/* TODO: Implement Entity API Functions
BECS_Entity BECS_EntityCreate(BECS_ECS *ecs)
{
}
void BECS_EntityDestroy(BECS_ECS *ecs, BECS_Entity entity)
{
}
bool BECS_IsEntityAlive(BECS_ECS *ecs, BECS_Entity entity)
{
}
*/
/* Component API Functions */
BECS_ComponentID BECS_ComponentRegister(BECS_ECS *ecs, size_t componentSize, size_t maximumComponentCount)
{
if (maximumComponentCount == 0)
{
maximumComponentCount = BECS_MAX_ENTITIES;
}
BECS_ComponentID id = ecs->componentTypeCount;
BECS_ComponentPoolInit(&ecs->arena, &ecs->componentPools[id],
componentSize, maximumComponentCount);
ecs->componentTypeCount++;
return id;
}
void *BECS_ComponentAdd(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId)
{
void *component = BECS_ComponentPoolAdd(&ecs->componentPools[componentId], entity);
ecs->entitySignatures[entity] = BECS_SignatureSet(ecs->entitySignatures[entity], componentId);
memset(component, 0, ecs->componentPools[componentId].unitSize);
return component;
}
void BECS_ComponentRemove(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId)
{
if (!ecs->aliveEntites[entity])
{
return;
}
BECS_ComponentPoolRemove(&ecs->componentPools[componentId], entity);
ecs->entitySignatures[entity] &= ~(1ULL << componentId);
}
void* BECS_ComponentGet(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId)
{
if (!ecs->aliveEntites[entity])
{
return NULL;
}
return BECS_ComponentPoolGet(&ecs->componentPools[componentId], entity);
}
bool BECS_ComponentHas(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId)
{
if (!ecs->aliveEntites[entity])
{
return false;
}
return ecs_signature_has(ecs->entitySignatures[entity], componentId);
}
#endif /* BECS_IMPLEMENTATION */
#endif /* BECS_H */

View file

@ -4,5 +4,5 @@ REM Build Script for BECS
mkdir build
pushd ".\build"
cl /GR- /Oi /EHa- /MTd /Zi "..\test\main.c"
cl /Za /GR- /Oi /EHa- /MTd /Zi /Tc "..\test\main.c"
popd