From 01edc3f8667993396ca380defd2081759db49fd2 Mon Sep 17 00:00:00 2001 From: Rodolfo Barcelli Jo Date: Wed, 21 Jan 2026 17:15:51 +0800 Subject: [PATCH] Update: Implement becs3.h in C89 --- .clangd | 4 +- .editorconfig | 7 ++ becs3.h | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++ build.bat | 2 +- 4 files changed, 298 insertions(+), 2 deletions(-) create mode 100644 .editorconfig create mode 100644 becs3.h diff --git a/.clangd b/.clangd index bbfecc8..e29af29 100644 --- a/.clangd +++ b/.clangd @@ -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", diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a882442 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 diff --git a/becs3.h b/becs3.h new file mode 100644 index 0000000..adcbce8 --- /dev/null +++ b/becs3.h @@ -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 +#include +#include +#include + +#ifdef BECS_SLOW +#include +#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 */ diff --git a/build.bat b/build.bat index cd1f219..507473c 100644 --- a/build.bat +++ b/build.bat @@ -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