/*
* LibAxl: Another XML library
* Copyright (C) 2006 Advanced Software Production Line, S.L.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*
* You may find a copy of the license under this software is released
* at COPYING file. This is LGPL software: you are welcome to
* develop proprietary applications using this library without any
* royalty or fee but returning back any change, improvement or
* addition in the form of source code, project image, documentation
* patches, etc.
*
* For commercial support on build XML enabled solutions contact us:
*
* Postal address:
* Advanced Software Production Line, S.L.
* Edificio Alius A, Oficina 102,
* C/ Antonio Suarez Nº 10,
* Alcalá de Henares 28802 Madrid
* Spain
*
* Email address:
* info@aspl.es - http://www.aspl.es/xml
*/
#include <axl_decl.h>
#include <axl_list.h>
#include <axl_log.h>
#include <axl_stream.h>
#define LOG_DOMAIN "axl-list"
typedef struct _axlListNode axlListNode;
struct _axlList {
/* functions used by the list to properly order elements
* inside it and how they are destroyed */
axlEqualFunc are_equal;
axlDestroyFunc destroy_data;
/* pointers to the list content */
axlListNode * first_node;
axlListNode * last_node;
/* list length */
int length;
/* memory management functions */
axlListNode ** preallocated;
int available;
int allocated;
};
struct _axlListNode {
axlListNode * previous;
axlListNode * next;
axlPointer data;
};
struct _axlListCursor {
axlList * list;
axlListNode * current;
};
/**
* \defgroup axl_list_module Axl List: A configurable double-linked list used across the library.
*/
/**
* \addtogroup axl_list_module
* @{
*/
/**
* @internal
*
* Internal are equal handler implementation for the axl list module
* that always return 0.
*/
int __axl_list_always_true (axlPointer a, axlPointer b)
{
return 0;
}
/**
* @internal
*
* Preallocates nodes to be used to store data on the lists.
*
* @param list The list where the preallocation operation will be
* performed.
*/
void __axl_list_allocate_nodes (axlList * list)
{
int iterator;
axlListNode ** temp;
int available;
list->available = 1;
list->allocated += list->available;
/* allocate enough memory to hold all nodes already
* allocated */
if (list->preallocated == NULL) {
list->preallocated = axl_new (axlListNode *, list->allocated);
if (list->preallocated == NULL) {
/* reduce noddes available */
list->available = 0;
list->allocated--;
return;
}
} else {
/* realloc for the list */
temp = list->preallocated;
list->preallocated = realloc (list->preallocated, (sizeof (axlListNode *)) * list->allocated);
/* check alloc operation */
if (list->preallocated == NULL) {
/* alloc failed, restore to previous pointer */
list->preallocated = temp;
/* reduce noddes available */
list->available = 0;
list->allocated--;
return;
}
} /* end */
/* reached this point, alloc operation worked */
/* allocate a node for each available position */
available = list->available;
for (iterator = 0; iterator < list->available; iterator++) {
/* alloc node */
list->preallocated[iterator] = axl_new (axlListNode, 1);
/* check alloc operation */
if (list->preallocated[iterator] == NULL) {
available--;
} /* end if */
} /* end if */
/* update list of available nodes */
list->available = available;
return;
}
/**
* @internal
*
* Disposes the node provided, reusing on the next operation
* requested.
*/
void __axl_list_dispose_node (axlList * list, axlListNode * node)
{
/* store the node to be usable again */
list->preallocated [list->available] = node;
/* increase current available nodes */
list->available++;
return;
}
/**
* @internal
*
* Returns a reference to an list node to be used.
*/
axlListNode * __axl_list_get_next_node_available (axlList * list)
{
axlListNode * node;
/* check that there are nodes available */
if (list->available == 0) {
/* alloc nodes */
__axl_list_allocate_nodes (list);
/* check if there are available nodes */
if (list->available == 0)
return NULL;
} /* end if */
/* get the next node available */
node = list->preallocated[list->available - 1];
list->available--;
/* clean node */
node->next = NULL;
node->previous = NULL;
node->data = NULL;
return node;
}
/**
* @brief Creates a new \ref axlList, using provided handlers.
*
* An \ref axlList is a double linked list, with the hability to
* recognize elements inside its list by providing the \ref
* axlEqualFunc "are_equal" function and the ability to release
* memory allocated by the data stored by providing a \ref
* axlDestroyFunc "destroy_data" handler.
*
* The destroy handler is a optional value. If not provided, any
* automatic deallocation function will not provided.
*
* The "are equal" handler is not optinal. You must provide it to make
* the list work. In the case you don't like a ordering feature you
* can provide an are equal that just return 0 when elements are equal
* and always -1 for the rest of the cases if element should be
* appended or 1 if the element should be prepended.
*
* The list is not prepared to be managed at the same type by several
* threads. If the list is created and then used by several threads it
* will work properly. However, if several threads add and removes
* elements at the same time rainy days will come and you'll get funny
* behaviours.
*
* To create the list you must provide two function that performs some
* minimal functions required by the list o properly order the data
* inside the list and how this data is deallocated (this is
* optional).
*
* For example, to create a list that will hold strings dinamically
* allocated you can use:
* \code
* axlList * list = axl_list_new (axl_list_equal_string, axl_free);
* \endcode
*
* For a list that will holds integer values you can use: \ref
* axl_list_equal_int.
*
* Previous list will cause all strings inside to be deallocated once
* called to \ref axl_list_free. If you don't like this, do not
* provide the deallocation function.
*
* You can always use the following function to make the list to allways
* add all data at the end of the list: \ref axl_list_always_return_1,
* which, as its names indicates, allways return 1, causing the item
* to be added at the end of the list. See \ref axlEqualFunc
* documentation to know more about creating ordenation functions.
*
* Now you have your list created, you can use the following functions
* to add items:
*
* - \ref axl_list_add
* - \ref axl_list_add_at
* - \ref axl_list_prepend
* - \ref axl_list_append
*
* Once you have inserted some data, you can use the following piece
* of code to perform an iteration:
*
* \code
* int iterator = 0;
* while (iterator < axl_list_length (list)) {
* // get the data at the given position
* data = axl_list_get_nth (list, iterator);
*
* // update the iterator
* iterator++;
* }
* \endcode
*
* However, it is preferred to use the \ref axlListCursor, which is
* far more efficient. See the following function to get more
* information: \ref axl_list_cursor_new.
*
* In general, if you are going to perform a lookup for a single item
* you can use \ref axl_list_lookup (by providing a handler) or \ref
* axl_list_get_nth if you know the position.
*
*
* @param are_equal The equal function to be used by the list to find
* and order elements inside the list.
*
* @param destroy_data An optional handler to destroy nodes in the
* case the list is unrefered.
*
* @return A newly allocated list, that must be destroy by calling to
* \ref axl_list_free.
*/
axlList * axl_list_new (axlEqualFunc are_equal, axlDestroyFunc destroy_data)
{
axlList * result;
axl_return_val_if_fail (are_equal, NULL);
/* alloc node */
result = axl_new (axlList, 1);
/* check alloc operation */
if (result == NULL)
return NULL;
result->are_equal = are_equal;
result->destroy_data = destroy_data;
/* preallocate memory for nodes */
/* __axl_list_allocate_nodes (result); */
return result;
}
/**
* @brief Allows to reconfigure the destroy function to be used on
* this list. This function can be useful to disable the destroy
* function associated to the list.
*
* @param list The list to reconfigure or disable (NULL) the destroy function.
*
* @param destroy_data The reference to the destroy function to be
* used or NULL to disable it.
*/
void axl_list_set_destroy_func (axlList * list, axlDestroyFunc destroy_data)
{
axl_return_if_fail (list);
list->destroy_data = destroy_data;
return;
}
/**
* @brief Allows to copy the provided list, returning a newly
* allocated structure.
*
* The copy process can also perform a deep copy for all data stored
* inside the \ref axlList. However, for this purpose it is required
* to provide a duplication function that is able to implement the
* duplication operation over the data received.
*
* This handler is optional, and in the case it is not provided, the
* list returned will be a copy having reference to the content
* stored.
*
* NOTE: the function also copy the destroy and equal function
* configured at \ref axl_list_new. If you want to disable destroy
* function on the copy returned you must use \ref
* axl_list_set_destroy_func, passing NULL as destroy handler.
*
* @param list The list to copy.
* @param func The duplication function used to perform a deep copy (optional handler).
*
* @return A newly allocated \ref axlList with the same configuration
* as the one provided.
*/
axlList * axl_list_copy (axlList * list, axlDuplicateFunc func)
{
axlList * result;
int iterator;
axlPointer data;
axl_return_val_if_fail (list, NULL);
/* create a new axl list */
result = axl_list_new (list->are_equal, list->destroy_data);
/* if the duplicate function is NULL, remove the destroy
* function */
if (func == NULL)
result->destroy_data = NULL;
/* add all elements */
iterator = 0;
while (iterator < axl_list_length (list)) {
/* get data pointer from the list */
data = axl_list_get_nth (list, iterator);
/* try to make a copy */
if (func != NULL)
data = func (data);
/* add the data to the new list */
axl_list_add (result, data);
/* update index */
iterator++;
}
return result;
}
/**
* @brief Equal function that is prepared to receive two strings a
* return if they are equal.
*
* This function is provided as a convenience implementation for the
* \ref axl_list_new function, allowing to store strings inside the
* given list.
*
* The function will return 0 if the string are equal and 1 if
* not. This will cause strings to be append at the end of the list.
*
* @param a The first string to compare.
* @param b The second string to compare.
*/
int axl_list_equal_string (axlPointer a, axlPointer b)
{
int length;
axl_return_val_if_fail (a, 1);
axl_return_val_if_fail (b, 1);
/* get length */
length = strlen (a);
/* check first that both strings have the same length */
if (length != strlen (b))
return 1;
if (axl_stream_cmp (a, b, length))
return 0;
/* differs */
return 1;
}
/**
* @brief Works like \ref axl_list_equal_string but ordering strings
* stored.
*
* @param a The first string to compare.
* @param b The second string to compare.
*
* @return 0 if both are equal, -1 if a shold go before b and 1 if a
* should go after b.
*/
int axl_list_order_string (axlPointer a, axlPointer b)
{
int value;
value = strcmp (a, b);
if (value == 0)
return value;
if (value > 0)
return -1;
return 1;
}
/**
* @brief Equal function that is prepared to receive to integers and
* return if they are equal.
*
* It is assumed that integers are stored in the list using the
* following:
* \code
* axl_list_add (list, INT_TO_PTR (integer));
* \endcode
*
* You can use this function to create an \ref axlList that stores
* integer values as follows:
* \code
* list = axl_list_new (axl_list_equal_int, NULL);
* \endcode
*
* The list created with this function will order data from litter to
* greater values.
*
*
* @param a A reference to the first integer.
* @param b A reference to the second integer.
*
* @return 0 if values received are equal, and 1 if b is greater than
* b. Otherwise -1 is returned.
*/
int axl_list_equal_int (axlPointer a, axlPointer b)
{
/* especial case where a 0 is stored */
if (PTR_TO_INT (a) == PTR_TO_INT (b))
return 0;
else if (PTR_TO_INT (a) < PTR_TO_INT (b))
return -1;
else
return 1;
}
/**
* @brief An equal function that could be used to make elements to be
* stored inside an \ref axlList at the end as the are added, without
* replacing any previously added item.
*
*
* If it is needed to create a list that stores elements at the end as
* they are added, this function could be used as the \ref
* axlEqualFunc, while calling to axl_list_new.
*
* @param a First item.
* @param b Second item.
*
* @return The function always return 1.
*
* NOTE: If you use this function and your intention is to remove
* items (without calling to axl_list_free) you must use \ref
* axl_list_remove_ptr or \ref axl_list_unlink_ptr since \ref
* axl_list_remove and \ref axl_list_unlink relays on the equal
* function to find and remove the item. Because this function never
* return 0, the item is never removed.
*/
int axl_list_always_return_1 (axlPointer a, axlPointer b)
{
return 1;
}
/**
* @brief Allows to store a new element inside the list, using the
* provided data.
*
* The function will fail to perform any action if a null reference is
* provided to the function.
*
* @param list The list where the element will be added.
*
* @param pointer The pointer to store.
*
* NOTE: The function uses the equal function defined at \ref
* axl_list_new. If the function shows that the item to be added is
* already added (because the equal function returns 0, then the item
* won't be added.
*
*/
void axl_list_add (axlList * list, axlPointer pointer)
{
axlListNode * new_node;
axlListNode * node;
axlListNode * node2;
/* perform some environment checkings */
axl_return_if_fail (list);
new_node = __axl_list_get_next_node_available (list);
/* check returned node */
if (new_node == NULL)
return;
new_node->data = pointer;
/* check basic case */
if (list->first_node == NULL) {
list->first_node = new_node;
list->last_node = new_node;
list->length = 1;
return;
}
/* complex case */
node = list->first_node;
node2 = list->last_node;
/* lookup */
while ((node != NULL) || (node2 != NULL)) {
/* lookup the head */
if (node != NULL) {
switch (list->are_equal (node->data, pointer)) {
case -1:
/* the node should be added before node */
new_node->next = node;
new_node->previous = node->previous;
node->previous = new_node;
/* if new previous is null do not update it */
if (new_node->previous != NULL)
new_node->previous->next = new_node;
else
list->first_node = new_node;
/* update list length */
list->length ++;
return;
case 0:
/* the node found is equal, do not perform any operation */
return;
case 1:
default:
/* the node should be added after */
node = node->next;
break;
}
} /* end if */
/* lookup from the tail */
if (node2 != NULL) {
switch (list->are_equal (node2->data, pointer)) {
case -1:
default:
/* the node should be added before node */
node2 = node2->previous;
break;
case 0:
/* the node found is equal, do not perform any operation */
return;
case 1:
/* the node should be added after */
new_node->previous = node2;
new_node->next = node2->next;
node2->next = new_node;
/* do not update if next is NULL */
if (new_node->next != NULL)
new_node->next->previous = new_node;
else
list->last_node = new_node;
/* update length size */
list->length ++;
return;
}
} /* end if */
} /* end while */
/* nothing more to do */
return;
}
/**
* @brief Allows to adds the provided item to the given list at the
* selected position.
*
* The function will perform an indexed addition, using the value
* <b>position</b>, by-passing current list configuration (\ref
* axl_list_new).
*
* If the position is greater than the length of the list, the item is
* added at the end of the list. If the position is 0, the item is
* added at the begin (equivalent to call \ref axl_list_prepend).
*
* If an item is found at the provided position, the element is added
* before the already found.
*
* @param list The list where the addition operation will be performed.
*
* @param pointer The item to add to the list.
*
* @param position Position where the addition operation will be
* performed. Values allowed ranges from 0 up to list length - 1.
*/
void axl_list_add_at (axlList * list, axlPointer pointer, int position)
{
int iterator;
axlListNode * node;
axlListNode * new_node;
/* check incoming values */
axl_return_if_fail (list);
/* check if we have a prepend operation */
if (position <= 0) {
__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "adding item using prepend");
/* prepend */
axl_list_prepend (list, pointer);
return;
}
/* check if we have an append operation */
if (position >= list->length) {
__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "adding item using append (position=%d >= length=%d)",
position, list->length);
/* append */
axl_list_append (list, pointer);
return;
}
/* allocate a new node */
new_node = __axl_list_get_next_node_available (list);
/* check returned node */
if (new_node == NULL)
return;
new_node->data = pointer;
__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "looking node position: %d", position);
/* basic case isn't reached here (remove first and last
* cases) */
iterator = 1;
node = list->first_node->next;
while (iterator < position) {
/* get the next element */
node = node->next;
/* update the iterator */
iterator++;
}
__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "adding item at: %d", iterator);
/* add the element */
new_node->previous = node->previous;
if (node->previous != NULL)
node->previous->next = new_node;
new_node->next = node;
node->previous = new_node;
/* update number of items inside */
list->length++;
return;
}
/**
* @brief Allows to add a node to the provided list, at the first
* position, without taking into consideration current \ref axlList
* configuration (\ref axlEqualFunc at \ref axl_list_new).
*
* @param list The list where the data will be added at the first
* position.
*
* @param pointer The pointer to add.
*/
void axl_list_prepend (axlList * list, axlPointer pointer)
{
axlListNode * new_node;
axl_return_if_fail (list);
/* simulate adding the node at the first position */
new_node = __axl_list_get_next_node_available (list);
/* check returned node */
if (new_node == NULL)
return;
new_node->data = pointer;
/* make the new node the be the first one */
if (list->first_node == NULL) {
list->first_node = new_node;
list->last_node = new_node;
}else {
list->first_node->previous = new_node;
new_node->next = list->first_node;
list->first_node = new_node;
}
/* update number of items inside */
list->length++;
return;
}
/**
* @brief Allows to add a node to the provided list, at the last
* position, without taking into consideration current \ref axlList
* configuration (\ref axlEqualFunc at \ref axl_list_new).
*
* @param list The list where the data will be added at the last
* position.
*
* @param pointer The pointer to add.
*/
void axl_list_append (axlList * list, axlPointer pointer)
{
axlListNode * new_node;
axl_return_if_fail (list);
/* simulate adding the node at the first position */
new_node = __axl_list_get_next_node_available (list);
/* check allocated node */
if (new_node == NULL)
return;
new_node->data = pointer;
/* make the new node the be the first one */
if (list->last_node == NULL) {
list->first_node = new_node;
list->last_node = new_node;
}else {
list->last_node->next = new_node;
new_node->previous = list->last_node;
list->last_node = new_node;
}
/* update number of items inside */
list->length++;
return;
}
/**
* @internal Internal list lookup using a linear search, checking all
* items inside the list taking into considerations hints provided by
* equal function.
*
* @param list The list where the linear search will be performed.
* @param pointer The pointer that is being looked up.
*
* @return A reference to the internal axl list node containing the
* pointer.
*/
axlListNode * axl_list_internal_linear_lookup (axlList * list,
axlPointer pointer)
{
axlListNode * node;
axl_return_val_if_fail (list, NULL);
/* complex case */
node = list->first_node;
/* lookup */
while (node != NULL) {
if (list->are_equal (node->data, pointer) == 0)
return node;
/* the node should be after this one */
node = node->next;
} /* end while */
return NULL;
}
/**
* @internal Internal list lookup using a linear search, checking all
* items inside the list withtout taking into considerations hints
* provided by equal function (search by pointer).
*
* @param list The list where the linear search will be performed.
* @param pointer The pointer that is being looked up.
*
* @return A reference to the internal axl list node containing the
* pointer.
*/
axlListNode * axl_list_internal_linear_lookup_ptr (axlList * list,
axlPointer pointer)
{
axlListNode * node;
axl_return_val_if_fail (list, NULL);
/* complex case */
node = list->first_node;
/* lookup */
while (node && node->data != pointer) {
/* the node should be after this one */
node = node->next;
} /* end while */
/* return current result */
return node;
}
/**
* @internal
* @brief Internal lookup function to locate the axlListNode that contains the pointer.
*
*
* @param list The list where the lookup will be performed.
* @param pointer The pointer data to lookup.
*
* @return A reference to the \ref axlListNode or NULL if no pointer
* is found.
*/
axlListNode * axl_list_internal_lookup (axlList * list, axlPointer pointer)
{
axlListNode * node;
axlListNode * node2;
axl_return_val_if_fail (list, NULL);
axl_return_val_if_fail (pointer, NULL);
/* complex case */
node = list->first_node;
node2 = list->last_node;
/* lookup */
while ((node != NULL) || (node2 != NULL)) {
/* lookup the head */
if (node != NULL) {
switch (list->are_equal (node->data, pointer)) {
case -1:
default:
/* node should be before the node
* found. So we are not going to find
* a node that is lower, the element
* is not in the list.
*/
return NULL;
case 0:
return node;
case 1:
/* the node should be after this one */
node = node->next;
break;
}
} /* end if */
/* lookup from the tail */
if (node2 != NULL) {
switch (list->are_equal (node2->data, pointer)) {
case -1:
/* the node should be added before node */
node2 = node2->next;
break;
case 0:
return node2;
case 1:
default:
/* it seems that the node should be
* found after this node but this is
* not going to be possible. The node is not in the list.
*/
return NULL;
}
} /* end if */
} /* end while */
return NULL;
}
/**
* @internal
*
* @brief Allows to lookup the pointer stored, inside the provided
* list, on the given position.
*
* @param list The list where the operation will be performed.
* @param position The position to lookup for stored data.
*
* @return A reference or NULL if fails.
*/
axlListNode * axl_list_internal_get_nth (axlList * list, int position)
{
axlListNode * node;
int iterator = 0;
axl_return_val_if_fail (list, NULL);
axl_return_val_if_fail (position >= 0 && position < list->length, NULL);
/* iterate until the node is found */
node = list->first_node;
while (node != NULL && (iterator != position)) {
iterator ++;
node = node->next;
}
/* return data found */
if (iterator == position)
return node;
return NULL;
}
/* remove the selected node */
void __axl_list_common_remove_selected_node (axlList * list, axlListNode * node,
axl_bool alsoRemove)
{
axlPointer pointer;
/* do not remove anything if a null reference is received */
if (node == NULL)
return;
/* get a reference to the pointer */
pointer = node->data;
if (node->previous == NULL)
list->first_node = node->next;
else
node->previous->next = node->next;
if (node->next == NULL)
list->last_node = node->previous;
else
node->next->previous = node->previous;
/* release user space allocated memory
* if defined destroy function */
if (alsoRemove && (list->destroy_data != NULL))
list->destroy_data (pointer);
/* release memory allocated by the node */
__axl_list_dispose_node (list, node);
/* decrease list length */
list->length--;
/* nothing more to do */
return;
}
/**
* @internal
*
* Internal support for axl_list_remove and axl_list_unlink
* function. The function perform a node removing, the one that
* contains the node pointed by the provided pointer, and making a
* node deallocation according to the configuration of the
* <b>alsoRemove</b>.
*
* @param list The list where the operation will be performed.
*
* @param pointer The pointer to remove
*
* @param alsoRemove Also call to destroy function.
*
* @param byPtr Makes the linear search to be done by pointer.
*/
void axl_list_common_remove (axlList * list, axlPointer pointer, axl_bool alsoRemove, axl_bool byPtr)
{
axlListNode * node;
axl_return_if_fail (list);
/* complex case */
if (byPtr)
node = axl_list_internal_linear_lookup_ptr (list, pointer);
else
node = axl_list_internal_linear_lookup (list, pointer);
if (node == NULL) {
__axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "unable to find item by pointer (0x%x)",
pointer);
return;
}
/* remove the selected node */
__axl_list_common_remove_selected_node (list, node, alsoRemove);
return;
}
/**
* @brief Allows to remove the given pointer from the list.
*
* The element referenced by <b>pointer</b> will be removed from the
* list, include the memory allocated if a destroy function were
* provided.
*
* If it is required to remove a pointer from the list, without
* calling to the destroy function, you can use \ref axl_list_unlink.
*
* The function will fail to work if references provided are NULL.
*
* @param list The list where the removal operation will be performed.
* @param pointer The pointer where the
*
* NOTE: The function uses the current equal function configured. A
* not properly configured equal function will make this function to
* not remove the item selected. If you are trying to remove by
* pointer, you can use \ref axl_list_remove_ptr.
*/
void axl_list_remove (axlList * list, axlPointer pointer)
{
/* perform a complete removing */
axl_list_common_remove (list, pointer,
/* alsoRemove */
axl_true,
/* byPtr */
axl_false);
return;
}
/**
* @internal Implementation that removes or unlinks a selected node at
* a particular position.
*/
void __axl_list_remove_at_common (axlList * list, int position, axl_bool remove_node)
{
axlListNode * node;
int iterator = 0;
axl_return_if_fail (list);
/* find the item by position */
node = list->first_node;
/* lookup */
while (node) {
/* return selected node */
if (iterator == position)
break;
/* the node should be after this one */
node = node->next;
/* next iterator */
iterator++;
} /* end while */
if (node) {
/* remove selected node */
__axl_list_common_remove_selected_node (list, node, remove_node);
} /* end if */
return;
}
/**
* @brief Allows to remove a particular item inside the list at a
* selected position.
*
* This function also deallocates the memory used by the node located
* at the particular position. In the case only a removal operation is
* required use \ref axl_list_unlink_at.
*
* @param list The list where the remove operation will take place.
*
* @param position The position where the removal operation will take
* place. Position values ranges from 0 up to (N - 1).
*/
void axl_list_remove_at (axlList * list, int position)
{
/* call to common implementation */
__axl_list_remove_at_common (list, position, axl_true);
return;
}
/**
* @brief Allows to remove the provided pointer from the list (calling
* to the destroy function defined).
*
* Unlike \ref axl_list_remove, which removes the element selected by
* using the equal function configured at \ref axl_list_new, this
* function allows to perform a remove operation by pointer.
*
* @param list The list on which the operation is performed.
*
* @param pointer The pointer to remove from the list.
*/
void axl_list_remove_ptr (axlList * list, axlPointer pointer)
{
/* perform a complete removing */
axl_list_common_remove (list, pointer,
/* alsoRemove */
axl_true,
/* byPtr */
axl_true);
}
/**
* @brief Removes the given pointer from the list, without calling the
* destroy function, even when it is configured.
*
* @param list The list where the operation will be performed.
*
* @param pointer The pointer to remove.
*/
void axl_list_unlink (axlList * list, axlPointer pointer)
{
/* perform a complete removing */
axl_list_common_remove (list, pointer,
/* alsoRemove */
axl_false,
/* byPtr */
axl_false);
return;
}
/**
* @brief Allows to remove the provided item from the axl list
* withtout using the equal function provided (remove by pointer) and
* without calling to the associated destroy function.
*
* @param list The list where the operation will be implemented.
*
* @param pointer the pointer to remove from the list without calling
* to the destroy function.
*/
void axl_list_unlink_ptr (axlList * list, axlPointer pointer)
{
/* perform an unlink operation, without using equal
* function */
/* perform a complete removing */
axl_list_common_remove (list, pointer,
/* alsoRemove */
axl_false,
/* byPtr */
axl_true);
return;
}
/**
* @brief Allows to unlink a particular item inside the list at a
* selected position.
*
* This function DO NOT deallocates the memory used by the node located
* at the particular position. In the case a complete removal operation is
* required use \ref axl_list_remove_at.
*
* @param list The list where the unlink operation will take place.
*
* @param position The position where the unlink operation will take
* place. Position values ranges from 0 up to (N - 1).
*/
void axl_list_unlink_at (axlList * list, int position)
{
/* call to common implementation */
__axl_list_remove_at_common (list, position, axl_false);
return;
}
/**
* @brief Allows to remove the first element, calling to the destroy
* function if defined.
*
* @param list The list where the first element will be removed.
*/
void axl_list_remove_first (axlList * list)
{
axl_return_if_fail (list);
/* do not perform any operation if no node is stored */
if (list->first_node == NULL)
return;
/* remove the selected node */
__axl_list_common_remove_selected_node (list, list->first_node, axl_true);
return;
}
/**
* @brief Allows to remove the first element from the list without
* calling to the destroy function, even with it is defined.
*
* @param list The list where the first element will be removed.
*/
void axl_list_unlink_first (axlList * list)
{
axl_return_if_fail (list);
/* do not perform any operation if no node is stored */
if (list->first_node == NULL)
return;
/* remove the selected node */
__axl_list_common_remove_selected_node (list, list->first_node, axl_false);
return;
}
/**
* @brief Allows to remove the last element, calling to the destroy
* function if defined.
*
* @param list The list where the last element will be removed.
*/
void axl_list_remove_last (axlList * list)
{
axl_return_if_fail (list);
/* do not perform any operation if no node is stored */
if (list->last_node == NULL)
return;
/* remove the selected node */
__axl_list_common_remove_selected_node (list, list->last_node, axl_true);
return;
}
/**
* @brief Allows to remove the last element from the list without
* calling to the destroy function, even with it is defined.
*
* @param list The list where the last element will be removed.
*/
void axl_list_unlink_last (axlList * list)
{
axl_return_if_fail (list);
/* do not perform any operation if no node is stored */
if (list->last_node == NULL)
return;
/* remove the selected node */
__axl_list_common_remove_selected_node (list, list->last_node, axl_false);
return;
}
/**
* @brief Allows to check if the given pointer is stored on the given
* list.
*
* @param list The list where the lookup will be performed.
*
* @param pointer The pointer to lookup.
*
* @return \ref axl_true if the element is stored on the list,
* otherwise axl_false is returned. The function will fail to lookup
* if a NULL reference is received, either the list or the pointer.
*/
axl_bool axl_list_exists (axlList * list, axlPointer pointer)
{
axl_return_val_if_fail (list, axl_false);
axl_return_val_if_fail (pointer, axl_false);
if (axl_list_internal_lookup (list, pointer) != NULL)
return axl_true;
return axl_false;
}
/**
* @brief Allows to check if the provided list is empty (no element
* stored).
*
* @param list The list to check for emptyness.
*
* @return axl_true if the list is empty, axl_false if not. The function
* return axl_false in the case a null reference is provided.
*/
axl_bool axl_list_is_empty (axlList * list)
{
axl_return_val_if_fail (list, axl_false);
/* check if the first node is defined */
return (list->first_node == NULL);
}
/**
* @brief Allows to check if the given pointer is stored on the given position.
*
* @param list The list where the operation will be run.
* @param pointer The pointer to check.
* @param position The position where is expected to find the pointer.
*
* @return axl_true if the given data, referenced by the pointer, is
* stored on the given position.
*/
axl_bool axl_list_exists_at (axlList * list, axlPointer pointer, int position)
{
axlListNode * node;
node = axl_list_internal_get_nth (list, position);
if (node != NULL) {
if (! list->are_equal (node->data, pointer))
return axl_true;
}
return axl_false;
}
/**
* @brief Returns the first element stored on the list.
*
*
* @param list The list where the first element stored will be
* returned.
*
* @return An \ref axlPointer containing the data stored on the list.
*/
axlPointer axl_list_get_first (axlList * list)
{
axl_return_val_if_fail (list, NULL);
if (list->first_node == NULL)
return NULL;
return list->first_node->data;
}
/**
* @brief Returns the last element stored on the list.
*
* @param list The list where the last element will be returned.
*
* @return An \ref axlPointer reference containing the last stored
* value.
*/
axlPointer axl_list_get_last (axlList * list)
{
axl_return_val_if_fail (list, NULL);
if (list->last_node == NULL)
return NULL;
return list->last_node->data;
}
/**
* @brief Allows to get current pointer stored at the given position.
*
* @param list The list where the data will be retrieved.
*
* @param position A value ranging from 0 up to the the list lenght -
* 1.
*
* @return The \ref axlPointer stored on the given position or NULL if
* fail.
*/
axlPointer axl_list_get_nth (axlList * list, int position)
{
axlListNode * node;
node = axl_list_internal_get_nth (list, position);
if (node != NULL)
return node->data;
return NULL;
}
/**
* @brief Allows to perform a linear lookup on the list provided,
* givin a function that is used to now the object to return due to
* the lookup.
*
* The function can also be used as a foreach function. The following
* example shows how to launch the function and perform a tasks on the
* lookup function:
* \code
* // perform the lookup
* return axl_list_lookup (list, __find_item, name);
*
* // the lookup function
* axl_bool __find_item (axlPointer _element, axlPointer data)
* {
* SomeItem * element = _element;
* char * name = data;
*
* // check the name
* if (axl_cmp (element->name, name))
* return axl_true;
*
* // it is not the element
* return axl_false;
* }
* \endcode
*
* In the case you create a list to hold string values, you can use
* \ref axl_list_find_string as lookup function predefined to perform
* the search.
*
* @param list The list where the lookup will be performed.
*
* @param func The function to use to perform the lookup.
*
* @param data User defined data that will be passed to the func provided.
*
* @return A pointer to the object found or NULL if no item was found.
*/
axlPointer axl_list_lookup (axlList * list, axlLookupFunc func, axlPointer data)
{
axlListNode * node;
axl_return_val_if_fail (list, NULL);
axl_return_val_if_fail (func, NULL);
/* get the first pointer */
node = list->first_node;
do {
/* if the next node to check is NULL, terminate the
* lookup. */
if (node == NULL)
return NULL;
/* check if the node found is the one looked up */
if (func (node->data, data))
return node->data;
/* seems not, update to the next reference */
node = node->next;
}while (1);
/* return no node found */
return NULL;
}
/**
* @brief Helper function that could be used at \ref axl_list_lookup if
* the list created only contains strings.
*
* Use this function as a parameter for the lookup function at \ref
* axl_list_lookup.
*
* @param element The element at the list, in this case, an string value.
*
* @param data The data provided at \ref axl_list_lookup, in this
* case, the value we are looking.
*
* @return \ref axl_true if the string was found, \ref axl_false if not.
*/
axl_bool axl_list_find_string (axlPointer element, axlPointer data)
{
/* if the string received is null, just return axl_false */
if (data == NULL)
return axl_false;
/* return the comparison status */
return axl_cmp ((char *) element, (char *) data);
}
/**
* @brief Allows to get current list length.
*
* @param list The list to operate.
*
* @return The list length or -1 if fail (the list reference received
* is null).
*/
int axl_list_length (axlList * list)
{
axl_return_val_if_fail (list, -1);
return list->length;
}
/**
* @brief Allows to destroy the given list, and all user space
* associated memory if a destroy handler was provided.
*
* @param list The list to destroy
*/
void axl_list_free (axlList * list)
{
axlListNode * node;
axlListNode * node2;
int iterator;
/* if a null reference is received do not oper */
if (list == NULL)
return;
node = list->first_node;
while (node != NULL) {
if (list->destroy_data != NULL) {
list->destroy_data (node->data);
}
node2 = node;
node = node->next;
axl_free (node2);
}
/* allocate a node for each available position */
for (iterator = 0; iterator < list->available; iterator++) {
axl_free (list->preallocated[iterator]);
}
/* free the array */
axl_free (list->preallocated);
/* free the list itself */
axl_free (list);
return;
}
/* @} */
/**
* \defgroup axl_list_cursor_module Axl List Cursor: Iterator support for the Axl List
*/
/**
* \addtogroup axl_list_cursor_module
* @{
*/
/**
* @brief Allows to get a cursor to iterate the list in a linear and
* efficient way.
*
* The \ref axlListCursor could be used to iterate an \ref axlList in
* an efficient way because it stores current state (position). Then
* using the following functions you can modify the state (current
* position to get):
*
* - \ref axl_list_cursor_first
* - \ref axl_list_cursor_last
* - \ref axl_list_cursor_next
* - \ref axl_list_cursor_previous
*
* Finally, a function is provided to get the data stored at a
* particular position, pointed by the current status of the cursor:
*
* - \ref axl_list_cursor_get
*
* You are allowed to remove elements from the list (\ref axlList)
* having a cursor created (\ref axlListCursor), using \ref
* axl_list_cursor_unlink.
*
* Here is an example:
* \code
* axlPointer value;
* axlListCursor * cursor;
*
* // create the cursor
* cursor = axl_list_cursor_new (list);
*
* // while there are more elements
* while (axl_list_cursor_has_item (cursor)) {
*
* // get the value
* value = axl_list_cursor_get (cursor);
*
*
* // get the next
* axl_list_cursor_next (cursor);
*
* // update the iterator
* iterator++;
*
* }
*
* // free the cursor
* axl_list_cursor_free (cursor);
* \endcode
*
* @param list The list that the new cursor (\ref axlListCursor) will
* provide access.
*
* @return A newly created \ref axlListCursor used to iterate the
* list. Once finished you must call to \ref axl_list_cursor_free.
*/
axlListCursor * axl_list_cursor_new (axlList * list)
{
axlListCursor * cursor;
axl_return_val_if_fail (list, NULL);
/* create the cursor */
cursor = axl_new (axlListCursor, 1);
/* initial configuration */
cursor->list = list;
cursor->current = list->first_node;
return cursor;
}
/**
* @brief Allows to configure the cursor to point to the first item of
* the list (if there are any).
*
* @param cursor The cursor that is required to be configured to point the first item.
*/
void axl_list_cursor_first (axlListCursor * cursor)
{
axl_return_if_fail (cursor);
if (cursor->list->length == 0) {
cursor->current = NULL;
return;
} /* end if */
/* set the first node */
cursor->current = cursor->list->first_node;
return;
}
/**
* @brief Allows to configure the cursor to point to the last item of
* the list (if there are any).
*
* @param cursor The cursor that is required to be configured to point
* to the last item.
*/
void axl_list_cursor_last (axlListCursor * cursor)
{
axl_return_if_fail (cursor);
/* set the first node */
cursor->current = cursor->list->last_node;
return;
}
/**
* @brief Allows to configure the cursor to point to the next item of
* the list (if there are any).
*
* @param cursor The cursor that is required to be configured to point
* to the next item.
*/
void axl_list_cursor_next (axlListCursor * cursor)
{
axl_return_if_fail (cursor);
/* set the next node */
if (cursor->current != NULL)
cursor->current = cursor->current->next;
return;
}
/**
* @brief Allows to configure the cursor to point to the previous item
* of the list (if there are any).
*
* @param cursor The cursor that is required to be configured to point
* to the previous item.
*/
void axl_list_cursor_previous (axlListCursor * cursor)
{
axl_return_if_fail (cursor);
/* set the next node */
if (cursor->current != NULL)
cursor->current = cursor->current->previous;
return;
}
/**
* @brief Allows to check if there are more elements next to the
* current element pointed by the cursor.
*
* @param cursor The cursor that is required to return if there are
* next items.
*
* @return \ref axl_true if more items are found, otherwise \ref axl_false is
* returned.
*/
axl_bool axl_list_cursor_has_next (axlListCursor * cursor)
{
axl_return_val_if_fail (cursor, axl_false);
/* check for empty list */
if (cursor->current == NULL)
return axl_false;
/* return if the next element isn't null */
return (cursor->current->next != NULL);
}
/**
* @brief Allows to check if there are more elements next to the
* current element pointed by the cursor.
*
* @param cursor The cursor that is required to return if there are
* next items.
*
* @return \ref axl_true if more items are found, otherwise \ref axl_false is
* returned.
*/
axl_bool axl_list_cursor_has_previous (axlListCursor * cursor)
{
axl_return_val_if_fail (cursor, axl_false);
/* check for empty list */
if (cursor->current == NULL)
return axl_false;
/* return if the next element isn't null */
return (cursor->current->previous != NULL);
}
/**
* @brief Allows to know if the current position has items.
*
* @param cursor The cursor that is requested to return if a call to
* \ref axl_list_cursor_get will return data.
*
* @return \ref axl_true if the list that is iterated can return data at
* the current position, otherwise \ref axl_false is returned.
*/
axl_bool axl_list_cursor_has_item (axlListCursor * cursor)
{
axl_return_val_if_fail (cursor, axl_false);
/* return if there are current */
return (cursor->current != NULL);
}
/**
* @brief Allows to remove current element pointed by the cursor,
* maintainig internal state of the cursor.
*
* The function won't call to the destroy function asociated to the
* list. If you want the item stored to be also destroyed call \ref
* axl_list_cursor_remove.
*
* @param cursor The cursor pointing to the item inside the list that
* must be removed.
*/
void axl_list_cursor_unlink (axlListCursor * cursor)
{
axlListNode * node;
axl_return_if_fail (cursor);
/* if current cursor is pointing nowhere, just do nothing */
if (cursor->current == NULL)
return;
/* remember node */
node = cursor->current;
/* configure the cursor to point to the next element (or the previous if the next element is null) */
cursor->current = (node->next != NULL) ? node->next : node->previous;
/* call to unlik */
__axl_list_common_remove_selected_node (cursor->list, node, axl_false);
return;
}
/**
* @brief Allows to remove current element pointed by the cursor,
* maintainig internal state of the cursor, calling to the destroy
* function associated in the list.
*
* The function will call to the destroy function asociated to the
* list. If you don't want the item stored to be also destroyed call \ref
* axl_list_cursor_unlink.
*
* @param cursor The cursor pointing to the item inside the list that
* must be removed.
*/
void axl_list_cursor_remove (axlListCursor * cursor)
{
axlListNode * node;
axl_return_if_fail (cursor);
/* if current cursor is pointing nowhere, just do nothing */
if (cursor->current == NULL)
return;
/* remember node */
node = cursor->current;
/* configure the cursor to point to the next element (or the previous if the next element is null) */
cursor->current = (node->next != NULL) ? node->next : node->previous;
/* call to remove */
__axl_list_common_remove_selected_node (cursor->list, node, axl_true);
return;
}
/**
* @brief Allows to get current data at the current cursor state.
*
* @param cursor The cursor that will be used to return the data
* located at the list, using cursor current state.
*/
axlPointer axl_list_cursor_get (axlListCursor * cursor)
{
axl_return_val_if_fail (cursor, NULL);
/* nothing to return if current is NULL */
if (cursor->current == NULL)
return NULL;
/* return data */
return cursor->current->data;
}
/**
* @brief Allows to get the reference to the list that is associated
* to the cursor received.
*
* @param cursor The cursor that is required to return the list associated.
*
* @return A reference to the list being iterated or NULL if fails.
*/
axlList * axl_list_cursor_list (axlListCursor * cursor)
{
/* check incoming cursor */
axl_return_val_if_fail (cursor, NULL);
/* return the list */
return cursor->list;
}
/**
* @brief Deallocates memory used by the cursor.
*
* @param cursor The cursor to be deallocated.
*/
void axl_list_cursor_free (axlListCursor * cursor)
{
axl_return_if_fail (cursor);
/* free the cursor */
axl_free (cursor);
return;
}
/* @} */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>