289 lines
8.2 KiB
C
289 lines
8.2 KiB
C
/**
|
|
* @file
|
|
* Doubly linked list implementation.
|
|
*/
|
|
#include "refcount/list.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
|
|
/**
|
|
* Like #refcount_list_build, but lets you specify the allocator to use and
|
|
* takes a va_list instead of variable arguments.
|
|
* @param count The number of elements given
|
|
* @param alloc The allocator to use
|
|
* @param args The va_list to use to get the elements
|
|
* @return The built list, or NULL if a memory allocation error occurred
|
|
*/
|
|
RefcountList *refcount_list_build_full_va(int count,
|
|
const RefcountAllocator *alloc,
|
|
va_list args) {
|
|
RefcountList *start = NULL;
|
|
RefcountList *end;
|
|
while (count--) {
|
|
RefcountList *new_list = refcount_malloc(alloc, sizeof(RefcountList));
|
|
if (!new_list) {
|
|
refcount_list_free(start, NULL);
|
|
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;
|
|
}
|
|
}
|
|
return start;
|
|
}
|
|
|
|
/**
|
|
* Same as #refcount_list_copy, but lets you specify the allocator.
|
|
* @param list The list to copy
|
|
* @param callback The function to copy each element of the list, or NULL
|
|
* @param user_data Extra data to be passed to the copy callback
|
|
* @param alloc The allocator to use
|
|
* @return The copy, or NULL if an allocation error occurred
|
|
*/
|
|
RefcountList *refcount_list_copy_full(const RefcountList *list,
|
|
refcount_list_copy_callback_t callback,
|
|
void *user_data,
|
|
const RefcountAllocator *alloc) {
|
|
RefcountList *start = NULL;
|
|
RefcountList *end;
|
|
while (list) {
|
|
RefcountList *new_end = refcount_malloc(alloc, sizeof(RefcountList));
|
|
if (!new_end) {
|
|
refcount_list_free_full(start, NULL, alloc);
|
|
return NULL;
|
|
}
|
|
new_end->data = callback ? callback(list->data, user_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 alloc The allocator to use
|
|
* @param callback The function to call to free each member, or NULL
|
|
*/
|
|
void refcount_list_free_full(RefcountList *list,
|
|
refcount_list_destroy_callback_t callback,
|
|
const RefcountAllocator *alloc) {
|
|
while (list) {
|
|
RefcountList *next = list->next;
|
|
if (callback) {
|
|
callback(list->data);
|
|
}
|
|
refcount_free(alloc, 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
|
|
* @param alloc The allocator to use
|
|
*/
|
|
void refcount_list_free_with_data_full(
|
|
RefcountList *list, refcount_list_destroy_with_data_callback_t callback,
|
|
void *data, const RefcountAllocator *alloc) {
|
|
while (list) {
|
|
RefcountList *next = list->next;
|
|
if (callback) {
|
|
callback(list->data, data);
|
|
}
|
|
refcount_free(alloc, 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(const 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(const RefcountList *list, size_t n) {
|
|
RefcountList *link = refcount_list_drop((RefcountList *) list, n);
|
|
return link ? link->data : NULL;
|
|
}
|
|
|
|
/**
|
|
* The same as #refcount_list_remove, but lets you specify the allocator to use.
|
|
* @param list The list
|
|
* @param link The link to remove, or NULL
|
|
* @param callback The function to call to free the member, or NULL
|
|
* @param alloc The allocator to use
|
|
* @return The new value of the list
|
|
*/
|
|
RefcountList *
|
|
refcount_list_remove_full(RefcountList *list, RefcountList *link,
|
|
refcount_list_destroy_callback_t callback,
|
|
const RefcountAllocator *alloc) {
|
|
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(alloc, link);
|
|
return list == link ? rest : list;
|
|
}
|
|
|
|
/**
|
|
* The same as #refcount_list_push, but lets you specify the allocator to
|
|
* use.
|
|
* @param list The list
|
|
* @param element The element to add
|
|
* @param alloc The allocator to use
|
|
* @return The new value of the list, or NULL if allocation of the new node
|
|
* failed
|
|
*/
|
|
RefcountList *refcount_list_push_full(RefcountList *list, void *element,
|
|
const RefcountAllocator *alloc) {
|
|
RefcountList *new_head = refcount_malloc(alloc, 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->next = NULL;
|
|
}
|
|
list2->prev = end1;
|
|
}
|
|
return list1 ? list1 : list2;
|
|
}
|
|
|
|
/**
|
|
* The same as #refcount_list_push_back, but lets you specify the allocator to
|
|
* use.
|
|
* @param list The list
|
|
* @param element The element to add
|
|
* @param alloc The allocator to use
|
|
* @return The new value of the list, or NULL if allocation of the new node
|
|
* failed
|
|
*/
|
|
RefcountList *refcount_list_push_back_full(RefcountList *list, void *element,
|
|
const RefcountAllocator *alloc) {
|
|
RefcountList *new_end = refcount_malloc(alloc, 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);
|
|
}
|