332 lines
12 KiB
C
332 lines
12 KiB
C
/**
|
|
* Doubly linked list implementation.
|
|
*/
|
|
#ifndef INCLUDED_REFCOUNT_LIST_H
|
|
#define INCLUDED_REFCOUNT_LIST_H
|
|
|
|
#include <refcount/allocator.h>
|
|
#include <refcount/config.h>
|
|
#include <stdarg.h>
|
|
#include <stddef.h> // for NULL
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/**
|
|
* Linked list type with double linkage. To create an empty list, set it to
|
|
* NULL. This is not an opaque type and consumers of this file are free to
|
|
* access its fields.
|
|
*/
|
|
typedef struct RefcountList {
|
|
struct RefcountList *next; //!< The next link in the list, or NULL.
|
|
struct RefcountList *prev; //!< The previous link in the list, or NULL.
|
|
void *data; //!< The data of this link.
|
|
} RefcountList;
|
|
|
|
/**
|
|
* Callback to used to free list elements.
|
|
* @param ptr The pointer to free
|
|
* @see refcount_list_free
|
|
*/
|
|
typedef void (*refcount_list_destroy_callback_t)(void *ptr);
|
|
/**
|
|
* Callback to used to free list elements.
|
|
* @param ptr The pointer to free
|
|
* @param user_data Arbitrary user specified data
|
|
* @see refcount_list_free
|
|
*/
|
|
typedef void (*refcount_list_destroy_with_data_callback_t)(void *ptr,
|
|
void *user_data);
|
|
/**
|
|
* Callback used when copying elements from one list to another.
|
|
* @param ptr The element to copy
|
|
* @param user_data Arbitrary user specified data
|
|
* @return The new copy, or NULL if an error occurred
|
|
* @see refcount_list_copy
|
|
*/
|
|
typedef void *(*refcount_list_copy_callback_t)(const void *ptr,
|
|
void *user_data);
|
|
|
|
RefcountList *refcount_list_build_full_va(int count,
|
|
const RefcountAllocator *alloc,
|
|
va_list args);
|
|
|
|
/**
|
|
* Like #refcount_list_build, but lets you specify the allocator to use.
|
|
* @param count The number of elements given
|
|
* @param alloc The allocator to use
|
|
* @param ... The elements from which to build the list
|
|
* @return The built list, or NULL if a memory allocation error occurred
|
|
*/
|
|
static inline RefcountList *
|
|
refcount_list_build_full(int count, const RefcountAllocator *alloc, ...) {
|
|
va_list args;
|
|
va_start(args, alloc);
|
|
RefcountList *retval = refcount_list_build_full_va(count, alloc, args);
|
|
va_end(args);
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
static inline RefcountList *refcount_list_build(int count, ...) {
|
|
va_list args;
|
|
va_start(args, count);
|
|
RefcountList *retval =
|
|
refcount_list_build_full_va(count, refcount_global_allocator, args);
|
|
va_end(args);
|
|
return retval;
|
|
}
|
|
|
|
RefcountList *refcount_list_copy_full(const RefcountList *list,
|
|
refcount_list_copy_callback_t callback,
|
|
void *user_data,
|
|
const RefcountAllocator *alloc);
|
|
|
|
/**
|
|
* 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
|
|
* @param user_data Extra data to be passed to the copy callback
|
|
* @return The copy, or NULL if an allocation error occurred
|
|
*/
|
|
static inline RefcountList *
|
|
refcount_list_copy(const RefcountList *list,
|
|
refcount_list_copy_callback_t callback, void *user_data) {
|
|
return refcount_list_copy_full(list, callback, user_data,
|
|
refcount_global_allocator);
|
|
}
|
|
|
|
RefcountList *refcount_list_reverse(RefcountList *list);
|
|
|
|
void refcount_list_free_full(RefcountList *list,
|
|
refcount_list_destroy_callback_t callback,
|
|
const RefcountAllocator *alloc);
|
|
|
|
/**
|
|
* Free a #RefcountList.
|
|
* @param list The list to free
|
|
* @param callback The function to call to free each member, or NULL
|
|
*/
|
|
static inline void
|
|
refcount_list_free(RefcountList *list,
|
|
refcount_list_destroy_callback_t callback) {
|
|
refcount_list_free_full(list, callback, refcount_global_allocator);
|
|
}
|
|
|
|
void refcount_list_free_with_data_full(
|
|
RefcountList *list, refcount_list_destroy_with_data_callback_t callback,
|
|
void *data, const RefcountAllocator *alloc);
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
static inline void refcount_list_free_with_data(
|
|
RefcountList *list, refcount_list_destroy_with_data_callback_t callback,
|
|
void *data) {
|
|
refcount_list_free_with_data_full(list, callback, data,
|
|
refcount_global_allocator);
|
|
}
|
|
|
|
size_t refcount_list_length(const RefcountList *list);
|
|
|
|
/**
|
|
* Return the first element in a #RefcountList.
|
|
* @param list The list
|
|
* @return The first object in the list
|
|
*/
|
|
static inline void *refcount_list_peek(const RefcountList *list) {
|
|
if (!list) {
|
|
return NULL;
|
|
}
|
|
return list->data;
|
|
}
|
|
|
|
RefcountList *refcount_list_drop(RefcountList *list, size_t n);
|
|
|
|
void *refcount_list_nth(const RefcountList *list, size_t n);
|
|
|
|
RefcountList *
|
|
refcount_list_remove_full(RefcountList *list, RefcountList *link,
|
|
refcount_list_destroy_callback_t callback,
|
|
const RefcountAllocator *alloc);
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
static inline RefcountList *
|
|
refcount_list_remove(RefcountList *list, RefcountList *link,
|
|
refcount_list_destroy_callback_t callback) {
|
|
return refcount_list_remove_full(list, link, callback,
|
|
refcount_global_allocator);
|
|
}
|
|
|
|
/**
|
|
* Same as #refcount_list_remove_with_data, but lets you specify the allocator
|
|
* to use.
|
|
* @param list The list
|
|
* @param callback The function to call to free the member, or NULL
|
|
* @param link The link to remove, or NULL
|
|
* @param user_data Extra data to pass to the callback
|
|
* @param alloc The allocator to use
|
|
* @return The new value of the list
|
|
*/
|
|
static inline RefcountList *refcount_list_remove_with_data_full(
|
|
RefcountList *list, RefcountList *link,
|
|
refcount_list_destroy_with_data_callback_t callback, void *user_data,
|
|
const RefcountAllocator *alloc) {
|
|
if (list && link && callback) {
|
|
callback(link->data, user_data);
|
|
}
|
|
return refcount_list_remove_full(list, link, NULL, alloc);
|
|
}
|
|
|
|
/**
|
|
* Same as #refcount_list_remove except that the callback takes an additional
|
|
* argument. This also does nothing if either of list or link is NULL.
|
|
* @param list The list
|
|
* @param callback The function to call to free the member, or NULL
|
|
* @param link The link to remove, or NULL
|
|
* @param user_data Extra data to pass to the callback
|
|
* @return The new value of the list
|
|
*/
|
|
static inline RefcountList *refcount_list_remove_with_data(
|
|
RefcountList *list, RefcountList *link,
|
|
refcount_list_destroy_with_data_callback_t callback, void *user_data) {
|
|
return refcount_list_remove_with_data_full(list, link, callback, user_data,
|
|
refcount_global_allocator);
|
|
}
|
|
|
|
/**
|
|
* Same as #refcount_list_pop, but lets you specify the allocator to use.
|
|
*
|
|
* @param list The list
|
|
* @param callback The function to call to free the removed, or NULL
|
|
* @param alloc The allocator to use
|
|
* @return The new value of the list
|
|
*
|
|
* @see refcount_list_remove
|
|
*/
|
|
static inline RefcountList *
|
|
refcount_list_pop_full(RefcountList *list,
|
|
refcount_list_destroy_callback_t callback,
|
|
const RefcountAllocator *alloc) {
|
|
return refcount_list_remove_full(list, list, callback, alloc);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
static inline RefcountList *
|
|
refcount_list_pop(RefcountList *list,
|
|
refcount_list_destroy_callback_t callback) {
|
|
return refcount_list_pop_full(list, callback, refcount_global_allocator);
|
|
}
|
|
|
|
/**
|
|
* Same as #refcount_list_pop_with_data, but lets specify the allocator to use.
|
|
* @param list The list
|
|
* @param callback The function to call to free the member, or NULL
|
|
* @param user_data Extra data to pass to the callback
|
|
* @param alloc The allocator to use
|
|
* @return The new value of the list
|
|
*/
|
|
static inline RefcountList *refcount_list_pop_with_data_full(
|
|
RefcountList *list, refcount_list_destroy_with_data_callback_t callback,
|
|
void *user_data, const RefcountAllocator *alloc) {
|
|
if (list && callback) {
|
|
callback(list->data, user_data);
|
|
}
|
|
return refcount_list_pop_full(list, NULL, alloc);
|
|
}
|
|
|
|
/**
|
|
* Same as #refcount_list_pop except that the callback takes an additional
|
|
* argument.
|
|
* @param list The list
|
|
* @param callback The function to call to free the member, or NULL
|
|
* @param user_data Extra data to pass to the callback
|
|
* @return The new value of the list
|
|
*/
|
|
static inline RefcountList *
|
|
refcount_list_pop_with_data(RefcountList *list,
|
|
refcount_list_destroy_with_data_callback_t callback,
|
|
void *user_data) {
|
|
return refcount_list_pop_with_data_full(list, callback, user_data,
|
|
refcount_global_allocator);
|
|
}
|
|
|
|
RefcountList *refcount_list_push_full(RefcountList *list, void *element,
|
|
const RefcountAllocator *alloc);
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
static inline RefcountList *refcount_list_push(RefcountList *list,
|
|
void *element) {
|
|
return refcount_list_push_full(list, element, refcount_global_allocator);
|
|
}
|
|
|
|
RefcountList *refcount_list_tail(RefcountList *list);
|
|
|
|
RefcountList *refcount_list_join(RefcountList *list1, RefcountList *list2);
|
|
|
|
RefcountList *refcount_list_push_back_full(RefcountList *list, void *element,
|
|
const RefcountAllocator *alloc);
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
static inline RefcountList *refcount_list_push_back(RefcountList *list,
|
|
void *element) {
|
|
return refcount_list_push_back_full(list, element,
|
|
refcount_global_allocator);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|