/** * @file * Doubly linked list implementation. */ #include "refcount/list.h" #include #include /** * 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); }