Initial commit

This commit is contained in:
2025-08-28 04:59:23 -07:00
commit 5ac6aaf900
14 changed files with 4983 additions and 0 deletions

302
src/list.c Normal file
View File

@ -0,0 +1,302 @@
/**
* @file
* Doubly linked list implementation.
*/
#include "refcount/list.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
/**
* Build a #RefcountList from a number of elements.
*
* > that this will return NULL if count is 0 or if a memory allocation error
* > occurred. However, as no allocation is preformed is count is 0, these
* > errors should be mutually exclusive.
*
* @param count The number of elements given
* @param ... The elements from which to build the list
* @return The built list, or NULL if a memory allocation error occurred
*/
RefcountList *refcount_list_build(int count, ...) {
va_list args;
va_start(args, count);
RefcountList *start = NULL;
RefcountList *end;
while (count--) {
RefcountList *new_list = refcount_malloc(sizeof(RefcountList));
if (!new_list) {
refcount_list_free(start, NULL);
va_end(args);
return NULL;
}
new_list->data = va_arg(args, void *);
new_list->next = NULL;
if (!start) {
start = new_list;
end = new_list;
new_list->prev = NULL;
} else {
end->next = new_list;
new_list->prev = end;
end = new_list;
}
}
va_end(args);
return start;
}
/**
* Return a new #RefcountList that is a copy of list. This does no allocation if
* the input list is NULL.
* @param list The list to copy
* @param callback The function to copy each element of the list, or NULL
* @return The copy, or NULL if an allocation error occurred
*/
RefcountList *refcount_list_copy(RefcountList *list,
refcount_list_copy_callback_t callback) {
RefcountList *start = NULL;
RefcountList *end;
while (list) {
RefcountList *new_end = refcount_malloc(sizeof(RefcountList));
if (!new_end) {
refcount_list_free(start, NULL);
return NULL;
}
new_end->data = callback ? callback(list->data) : list->data;
new_end->next = NULL;
if (!start) {
start = new_end;
end = new_end;
new_end->prev = NULL;
} else {
end->next = new_end;
new_end->prev = end;
end = new_end;
}
list = list->next;
}
return start;
}
/**
* Reverse a #RefcountList and return it. This mutates the input list.
* @param list The list to reverse destructively
* @return The reversed list
*/
RefcountList *refcount_list_reverse(RefcountList *list) {
while (list) {
RefcountList *next = list->next;
list->next = list->prev;
list->prev = next;
if (!next) {
return list;
}
list = next;
}
return NULL;
}
/**
* Free a #RefcountList.
* @param list The list to free
* @param callback The function to call to free each member, or NULL
*/
void refcount_list_free(RefcountList *list,
refcount_list_destroy_callback_t callback) {
while (list) {
RefcountList *next = list->next;
if (callback) {
callback(list->data);
}
refcount_free(list);
list = next;
}
}
/**
* Free a #RefcountList.
* @param list The list to free
* @param callback The function to call to free each member, or NULL
* @param data Extra data to pass to the second argument of the callback
*/
void refcount_list_free_with_data(RefcountList *list,
refcount_list_foreach_callback_t callback,
void *data) {
while (list) {
RefcountList *next = list->next;
if (callback) {
callback(list->data, data);
}
refcount_free(list);
list = next;
}
}
/**
* Get the length of a #RefcountList. This will hang if the list is circular.
* @param list The list
* @return The length of the list
*/
size_t refcount_list_length(RefcountList *list) {
size_t len = 0;
while (list) {
list = list->next;
++len;
}
return len;
}
/**
* Return the sub-list starting at the Nth link. That is, drop the first n
* links. If n is greater than the length of the list, return NULL.
* @param list The list
* @param n The number of elements to drop
* @return The Nth link of the list
*/
RefcountList *refcount_list_drop(RefcountList *list, size_t n) {
for (size_t i = 0; list; ++i, list = list->next) {
if (i == n) {
return list;
}
}
return NULL;
}
/**
* Return the Nth element of a #RefcountList. If n is greater than the length of
* the list, return NULL.
* @param list The list
* @param n The index of the element
* @return The Nth element of the list
*/
void *refcount_list_nth(RefcountList *list, size_t n) {
RefcountList *link = refcount_list_drop(list, n);
return link ? link->data : NULL;
}
/**
* Remove the first element from a #RefcountList. Note that you must save the
* return value as it is the new value of the list. If the list is a sub-list,
* the state of the larger list is undefined after this call.
*
* @param list The list
* @param callback The function to call to free the removed, or NULL
* @return The new value of the list
*
* @see refcount_list_remove
*/
RefcountList *refcount_list_pop(RefcountList *list,
refcount_list_destroy_callback_t callback) {
return refcount_list_remove(list, list, callback);
}
/**
* Remove a link from a #RefcountList. Note that you must save the return
* value as it is the new value of the list.
*
* If the given list or link is NULL, this does nothing. It is undefined
* behavior if link is not in list.
*
* @param list The list
* @param link The link to remove, or NULL
* @param callback The function to call to free the member, or NULL
* @return The new value of the list
*/
RefcountList *refcount_list_remove(RefcountList *list, RefcountList *link,
refcount_list_destroy_callback_t callback) {
if (!list || !link) {
return list;
}
RefcountList *rest = link->next;
if (rest) {
rest->prev = link->prev;
}
if (link->prev) {
link->prev->next = rest;
}
if (callback) {
callback(link->data);
}
refcount_free(link);
return list == link ? rest : list;
}
/**
* Add an element to the start of a #RefcountList. Note that you must save the
* return value as it is the new value of the list.
* @param list The list
* @param element The element to add
* @return The new value of the list, or NULL if allocation of the new node
* failed
*/
RefcountList *refcount_list_push(RefcountList *list, void *element) {
RefcountList *new_head = refcount_malloc(sizeof(RefcountList));
if (!new_head) {
return NULL;
}
new_head->data = element;
new_head->next = list;
new_head->prev = NULL;
if (list) {
list->prev = new_head;
}
return new_head;
}
/**
* Return the last link in a #RefcountList.
* @param list The list
* @return The last link in the list, or NULL if the list is empty
*/
RefcountList *refcount_list_tail(RefcountList *list) {
if (!list) {
return NULL;
}
RefcountList *end = list;
while (end && end->next) {
end = end->next;
}
return end;
}
/**
* Join list1 and list2. This mutates both lists. If list2 is part of another
* list, it will be removed from that list!
* @param list1 The first list
* @param list2 The second list
* @return The joined list
*/
RefcountList *refcount_list_join(RefcountList *list1, RefcountList *list2) {
RefcountList *end1 = refcount_list_tail(list1);
if (end1) {
end1->next = list2;
}
if (list2) {
if (list2->prev) {
list2->prev = NULL;
}
list2->prev = end1;
}
return list1 ? list1 : list2;
}
/**
* Add an element to the end of a #RefcountList. Note that you must save the
* return value as it is the new value of the list.
* @param list The list
* @param element The element to add
* @return The new value of the list, or NULL if allocation of the new node
* failed
*/
RefcountList *refcount_list_push_back(RefcountList *list, void *element) {
RefcountList *new_end = refcount_malloc(sizeof(RefcountList));
if (!new_end) {
return NULL;
}
new_end->data = element;
new_end->next = NULL;
new_end->prev = NULL;
return refcount_list_join(list, new_end);
}