Переглянути джерело

Add other list functions

Implement and fix list functions and their tests.
master
TheoryOfNekomata 1 рік тому
джерело
коміт
a05e605ec4
6 змінених файлів з 395 додано та 85 видалено
  1. +2
    -0
      docs/meta/coding-principles.md
  2. +161
    -48
      src/packages/game/data/IZ_list.c
  3. +29
    -4
      src/packages/game/data/IZ_list.h
  4. +194
    -26
      src/packages/game/data/data.test.c
  5. +7
    -5
      src/packages/game/memory/IZ_pool.c
  6. +2
    -2
      src/packages/game/memory/memory.test.c

+ 2
- 0
docs/meta/coding-principles.md Переглянути файл

@@ -0,0 +1,2 @@
- Always check bounds before performing mutations (i.e. CRUD operations).
- Prefer declaring out variables over malloc'd variables.

+ 161
- 48
src/packages/game/data/IZ_list.c Переглянути файл

@@ -1,5 +1,70 @@
#include <stdio.h>
#include "IZ_list.h"

void IZ_ListDoDeleteNode(IZ_ListNode* node) {
if (!(node && node->list)) {
// should we raise warnings here?
return;
}

if (node->previous) {
node->previous->next = node->next;
} else {
if (node->next) {
node->next->previous = NULL;
}
node->list->root = node->next;
}

if (node->list->length > 0) {
node->list->length -= 1;
}

IZ_free(node);
}

void IZ_ListDoAppendNode(IZ_List* list, void* node_value, IZ_ListNode** new_node_ref) {
IZ_ListNode* new_node = IZ_malloc(sizeof(IZ_ListNode));
new_node->value = node_value;
new_node->next = NULL;
new_node->list = list;
new_node->list->length += 1;

if (!(new_node->list->root)) {
new_node->previous = NULL;
new_node->list->root = new_node;
if (new_node_ref) {
*new_node_ref = new_node;
}
return;
}

IZ_ListNode** cursor_node = &new_node->list->root;
while ((*cursor_node)->next) {
cursor_node = &(*cursor_node)->next;
}

new_node->previous = *cursor_node;
(*cursor_node)->next = new_node;
if (new_node_ref) {
*new_node_ref = new_node;
}
}

#ifdef IZ_DEBUG
void IZ_ListPrintNodeValues(IZ_List* list) {
list->iterator = &list->root;
u64 index = 0;
printf("\nlist@%p\n", list);
do {
printf(" %llu@%p:%u\n", index, *list->iterator, *((unsigned int*)(*list->iterator)->value));
index += 1;
list->iterator = &(*list->iterator)->next;
} while (*list->iterator);
list->iterator = NULL;
}
#endif

