Instead of using macros, use iterators built-in on each list for filtering and traversing.feature/data-structs
@@ -93,7 +93,7 @@ add_executable( | |||||
src/packages/game/util/IZ_midi.h | src/packages/game/util/IZ_midi.h | ||||
src/packages/net/core/IZ_websocket.h | src/packages/net/core/IZ_websocket.h | ||||
src/packages/net/core/IZ_websocket.c | src/packages/net/core/IZ_websocket.c | ||||
src/packages/net/IZ_net_client.c src/packages/net/IZ_net_client.h src/packages/game/IZ_app_net.c src/packages/game/IZ_app_net.h src/packages/game/IZ_app_video.c src/packages/game/IZ_app_video.h src/packages/game/IZ_subsystem.h src/packages/game/IZ_app_input.c src/packages/game/IZ_app_input.h src/packages/game/IZ_app_config.c src/packages/game/IZ_app_config.h src/packages/game/data/IZ_asset.c src/packages/game/data/IZ_asset.h) | |||||
src/packages/net/IZ_net_client.c src/packages/net/IZ_net_client.h src/packages/game/IZ_app_net.c src/packages/game/IZ_app_net.h src/packages/game/IZ_app_video.c src/packages/game/IZ_app_video.h src/packages/game/IZ_subsystem.h src/packages/game/IZ_app_input.c src/packages/game/IZ_app_input.h src/packages/game/IZ_app_config.c src/packages/game/IZ_app_config.h src/packages/game/asset/IZ_asset.c src/packages/game/asset/IZ_asset.h) | |||||
target_link_libraries( | target_link_libraries( | ||||
game | game | ||||
@@ -5,7 +5,7 @@ | |||||
| Combat knife | Combat knife | Guerrilla | Switzerland | - | | - | - | - | - | - | - | - | | | Combat knife | Combat knife | Guerrilla | Switzerland | - | | - | - | - | - | - | - | - | | ||||
| Oriental sword | Katana | Goemon | Japan | - | | - | - | - | - | - | - | - | | | Oriental sword | Katana | Goemon | Japan | - | | - | - | - | - | - | - | - | | ||||
| Pistol | Beretta 92FS (M9) | Servant | Italy | 9x19mm Parabellum | 15 | 16 | - | 381 | 217 | 38 | 137 | Can be dual-wielded | | | Pistol | Beretta 92FS (M9) | Servant | Italy | 9x19mm Parabellum | 15 | 16 | - | 381 | 217 | 38 | 137 | Can be dual-wielded | | ||||
| Pistol | Magnum Research Desert Eagle MK XIX | Hitman | United States | .50 Action Express | 7 | 8 | - | 470 | 273.1 | - | - | Uses 20-round extended magazine (from normal 15 rounds) | | |||||
| Pistol | Magnum Research Desert Eagle MK XIX | Hitman | United States | .50 Action Express | 7 | 8 | - | 470 | 273.1 | - | - | Uses 20-round extended magazine (from normal 12 rounds) | | |||||
| Pistol | H&K USP Elite .45 | Ordinator | Germany | .45 ACP | 20 | 10 | - | 350 | 239 | 29 | 151 | - | | | Pistol | H&K USP Elite .45 | Ordinator | Germany | .45 ACP | 20 | 10 | - | 350 | 239 | 29 | 151 | - | | ||||
| Sub-machine gun | H&K MP5A5 | Operator | West Germany | 9x19mm Parabellum | 30 | 10 | 800 | 400 | 680 (stock extended), 504.22 (stock collapsed) | 50 | 260 | - | | | Sub-machine gun | H&K MP5A5 | Operator | West Germany | 9x19mm Parabellum | 30 | 10 | 800 | 400 | 680 (stock extended), 504.22 (stock collapsed) | 50 | 260 | - | | ||||
| Sub-machine gun | H&K MP5KA1 | Tactician | West Germany | 9x19mm Parabellum | 30 | 10 | 900 | 375 | 325 | - | - | - | | | Sub-machine gun | H&K MP5KA1 | Tactician | West Germany | 9x19mm Parabellum | 30 | 10 | 900 | 375 | 325 | - | - | - | | ||||
@@ -144,7 +144,7 @@ IZ_AppResult IZ_AppRun(struct IZ_App* app, u8 argc, const char* argv[]) { | |||||
app->ticks = SDL_GetTicks64(); | app->ticks = SDL_GetTicks64(); | ||||
// TODO do audio processing | // TODO do audio processing | ||||
// TODO do networking | |||||
// TODO do inbound networking | |||||
if (IZ_AppHandleInputEvents(app)) { | if (IZ_AppHandleInputEvents(app)) { | ||||
break; | break; | ||||
@@ -3,7 +3,7 @@ | |||||
#include <SDL.h> | #include <SDL.h> | ||||
#include <libwebsockets.h> | #include <libwebsockets.h> | ||||
#include "data/IZ_asset.h" | |||||
#include "asset/IZ_asset.h" | |||||
#include "memory/IZ_pool.h" | #include "memory/IZ_pool.h" | ||||
#include "IZ_app_config.h" | #include "IZ_app_config.h" | ||||
#include "IZ_app_input.h" | #include "IZ_app_input.h" | ||||
@@ -1,81 +1,119 @@ | |||||
#include "IZ_list.h" | #include "IZ_list.h" | ||||
bool IZ_ListFindFilterAlwaysTrue(IZ_ListNode* _node, u64 _index) { | |||||
bool IZ_ListFindFilterAlwaysTrue(IZ_ListNode** _node, u64 _index, IZ_List* _list) { | |||||
return true; | return true; | ||||
} | } | ||||
/** | |||||
* Initializes a list. | |||||
* @param list - The list to initialize. | |||||
*/ | |||||
void IZ_ListInitialize(IZ_List* list) { | void IZ_ListInitialize(IZ_List* list) { | ||||
list->root = NULL; | list->root = NULL; | ||||
list->length = 0; | list->length = 0; | ||||
list->iterator = NULL; | |||||
} | } | ||||
/** | |||||
* Performs cleanup on a list. | |||||
* @param list - The list to clean up. | |||||
*/ | |||||
void IZ_ListTeardown(IZ_List* list) { | void IZ_ListTeardown(IZ_List* list) { | ||||
while (list->root) { | while (list->root) { | ||||
_IZ_ListDeleteFirstNode(list, IZ_ListFindFilterAlwaysTrue); | |||||
IZ_ListDeleteFirstNode(list, IZ_ListFindFilterAlwaysTrue); | |||||
} | } | ||||
list->iterator = NULL; | |||||
list->length = 0; | |||||
} | } | ||||
IZ_ListNode* IZ_ListAppendNode(IZ_List* list, void* node_value) { | |||||
/** | |||||
* Appends a node to the end of the list. | |||||
* @param list - The list to append to. | |||||
* @return Pointer to the newly created node. | |||||
*/ | |||||
IZ_ListNode** IZ_ListAppendNode(IZ_List* list, void* node_value) { | |||||
IZ_ListNode* new_node = SDL_malloc(sizeof(IZ_ListNode)); | IZ_ListNode* new_node = SDL_malloc(sizeof(IZ_ListNode)); | ||||
new_node->value = node_value; | new_node->value = node_value; | ||||
new_node->next = NULL; | |||||
list->length += 1; | |||||
if (!(list->root)) { | if (!(list->root)) { | ||||
list->root = new_node; | list->root = new_node; | ||||
} else { | |||||
IZ_ListNode *last_node = list->root; | |||||
while (last_node->next) { | |||||
last_node = last_node->next; | |||||
} | |||||
last_node->next = new_node; | |||||
return &list->root; | |||||
} | } | ||||
new_node->next = NULL; | |||||
list->length += 1; | |||||
return new_node; | |||||
IZ_ListNode *last_node = list->root; | |||||
while (last_node->next) { | |||||
last_node = last_node->next; | |||||
} | |||||
last_node->next = new_node; | |||||
return &last_node->next; | |||||
} | } | ||||
void _IZ_ListDeleteFirstNode(IZ_List* list, IZ_ListFindFilter filter) { | |||||
/** | |||||
* Deletes the first node in the list that matches the filter. | |||||
* @param list - The list to delete from. | |||||
* @param filter - The filter to use to find the node to delete. | |||||
*/ | |||||
void IZ_ListDeleteFirstNode(IZ_List* list, IZ_ListFindPredicate filter) { | |||||
if (!(list && list->root)) { | if (!(list && list->root)) { | ||||
// should we raise warnings here? | |||||
return; | return; | ||||
} | } | ||||
IZ_ListNode* iterator = list->root; | |||||
list->iterator = &list->root; | |||||
IZ_ListNode* previous_node = NULL; | IZ_ListNode* previous_node = NULL; | ||||
u64 index = 0; | u64 index = 0; | ||||
do { | do { | ||||
if (!filter(iterator, index)) { | |||||
previous_node = iterator; | |||||
iterator = iterator->next; | |||||
if (!filter(list->iterator, index, list)) { | |||||
previous_node = *list->iterator; | |||||
list->iterator = &((*list->iterator)->next); | |||||
index += 1; | index += 1; | ||||
// we haven't found the node we're looking for, go to the next one | |||||
continue; | continue; | ||||
} | } | ||||
if (previous_node) { | if (previous_node) { | ||||
previous_node->next = iterator->next; | |||||
// this is not the first node in the list | |||||
previous_node->next = (*list->iterator)->next; | |||||
} else { | } else { | ||||
list->root = iterator->next; | |||||
// this is the first node, set it as new root | |||||
list->root = (*list->iterator)->next; | |||||
// if there is only one item in the list, root, gets set to NULL | |||||
} | } | ||||
SDL_free(iterator); | |||||
iterator = NULL; | |||||
list->length -= 1; | |||||
SDL_free(*list->iterator); | |||||
list->iterator = NULL; | |||||
if (list->length > 0) { | |||||
// avoid underflow | |||||
list->length -= 1; | |||||
} | |||||
return; | return; | ||||
} while (iterator); | |||||
} while (*list->iterator); | |||||
} | } | ||||
IZ_ListNode* _IZ_ListFindFirstNode(IZ_List* list, IZ_ListFindFilter filter) { | |||||
/** | |||||
* Finds the first node in the list that matches the filter. | |||||
* @param list - The list to search. | |||||
* @param filter - The filter to use to find the node. | |||||
* @return Pointer to the node that matches the filter. | |||||
*/ | |||||
IZ_ListNode** IZ_ListFindFirstNode(IZ_List* list, IZ_ListFindPredicate filter) { | |||||
if (!(list && list->root)) { | if (!(list && list->root)) { | ||||
return NULL; | return NULL; | ||||
} | } | ||||
IZ_ListNode* iterator = list->root; | |||||
list->iterator = &list->root; | |||||
u64 index = 0; | u64 index = 0; | ||||
do { | do { | ||||
if (!filter(iterator, index)) { | |||||
iterator = iterator->next; | |||||
if (!filter(list->iterator, index, list)) { | |||||
list->iterator = &((*list->iterator)->next); | |||||
index += 1; | index += 1; | ||||
continue; | continue; | ||||
} | } | ||||
return iterator; | |||||
} while (iterator); | |||||
return list->iterator; | |||||
} while (*list->iterator); | |||||
return NULL; | return NULL; | ||||
} | } |
@@ -4,32 +4,65 @@ | |||||
#include "../../common/IZ_common.h" | #include "../../common/IZ_common.h" | ||||
#include "SDL_stdinc.h" | #include "SDL_stdinc.h" | ||||
/** | |||||
* A node in a linked list. | |||||
*/ | |||||
typedef struct IZ_ListNode { | typedef struct IZ_ListNode { | ||||
/** | |||||
* The value of the node. | |||||
*/ | |||||
void* value; | void* value; | ||||
/** | |||||
* The next node in the list. | |||||
*/ | |||||
struct IZ_ListNode* next; | struct IZ_ListNode* next; | ||||
} IZ_ListNode; | } IZ_ListNode; | ||||
/** | |||||
* A singly-linked list. | |||||
*/ | |||||
typedef struct { | typedef struct { | ||||
/** | |||||
* The first node in the list. | |||||
*/ | |||||
IZ_ListNode* root; | IZ_ListNode* root; | ||||
/** | |||||
* The number of nodes in the list. | |||||
*/ | |||||
u64 length; | u64 length; | ||||
/** | |||||
* The iterator for traversing the list. | |||||
*/ | |||||
IZ_ListNode** iterator; | |||||
} IZ_List; | } IZ_List; | ||||
typedef bool IZ_ListFindFilter(IZ_ListNode*, u64); | |||||
typedef bool IZ_ListFindPredicate(IZ_ListNode**, u64, IZ_List*); | |||||
/** | |||||
* Initializes a list. | |||||
*/ | |||||
void IZ_ListInitialize(IZ_List*); | void IZ_ListInitialize(IZ_List*); | ||||
/** | |||||
* Performs cleanup on a list. | |||||
*/ | |||||
void IZ_ListTeardown(IZ_List*); | void IZ_ListTeardown(IZ_List*); | ||||
IZ_ListNode* IZ_ListAppendNode(IZ_List*, void*); | |||||
/** | |||||
* Appends a node to the end of the list. | |||||
* @return Pointer to the newly created node. | |||||
*/ | |||||
IZ_ListNode** IZ_ListAppendNode(IZ_List*, void*); | |||||
#define IZ_ListFilterFunctionArgs(X) static X = NULL; | |||||
/** | |||||
* Deletes the first node in the list that matches the filter. | |||||
*/ | |||||
void IZ_ListDeleteFirstNode(IZ_List*, IZ_ListFindPredicate); | |||||
void _IZ_ListDeleteFirstNode(IZ_List*, IZ_ListFindFilter); | |||||
#define IZ_ListDeleteFirstNode(X, Y) X = Y; _IZ_ListDeleteFirstNode | |||||
IZ_ListNode* _IZ_ListFindFirstNode(IZ_List*, IZ_ListFindFilter); | |||||
#define IZ_ListFindFirstNode(X, Y) X = Y; _IZ_ListFindFirstNode | |||||
/** | |||||
* Finds the first node in the list that matches the filter. | |||||
* @return Pointer to the node that matches the filter. | |||||
*/ | |||||
IZ_ListNode** IZ_ListFindFirstNode(IZ_List*, IZ_ListFindPredicate); | |||||
#endif | #endif |
@@ -0,0 +1,57 @@ | |||||
# `data` | |||||
Fundamental functions for various data structures used in the project. | |||||
## `list` | |||||
A singly-linked list is used on certain parts of the game, such as the memory pool's entries of currently allocated game | |||||
objects. | |||||
Here is how to properly use a list: | |||||
```c | |||||
#include <stdbool.h> | |||||
#include "IZ_list.h" | |||||
// find predicates | |||||
bool FindNodeWithValueOne(IZ_ListNode**, unsigned long long, IZ_List*); | |||||
bool FindNodeWithValueTwo(IZ_ListNode**, unsigned long long, IZ_List*); | |||||
int main() { | |||||
IZ_List list; | |||||
// important to initialize lists first, else there may be garbage data in the list members. | |||||
IZ_ListInitialize(&list); | |||||
// lists should accept any type of value (void*), so the implementor is free to use whatever type they want | |||||
int node1_value = 1; | |||||
IZ_ListAppendNode(&list, &node1_value); | |||||
int node2_value = 2; | |||||
IZ_ListAppendNode(&list, &node2_value); | |||||
// pass predicate functions for finding nodes | |||||
ListNode* find_node; | |||||
find_node = IZ_ListFindFirstNode(&list, &FindNodeWithValueTwo); | |||||
if (find_node != NULL) { | |||||
printf("Found node with value 2!\n"); | |||||
} | |||||
// deletions are done only on one node at a time | |||||
IZ_ListDeleteFirstNode(&list, &FindNodeWithValueOne); | |||||
// teardown takes care of de-allocating nodes and making sure data cannot be accessed sensibly anymore. | |||||
IZ_ListTeardown(&list); | |||||
return 0; | |||||
} | |||||
bool FindNodeWithValueOne(IZ_ListNode** node, unsigned long long index, IZ_List* list) { | |||||
return (*node)->value == 1; | |||||
} | |||||
bool FindNodeWithValueTwo(IZ_ListNode** node, unsigned long long index, IZ_List* list) { | |||||
return (*node)->value == 2; | |||||
} | |||||
``` |
@@ -3,22 +3,27 @@ | |||||
#include "../../../__mocks__/SDL_stdinc.mock.h" | #include "../../../__mocks__/SDL_stdinc.mock.h" | ||||
#include "IZ_list.h" | #include "IZ_list.h" | ||||
IZ_ListFilterFunctionArgs(IZ_ListNode* __current_item_NodeExists) bool NodeExists(IZ_ListNode* node, u64 _index) { | |||||
return *((u64*) node->value) == 42069; | |||||
bool NodeExists(IZ_ListNode** node, u64 _index, IZ_List* list) { | |||||
return *((u64*) (*node)->value) == 42069; | |||||
} | } | ||||
IZ_ListFilterFunctionArgs(IZ_ListNode* __current_item_NodeExists2) bool NodeExists2(IZ_ListNode* node, u64 _index) { | |||||
return *((u64*) node->value) == 69420; | |||||
bool NodeExists2(IZ_ListNode** node, u64 _index, IZ_List* list) { | |||||
return *((u64*) (*node)->value) == 69420; | |||||
} | } | ||||
IZ_ListFilterFunctionArgs(IZ_ListNode* __current_item_NodeDoesNotExist) bool NodeDoesNotExist(IZ_ListNode* node, u64 _index) { | |||||
return *((u64*) node->value) == 55555; | |||||
bool NodeDoesNotExist(IZ_ListNode** node, u64 _index, IZ_List* list) { | |||||
return *((u64*) (*node)->value) == 55555; | |||||
} | } | ||||
spec("data") { | spec("data") { | ||||
describe("list") { | describe("list") { | ||||
describe("Initialize") { | describe("Initialize") { | ||||
static IZ_List list; | static IZ_List list; | ||||
after_each() { | |||||
IZ_ListTeardown(&list); | |||||
} | |||||
it("sets root to NULL") { | it("sets root to NULL") { | ||||
IZ_ListInitialize(&list); | IZ_ListInitialize(&list); | ||||
@@ -78,6 +83,14 @@ spec("data") { | |||||
mock_reset(SDL_malloc); | mock_reset(SDL_malloc); | ||||
} | } | ||||
after_each() { | |||||
IZ_ListTeardown(&list); | |||||
} | |||||
after_each() { | |||||
IZ_ListTeardown(&list2); | |||||
} | |||||
it("appends new node to empty list and sets it as root") { | it("appends new node to empty list and sets it as root") { | ||||
static u64 value = 69420u; | static u64 value = 69420u; | ||||
IZ_ListAppendNode(&list, &value); | IZ_ListAppendNode(&list, &value); | ||||
@@ -115,15 +128,19 @@ spec("data") { | |||||
IZ_ListAppendNode(&list, &value2); | IZ_ListAppendNode(&list, &value2); | ||||
} | } | ||||
after_each() { | |||||
IZ_ListTeardown(&list); | |||||
} | |||||
it("retrieves first node satisfying the filter condition") { | it("retrieves first node satisfying the filter condition") { | ||||
static IZ_ListNode* node; | |||||
node = _IZ_ListFindFirstNode(&list, NodeExists); | |||||
check(*((u64*) node->value) == 42069u, "Existing node not found."); | |||||
static IZ_ListNode** node; | |||||
node = IZ_ListFindFirstNode(&list, NodeExists); | |||||
check(*((u64*) (*node)->value) == 42069u, "Existing node not found."); | |||||
} | } | ||||
it("returns NULL when all nodes do not satisfy the filter condition") { | it("returns NULL when all nodes do not satisfy the filter condition") { | ||||
static IZ_ListNode* node; | |||||
node = _IZ_ListFindFirstNode(&list, NodeDoesNotExist); | |||||
static IZ_ListNode** node; | |||||
node = IZ_ListFindFirstNode(&list, NodeDoesNotExist); | |||||
check(node == NULL, "Non-existing node found."); | check(node == NULL, "Non-existing node found."); | ||||
} | } | ||||
} | } | ||||
@@ -147,7 +164,7 @@ spec("data") { | |||||
} | } | ||||
it("removes first node satisfying the filter condition") { | it("removes first node satisfying the filter condition") { | ||||
_IZ_ListDeleteFirstNode(&list, NodeExists); | |||||
IZ_ListDeleteFirstNode(&list, NodeExists); | |||||
check( | check( | ||||
mock_is_called(SDL_free), | mock_is_called(SDL_free), | ||||
@@ -155,12 +172,12 @@ spec("data") { | |||||
); | ); | ||||
check( | check( | ||||
_IZ_ListFindFirstNode(&list, NodeExists2), | |||||
IZ_ListFindFirstNode(&list, NodeExists2), | |||||
"Node supposed to be present in list is absent." | "Node supposed to be present in list is absent." | ||||
); | ); | ||||
check( | check( | ||||
!_IZ_ListFindFirstNode(&list, NodeExists), | |||||
!IZ_ListFindFirstNode(&list, NodeExists), | |||||
"Deleted node still present in list." | "Deleted node still present in list." | ||||
); | ); | ||||
@@ -26,7 +26,7 @@ IZ_PoolItem* IZ_PoolAllocate(IZ_Pool* pool, IZ_PoolAllocationArgs args) { | |||||
} | } | ||||
void* pointer = &pool->memory[pool->next_address]; | void* pointer = &pool->memory[pool->next_address]; | ||||
IZ_ListNode* new_item = IZ_ListAppendNode(&pool->items, &(IZ_PoolItem) { | |||||
IZ_ListNode** new_item = IZ_ListAppendNode(&pool->items, &(IZ_PoolItem) { | |||||
.pointer = pointer, | .pointer = pointer, | ||||
.args = args, | .args = args, | ||||
.pool = pool, | .pool = pool, | ||||
@@ -34,15 +34,15 @@ IZ_PoolItem* IZ_PoolAllocate(IZ_Pool* pool, IZ_PoolAllocationArgs args) { | |||||
pool->next_address = (pool->next_address + args.size) % POOL_MAX_SIZE; | pool->next_address = (pool->next_address + args.size) % POOL_MAX_SIZE; | ||||
pool->allocated_memory += args.size; | pool->allocated_memory += args.size; | ||||
return new_item->value; | |||||
return (*new_item)->value; | |||||
} | } | ||||
IZ_ListFilterFunctionArgs(IZ_PoolItem* __current_item) bool IZ_PoolGetSameItem(IZ_ListNode* node, u64 _index) { | |||||
return node->value == __current_item; | |||||
bool IZ_PoolGetSameItem(IZ_ListNode** node, u64 _index, IZ_List* list) { | |||||
return (*node)->value == (*list->iterator)->value; | |||||
} | } | ||||
void IZ_PoolDeallocate(IZ_PoolItem* item) { | void IZ_PoolDeallocate(IZ_PoolItem* item) { | ||||
IZ_ListDeleteFirstNode(__current_item, item)(&item->pool->items, IZ_PoolGetSameItem); | |||||
IZ_ListDeleteFirstNode(&item->pool->items, IZ_PoolGetSameItem); | |||||
} | } | ||||
void IZ_PoolTeardown(IZ_Pool* pool) { | void IZ_PoolTeardown(IZ_Pool* pool) { | ||||
@@ -5,7 +5,7 @@ | |||||
#include "../../common/IZ_common.h" | #include "../../common/IZ_common.h" | ||||
#include "../data/IZ_list.h" | #include "../data/IZ_list.h" | ||||
#define POOL_MAX_SIZE (1llu << 23) // 16MB | |||||
#define POOL_MAX_SIZE (1llu << 26) // 64MB | |||||
struct IZ_Pool; | struct IZ_Pool; | ||||
@@ -24,7 +24,7 @@ typedef struct { | |||||
typedef struct IZ_Pool { | typedef struct IZ_Pool { | ||||
IZ_List items; | IZ_List items; | ||||
u64 next_address; | u64 next_address; | ||||
u64 allocated_memory; | |||||
size_t allocated_memory; | |||||
size_t max_size; | size_t max_size; | ||||
void* memory; | void* memory; | ||||
} IZ_Pool; | } IZ_Pool; | ||||