Browse Source

Update linked list code

Instead of using macros, use iterators built-in on each list for
filtering and traversing.
feature/data-structs
TheoryOfNekomata 1 year ago
parent
commit
c155348c40
12 changed files with 209 additions and 64 deletions
  1. +1
    -1
      CMakeLists.txt
  2. +1
    -1
      docs/weapons.md
  3. +1
    -1
      src/packages/game/IZ_app.c
  4. +1
    -1
      src/packages/game/IZ_app.h
  5. +0
    -0
      src/packages/game/asset/IZ_asset.c
  6. +0
    -0
      src/packages/game/asset/IZ_asset.h
  7. +67
    -29
      src/packages/game/data/IZ_list.c
  8. +43
    -10
      src/packages/game/data/IZ_list.h
  9. +57
    -0
      src/packages/game/data/README.md
  10. +31
    -14
      src/packages/game/data/data.test.c
  11. +5
    -5
      src/packages/game/memory/IZ_pool.c
  12. +2
    -2
      src/packages/game/memory/IZ_pool.h

+ 1
- 1
CMakeLists.txt View File

@@ -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


+ 1
- 1
docs/weapons.md View File

@@ -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 | - | - | - |


+ 1
- 1
src/packages/game/IZ_app.c View File

@@ -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;


+ 1
- 1
src/packages/game/IZ_app.h View File

@@ -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"


src/packages/game/data/IZ_asset.c → src/packages/game/asset/IZ_asset.c View File


src/packages/game/data/IZ_asset.h → src/packages/game/asset/IZ_asset.h View File


+ 67
- 29
src/packages/game/data/IZ_list.c View File

@@ -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;
}

+ 43
- 10
src/packages/game/data/IZ_list.h View File

@@ -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

+ 57
- 0
src/packages/game/data/README.md View File

@@ -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;
}
```

+ 31
- 14
src/packages/game/data/data.test.c View File

@@ -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."
);



+ 5
- 5
src/packages/game/memory/IZ_pool.c View File

@@ -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) {


+ 2
- 2
src/packages/game/memory/IZ_pool.h View File

@@ -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;


Loading…
Cancel
Save