mirror of https://github.com/n1nj4sec/pupy.git
422 lines
9.0 KiB
C
422 lines
9.0 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;
|
|
}
|