260 lines
6.9 KiB
C
260 lines
6.9 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>
|
|
|
|
wp_intern WpDblList nodeToList(WpDblNode *node, u64 item_size);
|
|
wp_intern inline void dblListValidate(const WpDblList *list, u64 item_size);
|
|
wp_intern inline void dblListNodeValidate(const WpDblList *list, const WpDblNode *node, u64 item_size);
|
|
|
|
WpDblList *dblListAlloc(const WpAllocator *allocator, u64 item_size) {
|
|
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
|
|
|
|
WpDblList *list = wpMemAllocatorAlloc(allocator, sizeof(WpDblList));
|
|
if (!list) { goto DBL_LIST_ALLOC_RETURN; }
|
|
|
|
memset((void *)list, 0, sizeof(WpDblList));
|
|
list->magic = WP_DBL_LIST_MAGIC;
|
|
list->item_size = item_size;
|
|
|
|
DBL_LIST_ALLOC_RETURN:
|
|
return list;
|
|
}
|
|
|
|
WpDblNode *dblListNodeAlloc(const WpAllocator *allocator, void *item, u64 item_size) {
|
|
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
|
|
|
|
WpDblNode *node = wpMemAllocatorAlloc(allocator, sizeof(WpDblNode));
|
|
if (!node) { goto DBL_LIST_NODE_ALLOC_RETURN; }
|
|
|
|
memset((void *)node, 0, sizeof(WpDblNode));
|
|
node->item = item;
|
|
node->header.magic = WP_DBL_NODE_MAGIC;
|
|
node->header.item_size = item_size;
|
|
|
|
DBL_LIST_NODE_ALLOC_RETURN:
|
|
return node;
|
|
}
|
|
|
|
WpDblNode *dblListGet(const WpDblList *list, u64 index, u64 item_size) {
|
|
wpDebugAssert(list != NULL, "`list` should not be NULL");
|
|
dblListValidate(list, item_size);
|
|
wpRuntimeAssert(index < list->node_count, "`index` is out of bounds");
|
|
|
|
WpDblNode *output = NULL;
|
|
WpDblNode *current = list->first;
|
|
for (u64 i = 1; i <= index; ++i) {
|
|
current = current->header.next;
|
|
}
|
|
|
|
output = current;
|
|
|
|
return output;
|
|
}
|
|
|
|
void dblListPushFront(WpDblList *list, WpDblNode *node, u64 item_size) {
|
|
wpDebugAssert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");
|
|
dblListValidate(list, item_size);
|
|
dblListNodeValidate(list, node, item_size);
|
|
|
|
WpDblList node_list = nodeToList(node, item_size);
|
|
|
|
if (list->node_count == 0) {
|
|
*list = node_list;
|
|
return;
|
|
}
|
|
|
|
list->node_count += node_list.node_count;
|
|
|
|
WpDblNode *first = list->first;
|
|
if (first) {
|
|
first->header.prev = node_list.last;
|
|
}
|
|
|
|
list->first = node_list.first;
|
|
node_list.last->header.next = first;
|
|
}
|
|
|
|
void dblListPushBack(WpDblList *list, WpDblNode *node, u64 item_size) {
|
|
wpDebugAssert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");
|
|
dblListValidate(list, item_size);
|
|
dblListNodeValidate(list, node, item_size);
|
|
|
|
WpDblList node_list = nodeToList(node, item_size);
|
|
|
|
if (list->node_count == 0) {
|
|
*list = node_list;
|
|
return;
|
|
}
|
|
|
|
list->node_count += node_list.node_count;
|
|
|
|
WpDblNode *last = list->last;
|
|
if (last) {
|
|
last->header.next = node_list.first;
|
|
}
|
|
|
|
list->last = node_list.last;
|
|
node_list.first->header.prev = last;
|
|
}
|
|
|
|
void dblListInsert(WpDblList *list, WpDblNode *node, u64 index, u64 item_size) {
|
|
wpDebugAssert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");
|
|
dblListValidate(list, item_size);
|
|
dblListNodeValidate(list, node, item_size);
|
|
|
|
if (index == 0) {
|
|
dblListPushFront(list, node, item_size);
|
|
return;
|
|
} else if (index == list->node_count) {
|
|
dblListPushBack(list, node, item_size);
|
|
return;
|
|
}
|
|
|
|
WpDblNode *dst_node = dblListGet(list, index, item_size);
|
|
if (!dst_node) {
|
|
return;
|
|
}
|
|
|
|
WpDblList node_list = nodeToList(node, item_size);
|
|
|
|
list->node_count += node_list.node_count;
|
|
|
|
WpDblNode *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;
|
|
}
|
|
|
|
WpDblNode *dblListPopFront(WpDblList *list, u64 item_size) {
|
|
wpDebugAssert(list != NULL, "`list` should not be NULL");
|
|
dblListValidate(list, item_size);
|
|
|
|
WpDblNode *output = NULL;
|
|
|
|
if (list->node_count == 0) {
|
|
goto RETURN_LIST_POP_FRONT;
|
|
}
|
|
|
|
output = list->first;
|
|
|
|
if (list->node_count == 1) {
|
|
*list = (WpDblList){.magic = WP_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;
|
|
}
|
|
|
|
WpDblNode *dblListPopBack(WpDblList *list, u64 item_size) {
|
|
wpDebugAssert(list != NULL, "`list` should not be NULL");
|
|
dblListValidate(list, item_size);
|
|
|
|
WpDblNode *output = NULL;
|
|
|
|
if (list->node_count == 0) {
|
|
goto RETURN_LIST_POP_BACK;
|
|
}
|
|
|
|
output = list->last;
|
|
|
|
if (list->node_count == 1) {
|
|
*list = (WpDblList){.magic = WP_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;
|
|
}
|
|
|
|
WpDblNode *dblListRemove(WpDblList *list, u64 index, u64 item_size) {
|
|
wpDebugAssert(list != NULL, "`list` should not be NULL");
|
|
dblListValidate(list, item_size);
|
|
|
|
WpDblNode *output = NULL;
|
|
|
|
if (index == 0) {
|
|
output = dblListPopFront(list, item_size);
|
|
goto RETURN_LIST_REMOVE;
|
|
} else if (index == list->node_count) {
|
|
output = dblListPopBack(list, item_size);
|
|
goto RETURN_LIST_REMOVE;
|
|
}
|
|
|
|
output = dblListGet(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 dblListEmpty(WpDblList *list, u64 item_size) {
|
|
wpDebugAssert(list != NULL, "`list` should not be NULL");
|
|
dblListValidate(list, item_size);
|
|
|
|
u64 count = list->node_count;
|
|
for (u64 i = 0; i < count; ++i) {
|
|
dblListPopBack(list, item_size);
|
|
}
|
|
}
|
|
|
|
wp_intern WpDblList nodeToList(WpDblNode *node, u64 item_size) {
|
|
WpDblList output = {
|
|
.magic = WP_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;
|
|
}
|
|
|
|
wp_intern inline void dblListValidate(const WpDblList *list, u64 item_size) {
|
|
wpRuntimeAssert(list->magic == WP_DBL_LIST_MAGIC, "`list` isn't a valid wp list type");
|
|
wpRuntimeAssert(list->item_size == item_size, "Invalid item provided");
|
|
}
|
|
|
|
wp_intern inline void dblListNodeValidate(const WpDblList *list, const WpDblNode *node, u64 item_size) {
|
|
wpRuntimeAssert(node->header.magic == WP_DBL_NODE_MAGIC, "`node` isn't a valid wp node type");
|
|
wpRuntimeAssert(list->item_size == node->header.item_size, "Mismatched `list` and `node` types");
|
|
wpRuntimeAssert(node->header.item_size == item_size, "Invalid item provided");
|
|
}
|