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