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/net/core/IZ_websocket.h | |||
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( | |||
game | |||
@@ -5,7 +5,7 @@ | |||
| Combat knife | Combat knife | Guerrilla | Switzerland | - | | - | - | - | - | - | - | - | | |||
| Oriental sword | Katana | Goemon | Japan | - | | - | - | - | - | - | - | - | | |||
| 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 | - | | |||
| 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 | - | - | - | | |||
@@ -144,7 +144,7 @@ IZ_AppResult IZ_AppRun(struct IZ_App* app, u8 argc, const char* argv[]) { | |||
app->ticks = SDL_GetTicks64(); | |||
// TODO do audio processing | |||
// TODO do networking | |||
// TODO do inbound networking | |||
if (IZ_AppHandleInputEvents(app)) { | |||
break; | |||
@@ -3,7 +3,7 @@ | |||
#include <SDL.h> | |||
#include <libwebsockets.h> | |||
#include "data/IZ_asset.h" | |||
#include "asset/IZ_asset.h" | |||
#include "memory/IZ_pool.h" | |||
#include "IZ_app_config.h" | |||
#include "IZ_app_input.h" | |||
@@ -1,81 +1,119 @@ | |||
#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; | |||
} | |||
/** | |||
* Initializes a list. | |||
* @param list - The list to initialize. | |||
*/ | |||
void IZ_ListInitialize(IZ_List* list) { | |||
list->root = NULL; | |||
list->length = 0; | |||
list->iterator = NULL; | |||
} | |||
/** | |||
* Performs cleanup on a list. | |||
* @param list - The list to clean up. | |||
*/ | |||
void IZ_ListTeardown(IZ_List* list) { | |||
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)); | |||
new_node->value = node_value; | |||
new_node->next = NULL; | |||
list->length += 1; | |||
if (!(list->root)) { | |||
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)) { | |||
// should we raise warnings here? | |||
return; | |||
} | |||
IZ_ListNode* iterator = list->root; | |||
list->iterator = &list->root; | |||
IZ_ListNode* previous_node = NULL; | |||
u64 index = 0; | |||
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; | |||
// we haven't found the node we're looking for, go to the next one | |||
continue; | |||
} | |||
if (previous_node) { | |||
previous_node->next = iterator->next; | |||
// this is not the first node in the list | |||
previous_node->next = (*list->iterator)->next; | |||
} 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; | |||
} 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)) { | |||
return NULL; | |||
} | |||
IZ_ListNode* iterator = list->root; | |||
list->iterator = &list->root; | |||
u64 index = 0; | |||
do { | |||
if (!filter(iterator, index)) { | |||
iterator = iterator->next; | |||
if (!filter(list->iterator, index, list)) { | |||
list->iterator = &((*list->iterator)->next); | |||
index += 1; | |||
continue; | |||
} | |||
return iterator; | |||
} while (iterator); | |||
return list->iterator; | |||
} while (*list->iterator); | |||
return NULL; | |||
} |
@@ -4,32 +4,65 @@ | |||
#include "../../common/IZ_common.h" | |||
#include "SDL_stdinc.h" | |||
/** | |||
* A node in a linked list. | |||
*/ | |||
typedef struct IZ_ListNode { | |||
/** | |||
* The value of the node. | |||
*/ | |||
void* value; | |||
/** | |||
* The next node in the list. | |||
*/ | |||
struct IZ_ListNode* next; | |||
} IZ_ListNode; | |||
/** | |||
* A singly-linked list. | |||
*/ | |||
typedef struct { | |||
/** | |||
* The first node in the list. | |||
*/ | |||
IZ_ListNode* root; | |||
/** | |||
* The number of nodes in the list. | |||
*/ | |||
u64 length; | |||
/** | |||
* The iterator for traversing the list. | |||
*/ | |||
IZ_ListNode** iterator; | |||
} 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*); | |||
/** | |||
* Performs cleanup on a 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 |
@@ -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 "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") { | |||
describe("list") { | |||
describe("Initialize") { | |||
static IZ_List list; | |||
after_each() { | |||
IZ_ListTeardown(&list); | |||
} | |||
it("sets root to NULL") { | |||
IZ_ListInitialize(&list); | |||
@@ -78,6 +83,14 @@ spec("data") { | |||
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") { | |||
static u64 value = 69420u; | |||
IZ_ListAppendNode(&list, &value); | |||
@@ -115,15 +128,19 @@ spec("data") { | |||
IZ_ListAppendNode(&list, &value2); | |||
} | |||
after_each() { | |||
IZ_ListTeardown(&list); | |||
} | |||
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") { | |||
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."); | |||
} | |||
} | |||
@@ -147,7 +164,7 @@ spec("data") { | |||
} | |||
it("removes first node satisfying the filter condition") { | |||
_IZ_ListDeleteFirstNode(&list, NodeExists); | |||
IZ_ListDeleteFirstNode(&list, NodeExists); | |||
check( | |||
mock_is_called(SDL_free), | |||
@@ -155,12 +172,12 @@ spec("data") { | |||
); | |||
check( | |||
_IZ_ListFindFirstNode(&list, NodeExists2), | |||
IZ_ListFindFirstNode(&list, NodeExists2), | |||
"Node supposed to be present in list is absent." | |||
); | |||
check( | |||
!_IZ_ListFindFirstNode(&list, NodeExists), | |||
!IZ_ListFindFirstNode(&list, NodeExists), | |||
"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]; | |||
IZ_ListNode* new_item = IZ_ListAppendNode(&pool->items, &(IZ_PoolItem) { | |||
IZ_ListNode** new_item = IZ_ListAppendNode(&pool->items, &(IZ_PoolItem) { | |||
.pointer = pointer, | |||
.args = args, | |||
.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->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) { | |||
IZ_ListDeleteFirstNode(__current_item, item)(&item->pool->items, IZ_PoolGetSameItem); | |||
IZ_ListDeleteFirstNode(&item->pool->items, IZ_PoolGetSameItem); | |||
} | |||
void IZ_PoolTeardown(IZ_Pool* pool) { | |||
@@ -5,7 +5,7 @@ | |||
#include "../../common/IZ_common.h" | |||
#include "../data/IZ_list.h" | |||
#define POOL_MAX_SIZE (1llu << 23) // 16MB | |||
#define POOL_MAX_SIZE (1llu << 26) // 64MB | |||
struct IZ_Pool; | |||
@@ -24,7 +24,7 @@ typedef struct { | |||
typedef struct IZ_Pool { | |||
IZ_List items; | |||
u64 next_address; | |||
u64 allocated_memory; | |||
size_t allocated_memory; | |||
size_t max_size; | |||
void* memory; | |||
} IZ_Pool; | |||