/**
* Initializes a list.
* @param list - The list to initialize.
@@ -16,7 +81,7 @@ void IZ_ListInitialize(IZ_List* list) {
*/
void IZ_ListTeardown(IZ_List* list) {
while (list->length > 0) {
IZ_ListDeleteNode(list->root);
IZ_ListDoDeleteNode(list->root);
}
IZ_free(list);
}
@@ -24,29 +89,11 @@ void IZ_ListTeardown(IZ_List* list) {
/**
* Appends a node to the end of the list.
* @param list - The list to append to.
* @param node_value - The value of the node to append.
* @return Pointer to the newly created node.
*/
IZ_ListNode** IZ_ListAppendNode(IZ_List* list, void* node_value) {
IZ_ListNode* new_node = IZ_malloc(sizeof(IZ_ListNode));
new_node->value = node_value;
new_node->next = NULL;
new_node->list = list;
new_node->list->length += 1;

if (!(list->root)) {
new_node->previous = NULL;
new_node->list->root = new_node;
return &new_node->list->root;
}

IZ_ListNode *last_node = new_node->list->root;
while (last_node->next) {
last_node = last_node->next;
}

new_node->previous = last_node;
last_node->next = new_node;
return &last_node->next;
void IZ_ListAppendNode(IZ_List* list, void* node_value, IZ_ListNode** new_node) {
return IZ_ListDoAppendNode(list, node_value, new_node);
}

/**
@@ -54,27 +101,7 @@ IZ_ListNode** IZ_ListAppendNode(IZ_List* list, void* node_value) {
* @param node - The node to delete.
*/
void IZ_ListDeleteNode(IZ_ListNode* node) {
if (!node) {
// should we raise warnings here?
return;
}

if (node->previous) {
node->previous->next = node->next;
} else {
if (node->next) {
node->next->previous = NULL;
}
if (node->list) {
node->list->root = node->next;
}
}

if (node->list && node->list->length > 0) {
node->list->length -= 1;
}

IZ_free(node);
IZ_ListDoDeleteNode(node);
}

/**
@@ -82,13 +109,16 @@ void IZ_ListDeleteNode(IZ_ListNode* node) {
* @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.
* @see IZ_ListFindPredicate
* @see IZ_ListFindAllNodes
* @note This function will set the list's iterator to the node that was found. Ensure that the iterator is previously
* set to an existing node in the list before calling this function to know where to begin the search.
*/
IZ_ListNode** IZ_ListFindFirstNode(IZ_List* list, IZ_ListFindPredicate filter) {
void IZ_ListFindFirstNode(IZ_List* list, IZ_ListFindPredicate filter, IZ_ListNode** found_node) {
if (!(list && list->root)) {
return NULL;
return;
}

list->iterator = &list->root;
u64 index = 0;
do {
if (!filter(list->iterator, index, list)) {
@@ -96,8 +126,91 @@ IZ_ListNode** IZ_ListFindFirstNode(IZ_List* list, IZ_ListFindPredicate filter) {
index += 1;
continue;
}
return list->iterator;
*found_node = *list->iterator;
return;
} while (*list->iterator);

return NULL;
}

/**
* Finds all nodes in the list that match the filter.
* @param list - The list to search.
* @param filter - The filter to use to find the node.
* @param found_nodes - The list to append the found nodes to.
* @return New list containing nodes that match the filter.
* @see IZ_ListFindPredicate
* @see IZ_ListFindFirstNode
* @note This function will set the list's iterator to the node that was found. Ensure that the iterator is previously
* set to an existing node in the list before calling this function to know where to begin the search.
*/
void IZ_ListFindAllNodes(IZ_List* list, IZ_ListFindPredicate filter, IZ_List* found_nodes) {
if (!(list && list->root)) {
return;
}

list->iterator = &list->root;
u64 index;
for (index = 0; index < list->length; index += 1) {
if (filter(list->iterator, index, list)) {
IZ_ListDoAppendNode(found_nodes, (*list->iterator)->value, NULL);
}

list->iterator = &((*list->iterator)->next);
}
}

/**
* Inserts a node at the specified index.
* @param list - The list to append to.
* @param node_value - The value of the node to append.
* @param dest_index - The index to insert the node at.
* @return Pointer to the newly created node.
*/
void IZ_ListInsertNodeAtIndex(IZ_List* list, void* node_value, u64 dest_index, IZ_ListNode** new_node_ref) {
if (dest_index > list->length) {
// to consumer: check your bounds first!
return;
}

if (dest_index == list->length) {
return IZ_ListDoAppendNode(list, node_value, new_node_ref);
}

IZ_ListNode* new_node = IZ_malloc(sizeof(IZ_ListNode));
new_node->value = node_value;
new_node->list = list;

if (!(new_node->list->root)) {
new_node->previous = NULL;
new_node->next = NULL;
new_node->list->root = new_node;
if (new_node_ref) {
*new_node_ref = new_node;
}
return;
}

IZ_ListNode** cursor_node = NULL;
u64 index;
for (
index = 0, cursor_node = &new_node->list->root;
index < dest_index;
index += 1, cursor_node = &((*cursor_node)->next)
);

new_node->next = *cursor_node;
new_node->previous = (*cursor_node)->previous;

if (dest_index == 0) {
new_node->list->root = new_node;
} else if (dest_index < list->length) {
(*cursor_node)->previous->next = new_node;
} else {
(*cursor_node)->next = new_node;
}

new_node->list->length += 1;
if (new_node_ref) {
*new_node_ref = new_node;
}
}

+ 29
- 4
src/packages/game/data/IZ_list.h Переглянути файл

@@ -10,8 +10,13 @@ struct IZ_List;
* A node in a linked list.
*/
typedef struct IZ_ListNode {
/**
* The list that the node belongs to.
*/
struct IZ_List* list;

/**
* The previous node in the list.
*/
struct IZ_ListNode* previous;
/**
* The value of the node.
@@ -24,7 +29,7 @@ typedef struct IZ_ListNode {
} IZ_ListNode;

/**
* A singly-linked list.
* A doubly-linked list.
*/
typedef struct IZ_List {
/**
@@ -43,6 +48,10 @@ typedef struct IZ_List {

typedef bool IZ_ListFindPredicate(IZ_ListNode**, u64, IZ_List*);

#ifdef IZ_DEBUG
void IZ_ListPrintNodeValues(IZ_List*);
#endif

/**
* Initializes a list.
*/
@@ -57,7 +66,7 @@ void IZ_ListTeardown(IZ_List*);
* Appends a node to the end of the list.
* @return Pointer to the newly created node.
*/
IZ_ListNode** IZ_ListAppendNode(IZ_List*, void*);
void IZ_ListAppendNode(IZ_List*, void*, IZ_ListNode**);

/**
* Deletes the first node in the list that matches the filter.
@@ -67,7 +76,23 @@ void IZ_ListDeleteNode(IZ_ListNode*);
/**
* Finds the first node in the list that matches the filter.
* @return Pointer to the node that matches the filter.
* @see IZ_ListFindPredicate
* @see IZ_ListFindAllNodes
*/
void IZ_ListFindFirstNode(IZ_List*, IZ_ListFindPredicate, IZ_ListNode**);

/**
* Finds all nodes in the list that match the filter.
* @return New list containing nodes that match the filter.
* @see IZ_ListFindPredicate
* @see IZ_ListFindFirstNode
*/
void IZ_ListFindAllNodes(IZ_List*, IZ_ListFindPredicate, IZ_List*);

/**
* Inserts a node at the specified index.
* @return Pointer to the newly created node.
*/
IZ_ListNode** IZ_ListFindFirstNode(IZ_List*, IZ_ListFindPredicate);
void IZ_ListInsertNodeAtIndex(IZ_List*, void*, u64, IZ_ListNode**);

#endif

+ 194
- 26
src/packages/game/data/data.test.c Переглянути файл

@@ -11,10 +11,18 @@ bool NodeExists2(IZ_ListNode** node, u64 _index, IZ_List* list) {
return *((u64*) (*node)->value) == 69420;
}

bool NodeExists3(IZ_ListNode** node, u64 _index, IZ_List* list) {
return *((u64*) (*node)->value) == 69069;
}

bool NodeDoesNotExist(IZ_ListNode** node, u64 _index, IZ_List* list) {
return *((u64*) (*node)->value) == 55555;
}

bool NodeHasOddValue(IZ_ListNode** node, u64 _index, IZ_List* list) {
return *((u64*) (*node)->value) % 2 == 1;
}

spec("data") {
describe("list") {
describe("Initialize") {
@@ -44,9 +52,9 @@ spec("data") {
static u64 value3 = 69069u;

IZ_ListInitialize(&list);
IZ_ListAppendNode(&list, &value1);
IZ_ListAppendNode(&list, &value2);
IZ_ListAppendNode(&list, &value3);
IZ_ListAppendNode(&list, &value1, NULL);
IZ_ListAppendNode(&list, &value2, NULL);
IZ_ListAppendNode(&list, &value3, NULL);

mock_mode(IZ_free, IZ_FREE_CALLS_TRACKED);
}
@@ -106,10 +114,12 @@ spec("data") {

it("appends new node to empty list and sets it as root") {
static u64 value = 69420u;
IZ_ListAppendNode(&list, &value);
static IZ_ListNode* check_inserted_node;
IZ_ListAppendNode(&list, &value, &check_inserted_node);
u64 added_value = *((u64*) list.root->value);

check(added_value == 69420u, "Node not properly appended. Value: %u", added_value);
check(*((u64*) check_inserted_node->value) == added_value, "Node value not properly set. Value: %u", added_value);
check(
mock_is_called(IZ_malloc),
"Allocator function not called."
@@ -119,8 +129,9 @@ spec("data") {

it("appends new node to non-empty list") {
static u64 value1 = 42069u;

IZ_ListAppendNode(&list2, &value1);
static IZ_ListNode* check_inserted_node;
IZ_ListAppendNode(&list2, &value1, &check_inserted_node);
check(*((u64*) check_inserted_node->value) == value1, "Node value not properly set. Value: %u", value1);

check(*((u64*) list2.root->next->value) == 42069u, "Node not properly appended.");
check(
@@ -138,8 +149,8 @@ spec("data") {

before_each() {
IZ_ListInitialize(&list);
IZ_ListAppendNode(&list, &value1);
IZ_ListAppendNode(&list, &value2);
IZ_ListAppendNode(&list, &value1, NULL);
IZ_ListAppendNode(&list, &value2, NULL);
}

after_each() {
@@ -148,14 +159,16 @@ spec("data") {
}

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;
list.iterator = &list.root;
IZ_ListFindFirstNode(&list, NodeExists, &node);
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;
list.iterator = &list.root;
IZ_ListFindFirstNode(&list, NodeDoesNotExist, &node);
check(node == NULL, "Non-existing node found.");
}
}
@@ -169,9 +182,9 @@ spec("data") {
static u64 value3 = 69069u;

IZ_ListInitialize(&list);
IZ_ListAppendNode(&list, &value1);
IZ_ListAppendNode(&list, &value2);
IZ_ListAppendNode(&list, &value3);
IZ_ListAppendNode(&list, &value1, NULL);
IZ_ListAppendNode(&list, &value2, NULL);
IZ_ListAppendNode(&list, &value3, NULL);

mock_mode(IZ_free, IZ_FREE_CALLS_TRACKED);
}
@@ -181,26 +194,181 @@ spec("data") {
}

it("removes first node satisfying the filter condition") {
IZ_ListNode** existing_node = IZ_ListFindFirstNode(&list, NodeExists);
IZ_ListDeleteNode(*existing_node);
list.iterator = &list.root;
static IZ_ListNode* existing_node;
static IZ_ListNode* check_node;
IZ_ListFindFirstNode(&list, NodeExists, &existing_node);
IZ_ListDeleteNode(existing_node);

check(
mock_is_called(IZ_free),
"Deallocator function not called."
);

check(
IZ_ListFindFirstNode(&list, NodeExists2),
"Node supposed to be present in list is absent."
);
check_node = NULL;
list.iterator = &list.root;
IZ_ListFindFirstNode(&list, NodeExists2, &check_node);
check(check_node, "Node supposed to be present in list is absent.");

check(
!IZ_ListFindFirstNode(&list, NodeExists),
"Deleted node still present in list."
);
list.iterator = &list.root;
check_node = NULL;
IZ_ListFindFirstNode(&list, NodeExists, &check_node);
check(!check_node, "Deleted node still present in list.");

check(list.length == 2, "Length mismatch.");
}
}

describe("FindAllNodes") {
static IZ_List list;
static IZ_List out;

before_each() {
static u64 value1 = 69420u;
static u64 value2 = 42069u;
static u64 value3 = 69069u;

IZ_ListInitialize(&list);
IZ_ListAppendNode(&list, &value1, NULL);
IZ_ListAppendNode(&list, &value2, NULL);
IZ_ListAppendNode(&list, &value3, NULL);

mock_mode(IZ_free, IZ_FREE_CALLS_UNTRACKED);
}

before_each() {
IZ_ListInitialize(&out);
}

after_each() {
IZ_ListTeardown(&out);
}

after_each() {
IZ_ListTeardown(&list);
}

it("finds all nodes satisfying the filter condition") {
list.iterator = &list.root;
IZ_ListFindAllNodes(&list, NodeHasOddValue, &out);

check(out.length == 2, "Length mismatch. Length: %u", out.length);

out.iterator = &out.root;
static IZ_ListNode* check_node;
check_node = NULL;
IZ_ListFindFirstNode(&out, NodeExists, &check_node);
check(check_node, "Node supposed to be present in list is absent.");

out.iterator = &out.root;
check_node = NULL;
IZ_ListFindFirstNode(&out, NodeExists3, &check_node);
check(check_node, "Node supposed to be present in list is absent.");

out.iterator = &out.root;
check_node = NULL;
IZ_ListFindFirstNode(&out, NodeExists2, &check_node);
check(!check_node, "Node not supposed to be present in list is present.");
}
}

describe("InsertNodeAtIndex") {
static IZ_List list;

before_each() {
static u64 value1 = 69420u;
static u64 value2 = 42069u;
static u64 value3 = 69069u;

IZ_ListInitialize(&list);
IZ_ListAppendNode(&list, &value1, NULL);
IZ_ListAppendNode(&list, &value2, NULL);
IZ_ListAppendNode(&list, &value3, NULL);

mock_mode(IZ_free, IZ_FREE_CALLS_UNTRACKED);
}

after_each() {
IZ_ListTeardown(&list);
}

it("inserts node at the beginning of the list") {
static u64 value = 1337u;
static IZ_ListNode* check_node;
IZ_ListInsertNodeAtIndex(&list, &value, 0, &check_node);
check(*((u64*) check_node->value) == value, "Incorrect value of inserted node.");
static IZ_ListNode* check_other_node;

list.iterator = &list.root;
check_other_node = NULL;
IZ_ListFindFirstNode(&list, NodeExists, &check_other_node);
check(check_other_node, "Node supposed to be present in list is absent.");

list.iterator = &list.root;
check_other_node = NULL;
IZ_ListFindFirstNode(&list, NodeExists2, &check_other_node);
check(check_other_node, "Node supposed to be present in list is absent.");

list.iterator = &list.root;
check_other_node = NULL;
IZ_ListFindFirstNode(&list, NodeExists3, &check_other_node);
check(check_other_node, "Node supposed to be present in list is absent.");

check(list.length == 4, "Length mismatch.");
//IZ_ListPrintNodeValues(&list);
}

it("inserts node at the end of the list") {
static u64 value = 1337u;
static IZ_ListNode* check_node;
IZ_ListInsertNodeAtIndex(&list, &value, 3, &check_node);
check(*((u64*) check_node->value) == value, "Incorrect value of inserted node.");

static IZ_ListNode* check_other_node;
list.iterator = &list.root;
check_other_node = NULL;
IZ_ListFindFirstNode(&list, NodeExists, &check_other_node);
check(check_other_node, "Node supposed to be present in list is absent.");

list.iterator = &list.root;
check_other_node = NULL;
IZ_ListFindFirstNode(&list, NodeExists2, &check_other_node);
check(check_other_node, "Node supposed to be present in list is absent.");

list.iterator = &list.root;
check_other_node = NULL;
IZ_ListFindFirstNode(&list, NodeExists3, &check_other_node);
check(check_other_node, "Node supposed to be present in list is absent.");

check(list.length == 4, "Length mismatch.");
//IZ_ListPrintNodeValues(&list);
}

it("inserts node in the middle of the list") {
static u64 value = 1337u;
static IZ_ListNode* check_node;
IZ_ListInsertNodeAtIndex(&list, &value, 1, &check_node);
check(*((u64*) check_node->value) == value, "Incorrect value of inserted node.");

static IZ_ListNode* check_other_node;
list.iterator = &list.root;
check_other_node = NULL;
IZ_ListFindFirstNode(&list, NodeExists, &check_other_node);
check(check_other_node, "Node supposed to be present in list is absent.");

list.iterator = &list.root;
check_other_node = NULL;
IZ_ListFindFirstNode(&list, NodeExists2, &check_other_node);
check(check_other_node, "Node supposed to be present in list is absent.");

list.iterator = &list.root;
check_other_node = NULL;
IZ_ListFindFirstNode(&list, NodeExists3, &check_other_node);
check(check_other_node, "Node supposed to be present in list is absent.");

check(list.length == 4, "Length mismatch.");
//IZ_ListPrintNodeValues(&list);
}
}
}
}

+ 7
- 5
src/packages/game/memory/IZ_pool.c Переглянути файл

@@ -27,15 +27,16 @@ 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,
});
}, &new_item);

pool->next_address = (pool->next_address + args.size) % POOL_MAX_SIZE;
pool->allocated_memory += args.size;
return (*new_item)->value;
return new_item->value;
}

bool IZ_PoolGetSameItem(IZ_ListNode** node, u64 _index, IZ_List* list) {
@@ -43,9 +44,10 @@ bool IZ_PoolGetSameItem(IZ_ListNode** node, u64 _index, IZ_List* list) {
}

void IZ_PoolDeallocate(IZ_PoolItem* item) {
IZ_ListNode** node = IZ_ListFindFirstNode(&item->pool->items, IZ_PoolGetSameItem);
IZ_ListNode* node;
IZ_ListFindFirstNode(&item->pool->items, IZ_PoolGetSameItem, &node);
if (node) {
IZ_ListDeleteNode(*node);
IZ_ListDeleteNode(node);
}
}



+ 2
- 2
src/packages/game/memory/memory.test.c Переглянути файл

@@ -151,7 +151,7 @@ spec("memory") {
"Free memory not properly utilized."
);

void* p4 = IZ_PoolAllocate(&pool, (IZ_PoolAllocationArgs){
void* p4 = IZ_PoolAllocate(&pool, (IZ_PoolAllocationArgs) {
.size = sizeof(u8),
.priority = 0,
.timestamp = 5,
@@ -159,7 +159,7 @@ spec("memory") {

check(
p4 == p3 + sizeof(u8),
"Free memory not properly utilized."
"Free memory not properly allocated."
);
}
}


Завантаження…
Відмінити
Зберегти