pupy/client/sources-linux/list.c

422 lines
8.1 KiB
C

/*!
* @file list.c
* @brief Definitions for functions that operate on lists.
* @details An implementation of a simple thread safe double linked list structure. Can be used as either
* a stack (via pop/push), a queue (via push/shift) or an array (via get/add/insert/remove). If
* performing a group of actions on a list based on results from list actions, acquire the list
* lock before the group of actions and release lock when done.
*/
//#include "common.h"
#include <stdlib.h>
#include <stdio.h>
#include "list.h"
/*!
* @brief Create a thread-safe double linked list.
* @returns A new instance of a linked list.
* @retval NULL Indicates a memory allocation failure.
*/
PLIST list_create(void)
{
PLIST pList = (PLIST)malloc(sizeof(LIST));
if (pList != NULL)
{
pList->start = NULL;
pList->end = NULL;
pList->count = 0;
pthread_mutex_init(&pList->lock, NULL);
}
return pList;
}
/*!
* @brief Destroy an existing linked list.
* @details This destroys all nodes and the list itself but not the data held in the
* linked list. This is the responsibility of the caller to destroy.
* @param list The \c LIST instance to destroy.
*/
void list_destroy(PLIST pList)
{
PNODE current_node;
PNODE next_node;
if (pList != NULL)
{
pthread_mutex_lock(&pList->lock);
current_node = pList->start;
while (current_node != NULL)
{
next_node = current_node->next;
current_node->next = NULL;
current_node->prev = NULL;
free(current_node);
current_node = next_node;
}
pList->count = 0;
pthread_mutex_unlock(&pList->lock);
pthread_mutex_destroy(&pList->lock);
free(pList);
}
}
/*!
* @brief Get the number of items in the list.
* @param pList The \c LIST to get a count of.
* @returns The number of elements in the list.
* @remark If using this coung value to itterate through the list with `list_get`, acquire
* the lists lock before the `list_count/list_get` block and release it afterwards.
*/
unsigned int list_count(PLIST pList)
{
unsigned int count = 0;
if (pList != NULL)
{
pthread_mutex_lock(&pList->lock);
count = pList->count;
pthread_mutex_unlock(&pList->lock);
}
return count;
}
/*!
* @brief Get the data value held in the list and a specified index.
* @param pList Pointer to the \c LIST to get the element from.
* @param index Index of the element to get;
* @returns Pointer to the item in the list.
* @retval NULL Indicates the element doesn't exist in the list.
* @remark This will perform a linear search from the beginning of the list.
*/
void * list_get(PLIST pList, unsigned int index)
{
void * data = NULL;
PNODE current_node = NULL;
if (pList == NULL)
return NULL;
pthread_mutex_lock(&pList->lock);
if (pList->count <= index)
{
pthread_mutex_unlock(&pList->lock);
return NULL;
}
current_node = pList->start;
while (current_node != NULL)
{
if (index == 0)
{
break;
}
current_node = current_node->next;
index--;
}
if (current_node != NULL)
{
data = current_node->data;
}
pthread_mutex_unlock(&pList->lock);
return data;
}
/*!
* @brief Add a data item onto the end of the list.
* @param pList Pointer to the \c LIST to add the item to.
* @param data The data that is to be added to the list.
* @returns Indication of success or failure.
* @sa list_push
*/
bool list_add(PLIST pList, void * data)
{
return list_push(pList, data);
}
/*!
* @brief Internal function to remove a node from a list.
* @param pList Pointer to the \c LIST containing \c node.
* @param pNode Pointer to the \c NOTE to remove.
* @returns Indication of success or failure.
* @remark Assumes caller has aquired the appropriate lock first.
*/
bool list_remove_node(PLIST pList, PNODE pNode)
{
if (pList == NULL || pNode == NULL)
{
return false;
}
if (pList->count - 1 == 0)
{
pList->start = NULL;
pList->end = NULL;
}
else
{
if (pList->start == pNode)
{
pList->start = pList->start->next;
pList->start->prev = NULL;
}
else if (pList->end == pNode)
{
pList->end = pList->end->prev;
pList->end->next = NULL;
}
else
{
pNode->next->prev = pNode->prev;
pNode->prev->next = pNode->next;
}
}
pList->count -= 1;
pNode->next = NULL;
pNode->prev = NULL;
free(pNode);
return true;
}
/*!
* @brief Remove a given data item from the list.
* @param pList Pointer to the \c LIST to remove the item from.
* @param data The data that is to be removed from the list.
* @remark Assumes data items are unqique as only the first occurrence is removed.
* @returns Indication of success or failure.
* @sa list_remove_node
*/
bool list_remove(PLIST pList, void * data)
{
bool result = false;
PNODE current_node = NULL;
if (pList == NULL || data == NULL)
{
return false;
}
pthread_mutex_lock(&pList->lock);
current_node = pList->start;
while (current_node != NULL)
{
if (current_node->data == data)
{
break;
}
current_node = current_node->next;
}
result = list_remove_node(pList, current_node);
pthread_mutex_unlock(&pList->lock);
return result;
}
/*!
* @brief Remove a list item at the specified index.
* @param pList Pointer to the \c LIST to remove the item from.
* @param index Index of the item to remove.
* @returns Indication of success or failure.
*/
bool list_delete(PLIST pList, unsigned int index)
{
bool result = false;
void * data = NULL;
PNODE current_node = NULL;
if (pList == NULL)
{
return false;
}
pthread_mutex_lock(&pList->lock);
if (pList->count > index)
{
current_node = pList->start;
while (current_node != NULL)
{
if (index == 0)
{
result = list_remove_node(pList, current_node);
break;
}
current_node = current_node->next;
index--;
}
}
pthread_mutex_unlock(&pList->lock);
return result;
}
/*!
* @brief Push a data item onto the end of the list.
* @param pList Pointer to the \c LIST to append the data to.
* @param data Pointer to the data to append.
* @returns Indication of success or failure.
*/
bool list_push(PLIST pList, void * data)
{
PNODE pNode = NULL;
if (pList == NULL)
return false;
pNode = (PNODE)malloc(sizeof(NODE));
if (pNode == NULL)
{
return false;
}
pNode->data = data;
pNode->next = NULL;
pNode->prev = NULL;
pthread_mutex_lock(&pList->lock);
if (pList->end != NULL)
{
pList->end->next = pNode;
pNode->prev = pList->end;
pList->end = pNode;
}
else
{
pList->start = pNode;
pList->end = pNode;
}
pList->count += 1;
pthread_mutex_unlock(&pList->lock);
return true;
}
/*!
* @brief Pop a data value off the end of the list.
* @param pList Pointer to the \c LIST to pop the value from.
* @returns The popped value.
* @retval NULL Indicates no data in the list.
*/
void * list_pop(PLIST pList)
{
void * data = NULL;
if (pList == NULL)
{
return NULL;
}
pthread_mutex_lock(&pList->lock);
if (pList->end != NULL)
{
data = pList->end->data;
list_remove_node(pList, pList->end);
}
pthread_mutex_unlock(&pList->lock);
return data;
}
/*!
* @brief Pop a data value off the start of the list.
* @param pList Pointer to the \c LIST to shift the value from.
* @returns The shifted value.
* @retval NULL Indicates no data in the list.
*/
void * list_shift(PLIST pList)
{
void * data = NULL;
if (pList == NULL)
{
return NULL;
}
pthread_mutex_lock(&pList->lock);
if (pList->start != NULL)
{
data = pList->start->data;
list_remove_node(pList, pList->start);
}
pthread_mutex_unlock(&pList->lock);
return data;
}
/*!
* @brief Iterate over the list and call a function callback on each element.
* @param pList Pointer to the \c LIST to enumerate.
* @param pCallback Callback function to invoke for each element in the list.
* @param pState Pointer to the state to pass with each function call.
*/
bool list_enumerate(PLIST pList, PLISTENUMCALLBACK pCallback, void * pState)
{
PNODE pCurrent;
bool bResult;
if (pList == NULL || pCallback == NULL)
{
return false;
}
pthread_mutex_lock(&pList->lock);
pCurrent=pList->start;
bResult = false;
while (pCurrent != NULL)
{
bResult = pCallback(pState, pCurrent->data) || bResult;
pCurrent = pCurrent->next;
}
pthread_mutex_unlock(&pList->lock);
return bResult;
}