waveview/lib/dlist.c

415 lines
9.1 KiB
C

/*
* dlist.c - A generic doubly linked list.
*
* include LICENSE
*/
#include <stdio.h>
#include <sys/time.h>
#include <appclass.h>
#include <dlist.h>
#ifdef TRACE_MEM
#include <tracemem.h>
#endif
/***************************************************************************
*
* DList(AppClass) Methods : A doubly linked list.
*
***************************************************************************/
static DList *dlist_new_node(AppClass * data);
static void dlist_construct_node(DList * node, AppClass * data);
static DList *dlist_new_node(AppClass * data)
{
DList *node;
node = app_new0(DList, 1);
dlist_construct_node(node, data);
app_class_overload_destroy((AppClass *) node, dlist_destroy_node);
return node;
}
static void dlist_construct_node(DList * node, AppClass * data)
{
app_class_construct((AppClass *) node);
node->prev = node;
node->next = node;
node->data = data;
}
void dlist_destroy_node(void *node)
{
DList *_node = (DList *) node;
if (_node == NULL) {
return;
}
if (_node->data) {
app_class_unref(_node->data);
}
app_class_destroy(node);
}
/** \brief Add a new node to the end of the list.
*
* If cmp argument is not NULL, use cmp() to see if node already exists and
* don't add node if it exists.
*
* This function can unref data if not added, if asked by user (*res != 0).
* The res variable is set to 1 if data is not added.
*
* head is modified only when the list is empty.
* So it is safe to add node to an existing list.
*/
DList *dlist_add(DList * head, AppClass * data, DListFP_Cmp cmp, int *res)
{
if (head == NULL) {
/* The list is empty, make new node the head. */
head = dlist_new_node(NULL); /* special head node */
}
DList *node = head->next;
/* Walk the list to find the end */
if (cmp) {
while (node != head) {
if ((*cmp) (node->data, data) == 0) {
/* node already exists and we were asked to keep nodes unique */
if (res) {
if (*res) {
app_class_unref(data); /* elem destroyed */
}
*res = 1; /* elem not added to the list */
}
return head;
}
node = node->next;
}
}
/* reached the end - add at the tail */
dlist_add_at(head->prev, data);
if (res) {
*res = 0; /* elem added to the list */
}
return head;
}
/** \brief Add a new node after this node
*/
DList *dlist_add_at(DList * node, AppClass * data)
{
DList *head = node;
if (head == NULL) {
head = dlist_new_node(NULL); /* special head node */
}
DList *newnode = dlist_new_node(data);
newnode->prev = head->next->prev;
head->next->prev = newnode;
newnode->next = head->next;
head->next = newnode;
return head;
}
/* add a node at the head */
DList *dlist_add_head(DList * head, AppClass * data)
{
return dlist_add_at(head, data);
}
/* add a node at the tail */
DList *dlist_add_tail(DList * head, AppClass * data)
{
if ( ! head) {
return dlist_add_at(head, data);
}
dlist_add_at(head->prev, data);
return head;
}
/*
* remove node from list
* free the node and data if not refed
*/
void dlist_remove(DList * node)
{
if (node) {
node->prev->next = node->next;
node->next->prev = node->prev;
/* free the node and data */
app_class_unref((AppClass *) node);
}
}
/*
* get next data at head or tail
* return data
*/
AppClass *dlist_get_data(DList * head, DList * node)
{
if (head && node != head) {
return node->data;
}
return NULL;
}
/*
* get next data at head or tail
* remove node from list
* return data
* caller must call app_class_unref to free the data
*/
AppClass *dlist_get_data_rm_node(DList * head, DList * node)
{
AppClass *data = dlist_get_data(head, node);
if (data) {
app_class_ref(data);
dlist_remove(node);
}
return data;
}
/* get next data at head */
AppClass *dlist_dequeue_head(DList * head)
{
if (head) {
return dlist_get_data_rm_node(head, head->next);
}
return NULL;
}
/* get next data at tail */
AppClass *dlist_dequeue_tail(DList * head)
{
if (head) {
return dlist_get_data_rm_node(head, head->prev);
}
return NULL;
}
/** \brief Conditionally delete a node from the list.
*
* Delete a node from the list if the node's data matches the specified
* data. Returns the head of the modified list.
*/
DList *dlist_delete(DList * head, AppClass * data, DListFP_Cmp cmp)
{
if (cmp == NULL) {
msg_fatal("compare function not specified");
}
if (head == NULL) {
return NULL;
}
DList *node = head->next;
while (node != head) {
if ((*cmp) (node->data, data) == 0) {
/* remove the node from the list and also unref the node */
dlist_remove(node);
return head;
}
/* move on to next node */
node = node->next;
}
/* if we get here, data wasn't found, just return original head */
return head;
}
void dlist_delete_list(DList ** head)
{
if ( *head ){
if ( (*head)->next == *head ){
/* destroy special node as well */
app_class_unref((AppClass *) *head );
} else {
dlist_delete_all(*head);
}
*head = NULL;
}
}
/** \brief Blow away the entire list.
* Warning: after this function we must not reuse the head without
* setting it to NULL. Use dlist_delete_list instead.
*/
void dlist_delete_all(DList * head)
{
if (head == NULL) {
return;
}
DList *node = head->next;
DList *next;
while (node != head) {
next = node->next;
app_class_unref((AppClass *) node);
node = next;
}
/* destroy special node as well */
app_class_unref((AppClass *) node);
}
/** \brief Lookup an item in the list.
*
* Walk the list pointed to by head and return a pointer to the data if
* found. If not found, return NULL.
*
* \param head The head of the list to be iterated.
* \param data The data to be passed to the func when it is applied.
* \param cmp A function to be used for comparing the items.
*
* \return A pointer to the data found, or NULL if not found.
*/
AppClass *dlist_lookup(DList * head, AppClass * data, DListFP_Cmp cmp)
{
if (head == NULL) {
return NULL;
}
DList *node = head->next;
if (cmp == NULL) {
msg_fatal("compare function not specified");
}
while (node != head) {
if ((*cmp) (node->data, data) == 0) {
return node->data;
}
node = node->next;
}
/* If we get here, no node was found, return NULL. */
return NULL;
}
/*
* a simple node compare function for the iterator.
*/
int dlist_iterator_cmp(AppClass * n1, AppClass * n2)
{
/*
* Since this is only used in the iterator, we are guaranteed that it is
* safe to compare data pointers because both n1 and n2 came from the
* list.
*/
return (int) (n1 - n2);
}
/** \brief Iterate over all elements of the list.
*
* For each element, call the user supplied iterator function and pass it the
* node data and the user_data. If the iterator function return non-zero,
* remove the node from the list.
*
* \param head The head of the list to be iterated.
* \param func The function to be applied.
* \param user_data The data to be passed to the func when it is applied.
*
* \return An int : return what the function return
* func return:
* - 0 = OK all elements visited - return 0
* - DLIST_RM_NODE = remove the node, stop and return DLIST_RM_NODE
* - DLIST_RM_NODE_CONT = remove the node and continue the loop
* - any other value = stop the loop and return the value
*/
int dlist_iterator(DList * head, DListFP_Iter func, void *user_data)
{
if (func == NULL) {
msg_fatal("no iteration func supplied");
}
if (head == NULL) {
return 0;
}
DList *node = head->next;
while (node != head) {
/*
* WARNING: if next = node->next is placed before the func,
* we can suppress the node but we may have problem adding to the list
* WARNING: if next = node->next is placed after the func
* we have problem if node is suppressed by the func,
* but no problen to add to the list.
*/
int ret = (*func) (node->data, user_data);
/* placed here ! : so NEVER destroy the node in the function */
/* let the following code do the destroy */
DList *next = node->next; /* must be before remove node */
if (ret) {
/* remove node also unref the node + node->data */
if (ret == DLIST_RM_NODE || ret == DLIST_RM_NODE_CONT) {
dlist_remove(node);
}
if (ret != DLIST_RM_NODE_CONT) {
return ret;
}
}
node = next;
}
return 0;
}
/*
* return number of nodes in list
*/
int dlist_get_nelem(DList * head)
{
if (dlist_isempty(head)) {
return 0;
}
DList *node = head->next;
int n = 0;
while (node != head) {
n++;
node = node->next;
}
return n;
}
/*
* get a member of the list at index
*/
AppClass *dlist_get_ndata(DList * head, int n)
{
if (head == NULL) {
return NULL;
}
DList *node = head->next;
int i = 0;
while (node != head) {
if (i == n) {
return node->data;
}
i++;
node = node->next;
}
return NULL;
}