Files
wizapp-stdlib/src/base/dbl_list/dbl_list.c
T
2026-09-06 18:15:23 +01:00

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");
}