Files
balatro-gba-chinese-jocker-…/source/list.c
T
Rickey 47b1bac31f Add list_swap() and list_insert() functions (#232)
* Add list_swap() and list_insert() functions

* Update docs for list_swap

* Add test case and fix list insert bug

* Fix silly bug with list length on insertion

* Initial changes for pr

* Add reverse iterator and update test for it

* Cleanup iterator next function

* update tests to cover list_get_at_idx
2025-11-18 18:19:00 -08:00

303 lines
6.3 KiB
C

#include <stdbool.h>
#include "list.h"
#include "pool.h"
/**
* Remove a node from a list.
*
* Remove a @ref ListNode from a @ref List. There are no checks to ensure that the
* passed `node` is actually part of the passed `list`. Handle with care.
* This is used with the @ref ListItr specifically.
*
* @param list pointer to a @ref List
* @param node pointer to a @ref ListNode
*/
static void _list_remove_node(List *list, ListNode *node);
/**
* Get the next @ref ListNode in a @ref ListItr
*
* Note: Use of this function outside of testing is strongly discouraged. Unless
* you really want to access the @ref ListNode itself, it's preferred to just use
* @ref list_itr_next .
*
* @param itr pointer to the @ref ListItr
*
* @return A pointer to the @ref ListNode in the itr, otherwise return NULL.
*/
static ListNode* _list_itr_node_next(ListItr* itr);
List list_create(void)
{
List list = { .head = NULL, .tail = NULL, .len = 0 };
return list;
}
void list_clear(List* list)
{
if(list_is_empty(list)) return;
ListItr itr = list_itr_create(list);
ListNode* ln;
while((ln = _list_itr_node_next(&itr)))
{
POOL_FREE(ListNode, ln);
}
list->head = NULL;
list->tail = NULL;
list->len = 0;
}
bool list_is_empty(const List* list)
{
return list->len == 0;
}
void list_push_front(List *list, void* data)
{
ListNode *node = POOL_GET(ListNode);
node->data = data;
node->prev = NULL;
node->next = list->head;
if (list_is_empty(list))
{
list->tail = node;
}
else
{
list->head->prev = node;
}
list->head = node;
list->len++;
}
void list_push_back(List *list, void* data)
{
ListNode* node = POOL_GET(ListNode);
node->data = data;
node->prev = list->tail;
node->next = NULL;
if (list_is_empty(list))
{
list->head = node;
}
else
{
list->tail->next = node;
}
list->tail = node;
list->len++;
}
void list_insert(List* list, void* data, unsigned int idx)
{
if(idx >= list->len)
{
list_push_back(list, data);
return;
}
if(idx == 0)
{
list_push_front(list, data);
return;
}
// After the above two checks the index is guaranteed to be inbetween the
// `head` and `tail` of the `list`. This means the actual list doesn't need
// to modify it's head and tail, only it's length. Simplifying the code below:
unsigned int curr_idx = 0;
ListItr itr = list_itr_create(list);
ListNode* ln;
while((ln = _list_itr_node_next(&itr)))
{
if(idx == curr_idx++)
{
ListNode *node = POOL_GET(ListNode);
node->prev = ln->prev;
node->next = ln;
ln->prev->next = node;
ln->prev = node;
node->data = data;
list->len++;
return;
}
}
}
bool list_swap(List* list, unsigned int idx_a, unsigned int idx_b)
{
if(idx_a >= list->len || idx_b >= list->len) return false;
if(idx_a == idx_b) return true; // swapping with yourself isn't technically an error
unsigned int curr_idx = 0;
unsigned int max_idx = idx_a > idx_b ? idx_a : idx_b;
ListNode* node_a = NULL;
ListNode* node_b = NULL;
ListItr itr = list_itr_create(list);
ListNode* ln;
do
{
ln = _list_itr_node_next(&itr);
if(idx_a == curr_idx)
{
node_a = ln;
continue;
}
if(idx_b == curr_idx)
{
node_b = ln;
continue;
}
}
while(max_idx != curr_idx++);
// Just swap the data pointers
void* tmp = node_a->data;
node_a->data = node_b->data;
node_b->data = tmp;
return true;
}
static void _list_remove_node(List *list, ListNode *node)
{
if(node->prev && !node->next) // end of list
{
node->prev->next = NULL;
list->tail = node->prev;
}
else if(node->prev && node->next) // somewhere in between
{
node->prev->next = node->next;
node->next->prev = node->prev;
}
else if(node->next && !node->prev) // beginning of list
{
node->next->prev = NULL;
list->head = node->next;
}
else if(!node->prev && !node->next) // only element in list
{
list->head = NULL;
list->tail = NULL;
}
POOL_FREE(ListNode, node);
list->len--;
}
int list_get_len(const List* list)
{
return list->len;
}
void* list_get_at_idx(List* list, unsigned int idx)
{
if(idx >= list_get_len(list)) return NULL;
int curr_idx = 0;
ListItr itr = list_itr_create(list);
void* data = NULL;
while((data = list_itr_next(&itr)))
{
if (idx == curr_idx++) return data;
}
return NULL;
}
bool list_remove_at_idx(List* list, unsigned int idx)
{
if(idx >= list_get_len(list)) return false;
int len = 0;
ListItr itr = list_itr_create(list);
ListNode* ln;
while((ln = _list_itr_node_next(&itr)))
{
if(idx == len++)
{
_list_remove_node(list, ln);
return true;
}
}
return false;
}
ListItr list_itr_create(List* list)
{
ListItr itr =
{
.list = list,
.next_node = !list_is_empty(list) ? list->head : NULL,
.current_node = NULL,
.direction = LIST_ITR_FORWARD,
};
return itr;
}
ListItr rev_list_itr_create(List* list)
{
ListItr itr =
{
.list = list,
.next_node = !list_is_empty(list) ? list->tail : NULL,
.current_node = NULL,
.direction = LIST_ITR_REVERSE,
};
return itr;
}
void* list_itr_next(ListItr* itr)
{
ListNode* ln = _list_itr_node_next(itr);
return ln ? ln->data : NULL;
}
static ListNode* _list_itr_node_next(ListItr* itr)
{
if(!itr->next_node) return NULL;
itr->current_node = itr->next_node;
ListNode* ln = itr->next_node;
ListNode* next_itr_node = (itr->direction == LIST_ITR_FORWARD) ? ln->next : ln->prev;
if(next_itr_node)
{
itr->next_node = next_itr_node;
return ln;
}
itr->next_node = NULL;
return ln;
}
void list_itr_remove_current_node(ListItr* itr)
{
if(!itr || !itr->current_node) return;
ListNode* tmp_prev = itr->current_node->prev;
_list_remove_node(itr->list, itr->current_node);
itr->current_node = tmp_prev;
}