From 444c34c2d6ea64999241b4cd3eada8bcdbf1b8ed Mon Sep 17 00:00:00 2001 From: TheoryOfNekomata Date: Sun, 8 Jan 2023 22:01:49 +0800 Subject: [PATCH] Implement list functions Add the list functions that will be used for memory pools. --- src/packages/game/data/IZ_list.c | 94 +++++++++++++++++-- src/packages/game/data/IZ_list.h | 28 ++++++ src/packages/game/data/data.test.c | 125 ++++++++++++++++++++++--- src/packages/game/memory/IZ_pool.c | 13 +++ src/packages/game/memory/memory.test.c | 2 + 5 files changed, 242 insertions(+), 20 deletions(-) diff --git a/src/packages/game/data/IZ_list.c b/src/packages/game/data/IZ_list.c index e22247a..5bf59cd 100644 --- a/src/packages/game/data/IZ_list.c +++ b/src/packages/game/data/IZ_list.c @@ -29,25 +29,68 @@ 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 = SDL_malloc(sizeof(IZ_ListNode)); new_node->value = node_value; new_node->next = NULL; - list->length += 1; if (!(list->root)) { list->root = new_node; + list->length += 1; return &list->root; } - IZ_ListNode *last_node = list->root; + IZ_ListNode* last_node = list->root; while (last_node->next) { last_node = last_node->next; } last_node->next = new_node; + list->length += 1; + return &last_node->next; +} + +/** + * Inserts a node at the specified index in the list. + * @param list - The list to append to. + * @param node_value - The value of the node to insert. + * @param index - The index to insert the node at. + * @return Pointer to the newly created node. + */ +IZ_ListNode** IZ_ListInsertNodeAtIndex(IZ_List* list, void* node_value, u64 index) { + if (index > list->length) { + // we don't want to go out of bounds + return NULL; + } + + IZ_ListNode* new_node = SDL_malloc(sizeof(IZ_ListNode)); + new_node->value = node_value; + new_node->next = NULL; + + if (!(list->root)) { + list->root = new_node; + list->length += 1; + return &list->root; + } + + if (index == 0) { + new_node->next = list->root; + list->root = new_node; + list->length += 1; + return &list->root; + } + + IZ_ListNode* last_node = list->root; + for (u64 i = 0; i < index - 1; i += 1) { + last_node = last_node->next; + } + + new_node->next = last_node->next; + last_node->next = new_node; + list->length += 1; return &last_node->next; } @@ -107,13 +150,50 @@ IZ_ListNode** IZ_ListFindFirstNode(IZ_List* list, IZ_ListFindPredicate filter) { list->iterator = &list->root; u64 index = 0; do { - if (!filter(list->iterator, index, list)) { - list->iterator = &((*list->iterator)->next); - index += 1; - continue; + if (filter(list->iterator, index, list)) { + return list->iterator; } - return list->iterator; + list->iterator = &((*list->iterator)->next); + index += 1; } while (*list->iterator); return NULL; } + +void IZ_ListFilter(IZ_List* original_list, IZ_ListFindPredicate filter, IZ_List* out_filtered_list) { + // we assume the new list is already initialized + IZ_ListNode* node = original_list->root; + u64 index; + for (index = 0; node; index += 1) { + if (!filter(&node, index, original_list)) { + node = node->next; + continue; + } + IZ_ListAppendNode(out_filtered_list, node->value); + node = node->next; + } +} + +void IZ_ListSort(IZ_List* original_list, IZ_ListSortComparatorPredicate sort, IZ_List* out_sorted_list) { + // we assume the new list is already initialized + IZ_ListNode* original_node = original_list->root; + u64 original_index; + + for (original_index = 0; original_node; original_index += 1) { + if (!out_sorted_list->root) { + IZ_ListAppendNode(out_sorted_list, original_node->value); + original_node = original_node->next; + continue; + } + + IZ_ListNode* sorted_node = out_sorted_list->root; + for (u64 sorted_index = 0; sorted_node; sorted_index += 1) { + if (sort(&original_node, &sorted_node, original_index, original_list) < 0) { + IZ_ListInsertNodeAtIndex(out_sorted_list, original_node->value, sorted_index); + original_node = original_node->next; + break; + } + sorted_node = sorted_node->next; + } + } +} diff --git a/src/packages/game/data/IZ_list.h b/src/packages/game/data/IZ_list.h index 8bdf714..bb488f4 100644 --- a/src/packages/game/data/IZ_list.h +++ b/src/packages/game/data/IZ_list.h @@ -34,10 +34,22 @@ typedef struct { * The iterator for traversing the list. */ IZ_ListNode** iterator; + /** + * The custom data for use in matching for the find predicate. + */ + void* find_predicate_userdata; } IZ_List; +/** + * Predicate for finding a node in a list. + */ typedef bool IZ_ListFindPredicate(IZ_ListNode**, u64, IZ_List*); +/** + * Comparator callback for sorting a list. Returns a positive value if a > b, a negative value if a < b, and 0 if a == b. + */ +typedef i64 IZ_ListSortComparatorPredicate(IZ_ListNode**, IZ_ListNode**, u64, IZ_List*); + /** * Initializes a list. */ @@ -54,6 +66,12 @@ void IZ_ListTeardown(IZ_List*); */ IZ_ListNode** IZ_ListAppendNode(IZ_List*, void*); +/** + * Inserts a node at the specified index in the list. + * @return Pointer to the newly created node. + */ +IZ_ListNode** IZ_ListInsertNodeAtIndex(IZ_List*, void*, u64); + /** * Deletes the first node in the list that matches the filter. */ @@ -65,4 +83,14 @@ void IZ_ListDeleteFirstNode(IZ_List*, IZ_ListFindPredicate); */ IZ_ListNode** IZ_ListFindFirstNode(IZ_List*, IZ_ListFindPredicate); +/** + * Creates a new list that contains the nodes that match the filter. + */ +void IZ_ListFilter(IZ_List*, IZ_ListFindPredicate, IZ_List* out1); + +/** + * Creates a new list that contains the sorted nodes from the original list. + */ +void IZ_ListSort(IZ_List*, IZ_ListSortComparatorPredicate, IZ_List* out1); + #endif diff --git a/src/packages/game/data/data.test.c b/src/packages/game/data/data.test.c index b774a75..630181a 100644 --- a/src/packages/game/data/data.test.c +++ b/src/packages/game/data/data.test.c @@ -117,31 +117,60 @@ spec("data") { } } - describe("FindFirstNode") { + describe("InsertNodeAtIndex") { static IZ_List list; - static u64 value1 = 69420u; - static u64 value2 = 42069u; + static IZ_List list2; before_each() { IZ_ListInitialize(&list); - IZ_ListAppendNode(&list, &value1); - IZ_ListAppendNode(&list, &value2); + } + + before_each() { + IZ_ListInitialize(&list2); + static u64 existing_value = 69420u; + static IZ_ListNode existing_node = { + .value = &existing_value, + .next = NULL, + }; + list2.root = &existing_node; + list2.length = 1; + } + + after_each() { + mock_reset(SDL_malloc); } 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."); + after_each() { + IZ_ListTeardown(&list2); } - it("returns NULL when all nodes do not satisfy the filter condition") { - static IZ_ListNode** node; - node = IZ_ListFindFirstNode(&list, NodeDoesNotExist); - check(node == NULL, "Non-existing node found."); + it("inserts new node to empty list and sets it as root") { + static u64 value = 69420u; + IZ_ListInsertNodeAtIndex(&list, &value, 0); + + check(*((u64*) list.root->value) == 69420u, "Node not properly appended."); + check( + mock_is_called(SDL_malloc), + "Allocator function not called." + ); + check(list.length == 1, "Length mismatch."); + } + + it("inserts new node to non-empty list") { + static u64 value1 = 42069u; + + IZ_ListInsertNodeAtIndex(&list2, &value1, 0); + + check(*((u64*) list2.root->value) == 42069u, "Node not properly appended."); + check( + mock_is_called(SDL_malloc), + "Allocator function not called." + ); + check(list2.length == 2, "Length mismatch."); } } @@ -184,5 +213,75 @@ spec("data") { check(list.length == 2, "Length mismatch."); } } + + describe("FindFirstNode") { + static IZ_List list; + static u64 value1 = 69420u; + static u64 value2 = 42069u; + + before_each() { + IZ_ListInitialize(&list); + IZ_ListAppendNode(&list, &value1); + 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."); + } + + it("returns NULL when all nodes do not satisfy the filter condition") { + static IZ_ListNode** node; + node = IZ_ListFindFirstNode(&list, NodeDoesNotExist); + check(node == NULL, "Non-existing node found."); + } + } + + describe("Filter") { + static IZ_List original_list; + static IZ_List filtered_list; + + before_each() { + IZ_ListInitialize(&original_list); + IZ_ListInitialize(&filtered_list); + static u64 value1 = 69420u; + static u64 value2 = 42069u; + static u64 value3 = 69069u; + IZ_ListAppendNode(&original_list, &value1); + IZ_ListAppendNode(&original_list, &value2); + IZ_ListAppendNode(&original_list, &value3); + } + + it("ensures nodes that satisfy find predicate are present") { + IZ_ListFilter(&original_list, NodeExists, &filtered_list); + + check( + IZ_ListFindFirstNode(&filtered_list, NodeExists), + "Node supposed to be present in list is absent." + ); + + check(filtered_list.length == 1, "Length mismatch."); + } + + it("ensures nodes that do not satisfy find predicate are absent") { + IZ_ListFilter(&original_list, NodeDoesNotExist, &filtered_list); + + check( + IZ_ListFindFirstNode(&filtered_list, NodeDoesNotExist), + "Node supposed to be absent in list is present." + ); + + check(filtered_list.length == 0, "Length mismatch."); + } + } + + describe("Sort") { + + } } } diff --git a/src/packages/game/memory/IZ_pool.c b/src/packages/game/memory/IZ_pool.c index b842602..0c6a835 100644 --- a/src/packages/game/memory/IZ_pool.c +++ b/src/packages/game/memory/IZ_pool.c @@ -9,6 +9,12 @@ void IZ_PoolInitialize(IZ_Pool* pool, size_t size) { pool->max_size = size; } +bool IZ_PoolFindItemsWithLowerPrecedence(IZ_ListNode** node, u64 index, IZ_List* list) { + IZ_PoolItem* item = (IZ_PoolItem*)(*node)->value; + IZ_PoolAllocationArgs* args = (IZ_PoolAllocationArgs*) list->find_predicate_userdata; + return item->args.priority < args->priority; +} + IZ_PoolItem* IZ_PoolAllocate(IZ_Pool* pool, IZ_PoolAllocationArgs args) { // 1. check next free allocation for size // 2. if 1. returns non-null, @@ -21,7 +27,14 @@ IZ_PoolItem* IZ_PoolAllocate(IZ_Pool* pool, IZ_PoolAllocationArgs args) { // } // } + if (pool->max_size < args.size) { + // couldn't allocate anything bigger than the pool + return NULL; + } + if (pool->max_size - pool->allocated_memory < args.size) { + pool->items.find_predicate_userdata = &args; + IZ_ListDeleteFirstNode(&pool->items, IZ_PoolFindItemsWithLowerPrecedence); // TODO deallocate memory based from priority } diff --git a/src/packages/game/memory/memory.test.c b/src/packages/game/memory/memory.test.c index c2f191a..38bfa37 100644 --- a/src/packages/game/memory/memory.test.c +++ b/src/packages/game/memory/memory.test.c @@ -144,6 +144,8 @@ spec("memory") { // FIXME here onwards + printf("\np1: %p, p2: %p, p3: %p\n", p1, p2, p3); + check( p3 == p2 + sizeof(u8), "Free memory not properly utilized."