260 lines
7.2 KiB
C
260 lines
7.2 KiB
C
// vim:fileencoding=utf-8:foldmethod=marker
|
|
|
|
#include "./dbl_list.h"
|
|
#include "../mem/allocator/mem_allocator.h"
|
|
#include "../../common/assert/assert.h"
|
|
#include "../../common/aliases/aliases.h"
|
|
#include "../../common/platform/platform.h"
|
|
#include <stddef.h>
|
|
|
|
wapp_intern GenericList _node_to_list(GenericNode *node, u64 item_size);
|
|
wapp_intern inline void _dbl_list_validate(const GenericList *list, u64 item_size);
|
|
wapp_intern inline void _dbl_list_node_validate(const GenericList *list, const GenericNode *node, u64 item_size);
|
|
|
|
GenericList *_dbl_list_alloc(const Allocator *allocator, u64 item_size) {
|
|
wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL");
|
|
|
|
GenericList *list = wapp_mem_allocator_alloc(allocator, sizeof(GenericList));
|
|
if (!list) { goto DBL_LIST_ALLOC_RETURN; }
|
|
|
|
memset((void *)list, 0, sizeof(GenericList));
|
|
list->magic = WAPP_DBL_LIST_MAGIC;
|
|
list->item_size = item_size;
|
|
|
|
DBL_LIST_ALLOC_RETURN:
|
|
return list;
|
|
}
|
|
|
|
GenericNode *_dbl_list_node_alloc(const Allocator *allocator, void *item, u64 item_size) {
|
|
wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL");
|
|
|
|
GenericNode *node = wapp_mem_allocator_alloc(allocator, sizeof(GenericNode));
|
|
if (!node) { goto DBL_LIST_NODE_ALLOC_RETURN; }
|
|
|
|
memset((void *)node, 0, sizeof(GenericNode));
|
|
node->item = item;
|
|
node->header.magic = WAPP_DBL_NODE_MAGIC;
|
|
node->header.item_size = item_size;
|
|
|
|
DBL_LIST_NODE_ALLOC_RETURN:
|
|
return node;
|
|
}
|
|
|
|
GenericNode *_dbl_list_get(const GenericList *list, u64 index, u64 item_size) {
|
|
wapp_debug_assert(list != NULL, "`list` should not be NULL");
|
|
_dbl_list_validate(list, item_size);
|
|
wapp_runtime_assert(index < list->node_count, "`index` is out of bounds");
|
|
|
|
GenericNode *output = NULL;
|
|
GenericNode *current = list->first;
|
|
for (u64 i = 1; i <= index; ++i) {
|
|
current = current->header.next;
|
|
}
|
|
|
|
output = current;
|
|
|
|
return output;
|
|
}
|
|
|
|
void _dbl_list_push_front(GenericList *list, GenericNode *node, u64 item_size) {
|
|
wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");
|
|
_dbl_list_validate(list, item_size);
|
|
_dbl_list_node_validate(list, node, item_size);
|
|
|
|
GenericList node_list = _node_to_list(node, item_size);
|
|
|
|
if (list->node_count == 0) {
|
|
*list = node_list;
|
|
return;
|
|
}
|
|
|
|
list->node_count += node_list.node_count;
|
|
|
|
GenericNode *first = list->first;
|
|
if (first) {
|
|
first->header.prev = node_list.last;
|
|
}
|
|
|
|
list->first = node_list.first;
|
|
node_list.last->header.next = first;
|
|
}
|
|
|
|
void _dbl_list_push_back(GenericList *list, GenericNode *node, u64 item_size) {
|
|
wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");
|
|
_dbl_list_validate(list, item_size);
|
|
_dbl_list_node_validate(list, node, item_size);
|
|
|
|
GenericList node_list = _node_to_list(node, item_size);
|
|
|
|
if (list->node_count == 0) {
|
|
*list = node_list;
|
|
return;
|
|
}
|
|
|
|
list->node_count += node_list.node_count;
|
|
|
|
GenericNode *last = list->last;
|
|
if (last) {
|
|
last->header.next = node_list.first;
|
|
}
|
|
|
|
list->last = node_list.last;
|
|
node_list.first->header.prev = last;
|
|
}
|
|
|
|
void _dbl_list_insert(GenericList *list, GenericNode *node, u64 index, u64 item_size) {
|
|
wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");
|
|
_dbl_list_validate(list, item_size);
|
|
_dbl_list_node_validate(list, node, item_size);
|
|
|
|
if (index == 0) {
|
|
_dbl_list_push_front(list, node, item_size);
|
|
return;
|
|
} else if (index == list->node_count) {
|
|
_dbl_list_push_back(list, node, item_size);
|
|
return;
|
|
}
|
|
|
|
GenericNode *dst_node = _dbl_list_get(list, index, item_size);
|
|
if (!dst_node) {
|
|
return;
|
|
}
|
|
|
|
GenericList node_list = _node_to_list(node, item_size);
|
|
|
|
list->node_count += node_list.node_count;
|
|
|
|
GenericNode *prev = dst_node->header.prev;
|
|
|
|
dst_node->header.prev = node_list.last;
|
|
prev->header.next = node_list.first;
|
|
|
|
node_list.first->header.prev = prev;
|
|
node_list.last->header.next = dst_node;
|
|
}
|
|
|
|
GenericNode *_dbl_list_pop_front(GenericList *list, u64 item_size) {
|
|
wapp_debug_assert(list != NULL, "`list` should not be NULL");
|
|
_dbl_list_validate(list, item_size);
|
|
|
|
GenericNode *output = NULL;
|
|
|
|
if (list->node_count == 0) {
|
|
goto RETURN_LIST_POP_FRONT;
|
|
}
|
|
|
|
output = list->first;
|
|
|
|
if (list->node_count == 1) {
|
|
*list = (GenericList){.magic = WAPP_DBL_LIST_MAGIC, .item_size = item_size};
|
|
goto RETURN_LIST_POP_FRONT;
|
|
}
|
|
|
|
--(list->node_count);
|
|
list->first = output->header.next;
|
|
|
|
output->header.prev = output->header.next = NULL;
|
|
|
|
RETURN_LIST_POP_FRONT:
|
|
return output;
|
|
}
|
|
|
|
GenericNode *_dbl_list_pop_back(GenericList *list, u64 item_size) {
|
|
wapp_debug_assert(list != NULL, "`list` should not be NULL");
|
|
_dbl_list_validate(list, item_size);
|
|
|
|
GenericNode *output = NULL;
|
|
|
|
if (list->node_count == 0) {
|
|
goto RETURN_LIST_POP_BACK;
|
|
}
|
|
|
|
output = list->last;
|
|
|
|
if (list->node_count == 1) {
|
|
*list = (GenericList){.magic = WAPP_DBL_LIST_MAGIC, .item_size = item_size};
|
|
goto RETURN_LIST_POP_BACK;
|
|
}
|
|
|
|
--(list->node_count);
|
|
list->last = output->header.prev;
|
|
|
|
output->header.prev = output->header.next = NULL;
|
|
|
|
RETURN_LIST_POP_BACK:
|
|
return output;
|
|
}
|
|
|
|
GenericNode *_dbl_list_remove(GenericList *list, u64 index, u64 item_size) {
|
|
wapp_debug_assert(list != NULL, "`list` should not be NULL");
|
|
_dbl_list_validate(list, item_size);
|
|
|
|
GenericNode *output = NULL;
|
|
|
|
if (index == 0) {
|
|
output = _dbl_list_pop_front(list, item_size);
|
|
goto RETURN_LIST_REMOVE;
|
|
} else if (index == list->node_count) {
|
|
output = _dbl_list_pop_back(list, item_size);
|
|
goto RETURN_LIST_REMOVE;
|
|
}
|
|
|
|
output = _dbl_list_get(list, index, item_size);
|
|
if (!output) {
|
|
goto RETURN_LIST_REMOVE;
|
|
}
|
|
|
|
output->header.prev->header.next = output->header.next;
|
|
output->header.next->header.prev = output->header.prev;
|
|
|
|
--(list->node_count);
|
|
|
|
output->header.prev = output->header.next = NULL;
|
|
|
|
RETURN_LIST_REMOVE:
|
|
return output;
|
|
}
|
|
|
|
void _dbl_list_empty(GenericList *list, u64 item_size) {
|
|
wapp_debug_assert(list != NULL, "`list` should not be NULL");
|
|
_dbl_list_validate(list, item_size);
|
|
|
|
u64 count = list->node_count;
|
|
for (u64 i = 0; i < count; ++i) {
|
|
_dbl_list_pop_back(list, item_size);
|
|
}
|
|
}
|
|
|
|
wapp_intern GenericList _node_to_list(GenericNode *node, u64 item_size) {
|
|
GenericList output = {
|
|
.magic = WAPP_DBL_LIST_MAGIC,
|
|
.first = node,
|
|
.last = node,
|
|
.node_count = 1,
|
|
.item_size = item_size,
|
|
};
|
|
|
|
while (output.first->header.prev != NULL) {
|
|
output.first = output.first->header.prev;
|
|
++(output.node_count);
|
|
}
|
|
|
|
while (output.last->header.next != NULL) {
|
|
output.last = output.last->header.next;
|
|
++(output.node_count);
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
wapp_intern inline void _dbl_list_validate(const GenericList *list, u64 item_size) {
|
|
wapp_runtime_assert(list->magic == WAPP_DBL_LIST_MAGIC, "`list` isn't a valid wapp list type");
|
|
wapp_runtime_assert(list->item_size == item_size, "Invalid item provided");
|
|
}
|
|
|
|
wapp_intern inline void _dbl_list_node_validate(const GenericList *list, const GenericNode *node, u64 item_size) {
|
|
wapp_runtime_assert(node->header.magic == WAPP_DBL_NODE_MAGIC, "`node` isn't a valid wapp node type");
|
|
wapp_runtime_assert(list->item_size == node->header.item_size, "Mismatched `list` and `node` types");
|
|
wapp_runtime_assert(node->header.item_size == item_size, "Invalid item provided");
|
|
}
|