Added handle system and improve resource management.
This commit is contained in:
390
native/header/Common/Collections.h
Normal file
390
native/header/Common/Collections.h
Normal 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
|
||||
11
native/header/Common/Types.h
Normal file
11
native/header/Common/Types.h
Normal 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
|
||||
Reference in New Issue
Block a user