Added handle system and improve resource management.

This commit is contained in:
2025-12-31 23:50:22 +09:00
parent 5c988108ef
commit acaaa2a86e
33 changed files with 998 additions and 915 deletions

View File

@@ -0,0 +1,390 @@
#ifndef COLLECTIONS_H
#define COLLECTIONS_H
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define INVALID_HANDLE(T) (T){ .id = UINT32_MAX, .generation = 0 }
#define IS_VALID_HANDLE(h) ((h).id != UINT32_MAX)
#define HANDLE_DEF(name) \
typedef struct \
{ \
uint32_t id; \
uint32_t generation; \
} name##_handle_t;
#define ARRAY_DEF(T, name) \
typedef struct \
{ \
uint32_t length; \
T* buffer; \
} name##_array_t; \
static inline result_t name##_array_init(name##_array_t* array, uint32_t size) \
{ \
array->length = size; \
T* temp = (T*)malloc(sizeof(T) * size); \
if (temp == NULL) \
{ \
return RESULT_OUT_OF_MEMORY; \
} \
array->buffer = temp; \
return RESULT_SUCCESS; \
} \
static inline void name##_array_free(name##_array_t* array) \
{ \
if (array->buffer != NULL) \
{ \
free(array->buffer); \
array->buffer = NULL; \
array->length = 0; \
} \
} \
static inline T* name##_array_get(name##_array_t* array, uint32_t index) \
{ \
if (index >= array->length) \
{ \
return NULL; \
} \
return &array->buffer[index]; \
}
#define LIST_DEF(T, name) \
typedef struct \
{ \
uint32_t count; \
uint32_t capacity; \
T* buffer; \
} name##_list_t; \
static inline result_t name##_list_init(name##_list_t* list, uint32_t capacity) \
{ \
capacity = capacity == 0 ? 1 : capacity; \
list->count = 0; \
list->capacity = capacity; \
T* temp = (T*)malloc(sizeof(T) * capacity); \
if (temp == NULL) \
{ \
return RESULT_OUT_OF_MEMORY; \
} \
list->buffer = temp; \
return RESULT_SUCCESS; \
} \
static inline void name##_list_free(name##_list_t* list) \
{ \
if (list->buffer != NULL) \
{ \
free(list->buffer); \
list->buffer = NULL; \
list->count = 0; \
list->capacity = 0; \
} \
} \
static inline result_t name##_list_add(name##_list_t* list, T item) \
{ \
if (list->count >= list->capacity) \
{ \
uint32_t new_capacity = list->capacity == 0 ? 1 : list->capacity * 2; \
T* resized = (T*)realloc(list->buffer, sizeof(T) * new_capacity); \
if (resized == NULL) \
{ \
return RESULT_OUT_OF_MEMORY; \
} \
list->buffer = resized; \
list->capacity = new_capacity; \
} \
list->buffer[list->count++] = item; \
return RESULT_SUCCESS; \
} \
static inline void name##_list_remove_at(name##_list_t* list, uint32_t index) \
{ \
if (index >= list->count) \
{ \
return; \
} \
for (uint32_t i = index; i < list->count - 1; i++) \
{ \
list->buffer[i] = list->buffer[i + 1]; \
} \
list->count--; \
} \
static inline T* name##_list_get(name##_list_t* list, uint32_t index) \
{ \
if (index >= list->count) \
{ \
return NULL; \
} \
return &list->buffer[index]; \
} \
static inline result_t name##_list_index_of(name##_list_t* list, const T* item, uint32_t* index) \
{ \
for (uint32_t i = 0; i < list->count; i++) \
{ \
if (&list->buffer[i] == item) \
{ \
*index = i; \
return RESULT_SUCCESS; \
} \
} \
return RESULT_NOT_FOUND; \
}
#define QUEUE_DEF(T, name) \
typedef struct \
{ \
uint32_t head; \
uint32_t tail; \
uint32_t capacity; \
T* buffer; \
} name##_queue_t; \
static inline result_t name##_queue_init(name##_queue_t* queue, uint32_t capacity) \
{ \
capacity = capacity == 0 ? 1 : capacity; \
queue->head = 0; \
queue->tail = 0; \
queue->capacity = capacity; \
T* temp = (T*)malloc(sizeof(T) * capacity); \
if (temp == NULL) \
{ \
return RESULT_OUT_OF_MEMORY; \
} \
queue->buffer = temp; \
return RESULT_SUCCESS; \
} \
static inline void name##_queue_free(name##_queue_t* queue) \
{ \
if (queue->buffer != NULL) \
{ \
free(queue->buffer); \
queue->buffer = NULL; \
queue->head = 0; \
queue->tail = 0; \
queue->capacity = 0; \
} \
} \
static inline result_t name##_queue_enqueue(name##_queue_t* queue, T item) \
{ \
uint32_t next_tail = (queue->tail + 1) % queue->capacity; \
if (next_tail == queue->head) \
{ \
uint32_t new_capacity = queue->capacity * 2; \
T* resized = (T*)malloc(sizeof(T) * new_capacity); \
if (resized == NULL) \
{ \
return RESULT_OUT_OF_MEMORY; \
} \
uint32_t i = 0; \
while (queue->head != queue->tail) \
{ \
resized[i++] = queue->buffer[queue->head]; \
queue->head = (queue->head + 1) % queue->capacity; \
} \
free(queue->buffer); \
queue->buffer = resized; \
queue->head = 0; \
queue->tail = i; \
queue->capacity = new_capacity; \
next_tail = (queue->tail + 1) % queue->capacity; \
} \
queue->buffer[queue->tail] = item; \
queue->tail = next_tail; \
return RESULT_SUCCESS; \
} \
static inline result_t name##_queue_dequeue(name##_queue_t* queue, T* item_out) \
{ \
if (queue->head == queue->tail) \
{ \
return RESULT_NOT_FOUND; \
} \
*item_out = queue->buffer[queue->head]; \
queue->head = (queue->head + 1) % queue->capacity; \
return RESULT_SUCCESS; \
} \
static inline uint32_t name##_queue_size(const name##_queue_t* queue) \
{ \
if (queue->tail >= queue->head) \
{ \
return queue->tail - queue->head; \
} \
else \
{ \
return queue->capacity - queue->head + queue->tail; \
} \
}
#define SLOT_MAP_DEF(T, name) \
QUEUE_DEF(uint32_t, internal_##name##_slot_map_free_slots) \
typedef struct \
{ \
uint32_t count; \
uint32_t capacity; \
T* buffer; \
uint8_t* occupied; \
uint32_t* generation; \
internal_##name##_slot_map_free_slots_queue_t free_slots; \
} name##_slot_map_t; \
static inline result_t name##_slot_map_init(name##_slot_map_t* slot_map, uint32_t capacity) \
{ \
capacity = capacity == 0 ? 1 : capacity; \
slot_map->count = 0; \
slot_map->capacity = capacity; \
T* temp_buffer = (T*)malloc(sizeof(T) * capacity); \
if (temp_buffer == NULL) \
{ \
return RESULT_OUT_OF_MEMORY; \
} \
slot_map->buffer = temp_buffer; \
slot_map->occupied = (uint8_t*)calloc(capacity, sizeof(uint8_t)); \
if (slot_map->occupied == NULL) \
{ \
free(slot_map->buffer); \
return RESULT_OUT_OF_MEMORY; \
} \
slot_map->generation = (uint32_t*)calloc(capacity, sizeof(uint32_t)); \
if (slot_map->generation == NULL) \
{ \
free(slot_map->buffer); \
free(slot_map->occupied); \
return RESULT_OUT_OF_MEMORY; \
} \
result_t res = internal_##name##_slot_map_free_slots_queue_init(&slot_map->free_slots, capacity); \
if (res != RESULT_SUCCESS) \
{ \
free(slot_map->buffer); \
free(slot_map->occupied); \
free(slot_map->generation); \
return res; \
} \
for (uint32_t i = 0; i < capacity; i++) \
{ \
internal_##name##_slot_map_free_slots_queue_enqueue(&slot_map->free_slots, i); \
} \
return RESULT_SUCCESS; \
} \
static inline void name##_slot_map_free(name##_slot_map_t* slot_map) \
{ \
if (slot_map->buffer != NULL) \
{ \
free(slot_map->buffer); \
slot_map->buffer = NULL; \
} \
if (slot_map->occupied != NULL) \
{ \
free(slot_map->occupied); \
slot_map->occupied = NULL; \
} \
if (slot_map->generation != NULL) \
{ \
free(slot_map->generation); \
slot_map->generation = NULL; \
} \
internal_##name##_slot_map_free_slots_queue_free(&slot_map->free_slots); \
slot_map->count = 0; \
slot_map->capacity = 0; \
} \
static inline result_t name##_slot_map_add(name##_slot_map_t* slot_map, T item,uint32_t* out_id, uint32_t* out_generation)\
{ \
if (out_id == NULL || out_generation == NULL) \
{ \
return RESULT_INVALID_ARGUMENT; \
} \
uint32_t slot; \
result_t res = internal_##name##_slot_map_free_slots_queue_dequeue(&slot_map->free_slots, &slot); \
if (res != RESULT_SUCCESS) \
{ \
uint32_t old_capacity = slot_map->capacity; \
uint32_t new_capacity = slot_map->capacity * 2; \
T* resized_buffer = (T*)realloc(slot_map->buffer, sizeof(T) * new_capacity); \
if (resized_buffer == NULL) \
{ \
return RESULT_OUT_OF_MEMORY; \
} \
uint8_t* resized_occupied = (uint8_t*)realloc(slot_map->occupied, sizeof(uint8_t) * new_capacity);\
if (resized_occupied == NULL) \
{ \
return RESULT_OUT_OF_MEMORY; \
} \
uint32_t* resized_generation = (uint32_t*)realloc(slot_map->generation, sizeof(uint32_t) * new_capacity);\
if (resized_generation == NULL) \
{ \
return RESULT_OUT_OF_MEMORY; \
} \
slot_map->buffer = resized_buffer; \
slot_map->occupied = resized_occupied; \
slot_map->generation = resized_generation; \
memset(slot_map->occupied + old_capacity, 0, (size_t)(new_capacity - old_capacity) * sizeof(uint8_t)); \
memset(slot_map->generation + old_capacity, 0, (size_t)(new_capacity - old_capacity) * sizeof(uint32_t)); \
for (uint32_t i = slot_map->capacity; i < new_capacity; i++) \
{ \
internal_##name##_slot_map_free_slots_queue_enqueue(&slot_map->free_slots, i); \
} \
slot_map->capacity = new_capacity; \
res = internal_##name##_slot_map_free_slots_queue_dequeue(&slot_map->free_slots, &slot); \
if (res != RESULT_SUCCESS) \
{ \
return res; \
} \
} \
slot_map->buffer[slot] = item; \
slot_map->occupied[slot] = 1; \
slot_map->count++; \
*out_id = slot; \
*out_generation = slot_map->generation[slot]; \
return RESULT_SUCCESS; \
} \
static inline result_t name##_slot_map_remove(name##_slot_map_t* slot_map, uint32_t id, uint32_t generation) \
{ \
if (id >= slot_map->capacity) \
{ \
return RESULT_INVALID_ARGUMENT; \
} \
if (slot_map->occupied[id] == 0) \
{ \
return RESULT_NOT_FOUND; \
} \
if (slot_map->generation[id] != generation) \
{ \
return RESULT_INVALID_ARGUMENT; \
} \
slot_map->occupied[id] = 0; \
slot_map->generation[id]++; \
slot_map->count--; \
internal_##name##_slot_map_free_slots_queue_enqueue(&slot_map->free_slots, id); \
return RESULT_SUCCESS; \
} \
static inline T* name##_slot_map_get(name##_slot_map_t* slot_map, uint32_t id, uint32_t generation) \
{ \
if (id >= slot_map->capacity) \
{ \
return NULL; \
} \
if (slot_map->occupied[id] == 0) \
{ \
return NULL; \
} \
if (slot_map->generation[id] != generation) \
{ \
return NULL; \
} \
return &slot_map->buffer[id]; \
} \
static inline bool name##_slot_map_contains(name##_slot_map_t* slot_map, uint32_t id, uint32_t generation) \
{ \
if (id >= slot_map->capacity) \
{ \
return false; \
} \
if (slot_map->occupied[id] == 0) \
{ \
return false; \
} \
if (slot_map->generation[id] != generation) \
{ \
return false; \
} \
return true; \
}
#endif // COLLECTIONS_H

View File

@@ -0,0 +1,11 @@
#ifndef TYPES_H
#define TYPES_H
#include "Common/Collections.h"
HANDLE_DEF(mesh_model)
HANDLE_DEF(mesh_instance)
HANDLE_DEF(texture)
HANDLE_DEF(material)
#endif // TYPES_H