Add the list functions that will be used for memory pools.feature/data-structs
@@ -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; | |||
} | |||
} | |||
} |
@@ -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 |
@@ -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") { | |||
} | |||
} | |||
} |
@@ -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 | |||
} | |||
@@ -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." | |||