829 lines
17 KiB
C
829 lines
17 KiB
C
/** \file LL.c
|
|
* Define routines to deal with doubly linked lists
|
|
*/
|
|
|
|
/* This file is part of LCDproc.
|
|
*
|
|
* This file is released under the GNU General Public License.
|
|
* Refer to the COPYING file distributed with this package.
|
|
*
|
|
* Copyright(c) 1999, William Ferrell
|
|
* (c) 2000, Guillaume Filion
|
|
* (c) 2001, Joris Robijn
|
|
* (c) 2008, Peter Marschall
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include "LL.h"
|
|
|
|
#ifdef DEBUG
|
|
#undef DEBUG
|
|
#endif
|
|
|
|
//TODO: Test everything?
|
|
|
|
|
|
/** Create new linked list.
|
|
* \return Pointer to freshly created list object; \c NULL on error.
|
|
*/
|
|
LinkedList *
|
|
LL_new(void)
|
|
{
|
|
LinkedList *list;
|
|
|
|
list = malloc(sizeof(LinkedList));
|
|
if (list == NULL)
|
|
return NULL;
|
|
|
|
list->head.data = NULL;
|
|
list->head.prev = NULL;
|
|
list->head.next = &list->tail;
|
|
list->tail.data = NULL;
|
|
list->tail.prev = &list->head;
|
|
list->tail.next = NULL;
|
|
list->current = &list->head;
|
|
|
|
return list;
|
|
}
|
|
|
|
|
|
/** Destroy the entire list.
|
|
*
|
|
* \note
|
|
* This does not free the data, only the list itself.
|
|
*
|
|
* \param list List object to be destroyed.
|
|
* \retval <0 error
|
|
* \retval 0 success
|
|
*/
|
|
int
|
|
LL_Destroy(LinkedList *list)
|
|
{
|
|
LL_node *next, *prev;
|
|
LL_node *node;
|
|
|
|
if (!list)
|
|
return -1;
|
|
|
|
node = &list->head;
|
|
|
|
for (node = node->next; node && node->next; node = next) {
|
|
// Avoid accessing "node" after it's freed.. :)
|
|
next = node->next;
|
|
prev = node->prev;
|
|
|
|
if (next != NULL)
|
|
next->prev = prev;
|
|
if (prev != NULL)
|
|
prev->next = next;
|
|
|
|
node->next = NULL;
|
|
node->prev = NULL;
|
|
|
|
free(node);
|
|
}
|
|
|
|
free(list);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Return to the beginning of the list.
|
|
* Set list's \c current pointer to the first node in the list.
|
|
* \param list List object.
|
|
* \retval <0 error: no list given
|
|
* \retval 0 success
|
|
*/
|
|
int
|
|
LL_Rewind(LinkedList *list)
|
|
{
|
|
if (!list)
|
|
return -1;
|
|
|
|
list->current = (list->head.next != &list->tail)
|
|
? list->head.next
|
|
: &list->head;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Jump to the end of the list.
|
|
* Set list's \c current pointer to the last node in the list.
|
|
* \param list List object.
|
|
* \retval <0 error: no list given
|
|
* \retval 0 success
|
|
*/
|
|
int
|
|
LL_End(LinkedList *list)
|
|
{
|
|
if (!list)
|
|
return -1;
|
|
|
|
list->current = (list->tail.prev != &list->head)
|
|
? list->tail.prev
|
|
: &list->tail;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Go to the next node of the list.
|
|
* Advance list's \c current pointer to the next node in the list.
|
|
* \param list List object.
|
|
* \retval <0 error: no list given or no next node
|
|
* \retval 0 success
|
|
*/
|
|
int
|
|
LL_Next(LinkedList *list)
|
|
{
|
|
if (!list)
|
|
return -1;
|
|
if (!list->current)
|
|
return -1;
|
|
|
|
if (list->current->next == &list->tail)
|
|
return -1;
|
|
|
|
list->current = list->current->next;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Go to the previous node of the list.
|
|
* Set list's \c current pointer to the previous node in the list.
|
|
* \param list List object.
|
|
* \retval <0 error: no list given or no previous node
|
|
* \retval 0 success
|
|
*/
|
|
int
|
|
LL_Prev(LinkedList *list)
|
|
{
|
|
if (!list)
|
|
return -1;
|
|
if (!list->current)
|
|
return -1;
|
|
|
|
if (list->current->prev == &list->head)
|
|
return -1;
|
|
|
|
list->current = list->current->prev;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Access current node's data.
|
|
* Return pointer to list's \c current node's data.
|
|
* \param list List object.
|
|
* \return Pointer to \c current node's payload data;
|
|
* \c NULL may be empty payload or an error.
|
|
*/
|
|
void *
|
|
LL_Get(LinkedList *list)
|
|
{
|
|
if (!list)
|
|
return NULL;
|
|
if (!list->current)
|
|
return NULL;
|
|
|
|
return list->current->data;
|
|
}
|
|
|
|
|
|
/** Set/change current node's data.
|
|
* \param list List object.
|
|
* \param data Pointer to data to be set.
|
|
* \retval <0 error: no list given, or no current node
|
|
* \retval 0 success
|
|
*/
|
|
int
|
|
LL_Put(LinkedList *list, void *data)
|
|
{
|
|
if (!list)
|
|
return -1;
|
|
if (!list->current)
|
|
return -1;
|
|
|
|
list->current->data = data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Get current node in list.
|
|
* \param list List object.
|
|
* \return Pointer to current node.
|
|
*/
|
|
LL_node *
|
|
LL_GetNode(LinkedList *list)
|
|
{
|
|
if (!list)
|
|
return NULL;
|
|
|
|
return list->current;
|
|
}
|
|
|
|
|
|
/** Set list's \c current pointer to a specific node.
|
|
*
|
|
* \warning
|
|
* Don't use this unless you know what you're doing.
|
|
*
|
|
* \param list List object.
|
|
* \param node Node to become new \c current.
|
|
* \retval <0 error
|
|
* \retval 0 success
|
|
*/
|
|
int
|
|
LL_PutNode(LinkedList *list, LL_node *node)
|
|
{
|
|
if (!list)
|
|
return -1;
|
|
if (!node)
|
|
return -1;
|
|
|
|
list->current = node;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Access list's first node's data.
|
|
* Set list's \c current pointer to the first node and return its data.
|
|
* \param list List object.
|
|
* \return Pointer to first node's data; \c NULL on error.
|
|
*/
|
|
void *
|
|
LL_GetFirst(LinkedList *list)
|
|
{
|
|
if (!list)
|
|
return NULL;
|
|
|
|
if (0 > LL_Rewind(list))
|
|
return NULL;
|
|
|
|
return LL_Get(list);
|
|
}
|
|
|
|
|
|
/** Access next node's data.
|
|
* Advance list's \c current pointer to the next node and return its data.
|
|
* \param list List object.
|
|
* \return Pointer to next node's data; \c NULL on error.
|
|
*/
|
|
void *
|
|
LL_GetNext(LinkedList *list)
|
|
{
|
|
if (!list)
|
|
return NULL;
|
|
|
|
if (0 > LL_Next(list))
|
|
return NULL;
|
|
|
|
return LL_Get(list);
|
|
}
|
|
|
|
|
|
/** Access previous node's data.
|
|
* Set list's \c current pointer to the previous node, and return its data.
|
|
* \param list List object.
|
|
* \return Pointer to previous node's data; \c NULL on error.
|
|
*/
|
|
void *
|
|
LL_GetPrev(LinkedList *list)
|
|
{
|
|
if (!list)
|
|
return NULL;
|
|
|
|
if (0 > LL_Prev(list))
|
|
return NULL;
|
|
|
|
return LL_Get(list);
|
|
}
|
|
|
|
|
|
/** Access list's last node's data.
|
|
* Set list's \c current pointer to the last node and return its data.
|
|
* \param list List object.
|
|
* \return Pointer to last node's data; \c NULL on error.
|
|
*/
|
|
void *
|
|
LL_GetLast(LinkedList *list)
|
|
{
|
|
if (!list)
|
|
return NULL;
|
|
|
|
if (0 > LL_End(list))
|
|
return NULL;
|
|
|
|
return LL_Get(list);
|
|
}
|
|
|
|
|
|
/** Add/append a new node after current one in the list.
|
|
* Update the list's \c current pointer to point to the freshly created node.
|
|
* \param list List object.
|
|
* \param add Pointer to new node's data.
|
|
* \retval <0 error
|
|
* \retval 0 success
|
|
*/
|
|
int
|
|
LL_AddNode(LinkedList *list, void *add)
|
|
{
|
|
LL_node *node;
|
|
|
|
if (!list)
|
|
return -1;
|
|
if (!list->current)
|
|
return -1;
|
|
|
|
node = malloc(sizeof(LL_node));
|
|
if (node == NULL)
|
|
return -1;
|
|
|
|
// we're behind the list's end, go to previous node
|
|
if (list->current == &list->tail)
|
|
list->current = list->current->prev;
|
|
|
|
// Set node data
|
|
node->next = list->current->next;
|
|
node->prev = list->current;
|
|
node->data = add;
|
|
|
|
// Re-link
|
|
if (node->next)
|
|
node->next->prev = node;
|
|
list->current->next = node;
|
|
|
|
list->current = node;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Add/insert a new node before current one in the list.
|
|
* Update the list's \c current pointer to point to the freshly created node.
|
|
* \param list List object.
|
|
* \param add Pointer to new node's data.
|
|
* \retval <0 error
|
|
* \retval 0 success
|
|
*/
|
|
int
|
|
LL_InsertNode(LinkedList *list, void *add)
|
|
{
|
|
LL_node *node;
|
|
|
|
if (!list)
|
|
return -1;
|
|
if (!add)
|
|
return -1;
|
|
if (!list->current)
|
|
return -1;
|
|
|
|
node = malloc(sizeof(LL_node));
|
|
if (node == NULL)
|
|
return -1;
|
|
|
|
// we're before the list's start, go to next node
|
|
if (list->current == &list->head)
|
|
list->current = list->current->next;
|
|
|
|
node->next = list->current;
|
|
node->prev = list->current->prev;
|
|
node->data = add;
|
|
|
|
if (list->current->prev)
|
|
list->current->prev->next = node;
|
|
list->current->prev = node;
|
|
|
|
list->current = node;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Remove current node from the list.
|
|
* Set the list's \c current pointer to the one denoted by \c whereto.
|
|
* \param list List object.
|
|
* \param whereto Direction where to set the list's \c current pointer
|
|
* \return Pointer to data of deleted node; \c NULL on error.
|
|
*/
|
|
void *
|
|
LL_DeleteNode(LinkedList *list, Direction whereto)
|
|
{
|
|
LL_node *next, *prev;
|
|
void *data;
|
|
|
|
if (!list)
|
|
return NULL;
|
|
if (!list->current)
|
|
return NULL;
|
|
if (list->current == &list->head)
|
|
return NULL;
|
|
if (list->current == &list->tail)
|
|
return NULL;
|
|
|
|
next = list->current->next;
|
|
prev = list->current->prev;
|
|
data = list->current->data;
|
|
|
|
if (prev)
|
|
prev->next = next;
|
|
|
|
if (next)
|
|
next->prev = prev;
|
|
|
|
list->current->prev = NULL;
|
|
list->current->next = NULL;
|
|
// This should not free things; the user should do it explicitly.
|
|
//if(list->current->data) free(list->current->data);
|
|
list->current->data = NULL;
|
|
|
|
free(list->current);
|
|
|
|
switch (whereto) {
|
|
case FIRST: list->current = list->head.next;
|
|
break;
|
|
case LAST: list->current = list->tail.prev;
|
|
break;
|
|
case PREV: list->current = prev;
|
|
break;
|
|
default:
|
|
case NEXT: list->current = next;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
/** Remove a specific node from the list.
|
|
* Find a node by a pointer to its data and remove it.
|
|
* Set the list's \c current pointer to the one denoted by \c whereto.
|
|
* \param list List object.
|
|
* \param data Pointer to data of node to delete.
|
|
* \param whereto Direction where to set the list's \c current pointer
|
|
* \return Pointer to data of deleted node; \c NULL on error.
|
|
*/
|
|
void *
|
|
LL_Remove(LinkedList *list, void *data, Direction whereto)
|
|
{
|
|
if (!list)
|
|
return NULL;
|
|
|
|
LL_Rewind(list);
|
|
do {
|
|
void *find = LL_Get(list);
|
|
|
|
if (find == data)
|
|
return LL_DeleteNode(list, whereto);
|
|
} while (LL_Next(list) == 0);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/** Add/append a new node after the last one in the list.
|
|
* Jump to the last node in the list, append a new node
|
|
* and make this new one the list's \c current one.
|
|
* \param list List object.
|
|
* \param add Pointer to new node's data.
|
|
* \retval <0 error
|
|
* \retval 0 success
|
|
*/
|
|
int
|
|
LL_Push(LinkedList *list, void *add) // Add node to end of list
|
|
{
|
|
if (!list)
|
|
return -1;
|
|
if (!add)
|
|
return -1;
|
|
|
|
LL_End(list);
|
|
|
|
return LL_AddNode(list, add);
|
|
}
|
|
|
|
|
|
/** Remove the last node from the list, and return its data.
|
|
* Jump to the last node in the list, remove it from the list
|
|
* and return its data.
|
|
* \param list List object.
|
|
* \return Pointer to data of deleted node; \c NULL on error.
|
|
*/
|
|
void *
|
|
LL_Pop(LinkedList *list) // Remove node from end of list
|
|
{
|
|
if (!list)
|
|
return NULL;
|
|
|
|
if (0 > LL_End(list))
|
|
return NULL;
|
|
|
|
return LL_DeleteNode(list, PREV);
|
|
}
|
|
|
|
|
|
/** Access list's last node's data.
|
|
* Set list's \c current pointer to the last node and return its data.
|
|
* \param list List object.
|
|
* \return Pointer to last node's data; \c NULL on error.
|
|
*/
|
|
void *
|
|
LL_Top(LinkedList *list) // Peek at end node
|
|
{
|
|
return LL_GetLast(list);
|
|
}
|
|
|
|
|
|
/** Remove the first node from the list, and return its data.
|
|
* Jump to the first node in the list, remove it from the list and return its data.
|
|
* \param list List object.
|
|
* \return Pointer to data of deleted node; \c NULL on error.
|
|
*/
|
|
void *
|
|
LL_Shift(LinkedList *list) // Remove node from start of list
|
|
{
|
|
if (!list)
|
|
return NULL;
|
|
|
|
if (0 > LL_Rewind(list))
|
|
return NULL;
|
|
|
|
return LL_DeleteNode(list, NEXT);
|
|
}
|
|
|
|
|
|
/** Access list's first node's data.
|
|
* Set list's \c current pointer to the first node and return its data.
|
|
* \param list List object.
|
|
* \return Pointer to first node's data; \c NULL on error.
|
|
*/
|
|
void *
|
|
LL_Look(LinkedList *list) // Peek at first node
|
|
{
|
|
return LL_GetFirst(list);
|
|
}
|
|
|
|
|
|
/** Add/insert a new node before the first one in the list.
|
|
* Jump to the first node in the list and insert a new node before that one.
|
|
* \param list List object.
|
|
* \param add Pointer to new node's data.
|
|
* \retval <0 error
|
|
* \retval 0 success
|
|
*/
|
|
int
|
|
LL_Unshift(LinkedList *list, void *add) // Add node to beginning of list
|
|
{
|
|
if (!list)
|
|
return -1;
|
|
if (!add)
|
|
return -1;
|
|
|
|
LL_Rewind(list);
|
|
|
|
return LL_InsertNode(list, add);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Add an item to the end of its "priority group"
|
|
// The list is assumed to be sorted already...
|
|
int
|
|
LL_PriorityEnqueue(LinkedList *list, void *add, int (*compare)(void *, void *))
|
|
{
|
|
if (!list)
|
|
return -1;
|
|
if (!add)
|
|
return -1;
|
|
if (!compare)
|
|
return -1;
|
|
|
|
// From the end of the list, keep searching while we're "less than"
|
|
// the given nodes...
|
|
LL_End(list);
|
|
do {
|
|
void *data = LL_Get(list);
|
|
|
|
if (data) {
|
|
int i = compare(add, data);
|
|
|
|
if (i >= 0) { // If we're in the right place, add it and exit
|
|
LL_AddNode(list, add);
|
|
return 0;
|
|
}
|
|
}
|
|
} while (LL_Prev(list) == 0);
|
|
|
|
// If we're less than *everything*, put it at the beginning
|
|
LL_Unshift(list, add);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
int
|
|
LL_SwapNodes(LL_node *one, LL_node *two) // Switch two nodes positions...
|
|
{
|
|
LL_node *firstprev, *firstnext;
|
|
LL_node *secondprev, *secondnext;
|
|
|
|
if (!one || !two)
|
|
return -1;
|
|
if (one == two)
|
|
return 0; // Do nothing
|
|
|
|
firstprev = one->prev; // Look up the nodes neighbors...
|
|
firstnext = one->next;
|
|
secondprev = two->prev;
|
|
secondnext = two->next;
|
|
|
|
if (firstprev != NULL)
|
|
firstprev->next = two; // Swap the neighboring
|
|
if (firstnext != NULL)
|
|
firstnext->prev = two; // nodes pointers...
|
|
if (secondprev != NULL)
|
|
secondprev->next = one;
|
|
if (secondprev != NULL)
|
|
secondnext->prev = one;
|
|
|
|
one->next = secondnext; // Swap the nodes pointers
|
|
one->prev = secondprev;
|
|
two->next = firstnext;
|
|
two->prev = firstprev;
|
|
|
|
if (firstnext == two)
|
|
one->prev = two; // Fix things in case
|
|
if (firstprev == two)
|
|
one->next = two; // they were next to
|
|
if (secondprev == one)
|
|
two->next = one; // each other...
|
|
if (secondnext == one)
|
|
two->prev = one;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Calculate the length of a list.
|
|
* \param list List object.
|
|
* \return Number of nodes in the list; \c -1 on error.
|
|
*/
|
|
int
|
|
LL_Length(LinkedList *list)
|
|
{
|
|
LL_node *node;
|
|
int num = 0;
|
|
|
|
if (!list)
|
|
return -1;
|
|
|
|
node = &list->head;
|
|
|
|
for (num = -1; node != &list->tail; num++)
|
|
node = node->next;
|
|
|
|
return num;
|
|
}
|
|
|
|
|
|
/** Find a node by giving a comparison function and a value.
|
|
* Go to to the list node whose data matches the given value
|
|
* and return the data.
|
|
*
|
|
* \note
|
|
* This does \em not rewind the list first!
|
|
* Do it yourself if you want to start from the beginning!
|
|
*
|
|
* \param list List object.
|
|
* \param compare Pointer to a comparison function, that takes to void pointers
|
|
* as arguments and returns an int. If must return \c 0 exactly
|
|
* when the node's data matches \c value.
|
|
* \param value Pointer to the value used for matching.
|
|
* \return The found node's data pointer; \c NULL otherwise
|
|
*/
|
|
void *
|
|
LL_Find(LinkedList *list, int (*compare)(void *, void *), void *value)
|
|
{
|
|
if (!list)
|
|
return NULL;
|
|
if (!compare)
|
|
return NULL;
|
|
if (!value)
|
|
return NULL;
|
|
|
|
do {
|
|
void *data = LL_Get(list);
|
|
|
|
if (0 == compare(data, value))
|
|
return data;
|
|
} while (LL_Next(list) == 0);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/** Go to the n-th node in the list and return its data.
|
|
* Go to to the list node with the given \c index
|
|
* and return the data.
|
|
* \param list List object.
|
|
* \param index Index of the node whose data we want.
|
|
* \return The found node's data pointer; \c NULL otherwise
|
|
*/
|
|
void *
|
|
LL_GetByIndex(LinkedList *list, int index)
|
|
{
|
|
LL_node *node;
|
|
int num = 0;
|
|
|
|
if (!list)
|
|
return NULL;
|
|
if (index < 0)
|
|
return NULL;
|
|
|
|
for (node = list->head.next; node != &list->tail; node = node->next) {
|
|
if (num == index)
|
|
return node->data;
|
|
num++;
|
|
}
|
|
|
|
return NULL; // got past the end
|
|
}
|
|
|
|
|
|
/** Sort list by its contents.
|
|
* The list gets sorted using a comparison function for the data of its nodes.
|
|
* After the sorting, the list's current pointer is set to the first node.
|
|
* \param list List object.
|
|
* \param compare Pointer to a comparison function, that takes to void pointers
|
|
* as arguments and returns an int > \c 0 when the first argument
|
|
* is considered greater than the second.
|
|
* \retval <0 error
|
|
* \retval 0 success.
|
|
*/
|
|
int
|
|
LL_Sort(LinkedList *list, int (*compare)(void *, void *))
|
|
{
|
|
int i, j; // Junk / loop variables
|
|
int numnodes; // number of nodes in list
|
|
LL_node *best, *last; // best match and last node in the list
|
|
LL_node *current;
|
|
|
|
if (!list)
|
|
return -1;
|
|
if (!compare)
|
|
return -1;
|
|
|
|
numnodes = LL_Length(list); // get the number of nodes...
|
|
if (0 > LL_End(list))
|
|
return -1; // Find the last node.
|
|
last = LL_GetNode(list);
|
|
|
|
if (numnodes < 2)
|
|
return 0;
|
|
|
|
for (i = numnodes - 1; i > 0; i--) {
|
|
LL_Rewind(list); // get the first node again
|
|
best = last; // reset our "best" node
|
|
|
|
for (j = 0; j < i; j++) {
|
|
current = LL_GetNode(list);
|
|
// If we found a better match...
|
|
if (compare(current->data, best->data) > 0) {
|
|
best = current; // keep track of the "best" match
|
|
}
|
|
LL_Next(list); // Go to the next node.
|
|
}
|
|
|
|
LL_SwapNodes(last, best); // Switch two nodes...
|
|
if (best)
|
|
last = best->prev;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
LL_Rewind(list);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
LL_dprint(LinkedList *list)
|
|
{
|
|
LL_node *current;
|
|
|
|
current = &list->head;
|
|
|
|
printf("Head: prev:\t0x%p\taddr:\t0x%p\tnext:\t0x%p\n", list->head.prev, &list->head, list->head.next);
|
|
|
|
for (current = current->next; current != &list->tail; current = current->next) {
|
|
printf("node: prev:\t0x%p\taddr:\t0x%p\tnext:\t0x%p\n", current->prev, current, current->next);
|
|
}
|
|
|
|
printf("Tail: prev:\t0x%p\taddr:\t0x%p\tnext:\t0x%p\n", list->tail.prev, &list->tail, list->tail.next);
|
|
}
|