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. | * Appends a node to the end of the list. | ||||
* @param list - The list to append to. | * @param list - The list to append to. | ||||
* @param node_value - The value of the node to append. | |||||
* @return Pointer to the newly created node. | * @return Pointer to the newly created node. | ||||
*/ | */ | ||||
IZ_ListNode** IZ_ListAppendNode(IZ_List* list, void* node_value) { | IZ_ListNode** IZ_ListAppendNode(IZ_List* list, void* node_value) { | ||||
IZ_ListNode* new_node = SDL_malloc(sizeof(IZ_ListNode)); | IZ_ListNode* new_node = SDL_malloc(sizeof(IZ_ListNode)); | ||||
new_node->value = node_value; | new_node->value = node_value; | ||||
new_node->next = NULL; | new_node->next = NULL; | ||||
list->length += 1; | |||||
if (!(list->root)) { | if (!(list->root)) { | ||||
list->root = new_node; | list->root = new_node; | ||||
list->length += 1; | |||||
return &list->root; | return &list->root; | ||||
} | } | ||||
IZ_ListNode *last_node = list->root; | |||||
IZ_ListNode* last_node = list->root; | |||||
while (last_node->next) { | while (last_node->next) { | ||||
last_node = last_node->next; | last_node = last_node->next; | ||||
} | } | ||||
last_node->next = new_node; | 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; | return &last_node->next; | ||||
} | } | ||||
@@ -107,13 +150,50 @@ IZ_ListNode** IZ_ListFindFirstNode(IZ_List* list, IZ_ListFindPredicate filter) { | |||||
list->iterator = &list->root; | list->iterator = &list->root; | ||||
u64 index = 0; | u64 index = 0; | ||||
do { | 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); | } while (*list->iterator); | ||||
return NULL; | 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. | * The iterator for traversing the list. | ||||
*/ | */ | ||||
IZ_ListNode** iterator; | IZ_ListNode** iterator; | ||||
/** | |||||
* The custom data for use in matching for the find predicate. | |||||
*/ | |||||
void* find_predicate_userdata; | |||||
} IZ_List; | } IZ_List; | ||||
/** | |||||
* Predicate for finding a node in a list. | |||||
*/ | |||||
typedef bool IZ_ListFindPredicate(IZ_ListNode**, u64, IZ_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. | * Initializes a list. | ||||
*/ | */ | ||||
@@ -54,6 +66,12 @@ void IZ_ListTeardown(IZ_List*); | |||||
*/ | */ | ||||
IZ_ListNode** IZ_ListAppendNode(IZ_List*, void*); | 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. | * 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); | 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 | #endif |
@@ -117,31 +117,60 @@ spec("data") { | |||||
} | } | ||||
} | } | ||||
describe("FindFirstNode") { | |||||
describe("InsertNodeAtIndex") { | |||||
static IZ_List list; | static IZ_List list; | ||||
static u64 value1 = 69420u; | |||||
static u64 value2 = 42069u; | |||||
static IZ_List list2; | |||||
before_each() { | before_each() { | ||||
IZ_ListInitialize(&list); | 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() { | after_each() { | ||||
IZ_ListTeardown(&list); | 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."); | 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; | 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) { | IZ_PoolItem* IZ_PoolAllocate(IZ_Pool* pool, IZ_PoolAllocationArgs args) { | ||||
// 1. check next free allocation for size | // 1. check next free allocation for size | ||||
// 2. if 1. returns non-null, | // 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) { | 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 | // TODO deallocate memory based from priority | ||||
} | } | ||||
@@ -144,6 +144,8 @@ spec("memory") { | |||||
// FIXME here onwards | // FIXME here onwards | ||||
printf("\np1: %p, p2: %p, p3: %p\n", p1, p2, p3); | |||||
check( | check( | ||||
p3 == p2 + sizeof(u8), | p3 == p2 + sizeof(u8), | ||||
"Free memory not properly utilized." | "Free memory not properly utilized." | ||||