#ifdef HAVE_CONFIG_H # include #endif #include #include #include "Ecore_Data.h" /* Some tests showed that beyond that value heap sort is faster than merge sort * (in this implementation). This value has to be changed or at least review * if someone is changing the implementation. */ #define ECORE_MERGESORT_LIMIT 40000 /* Return information about the list */ static void * _ecore_list_current(Ecore_List *list); /* Adding functions */ static int _ecore_list_insert(Ecore_List *list, Ecore_List_Node *node); static int _ecore_list_append_0(Ecore_List *list, Ecore_List_Node *node); static int _ecore_list_prepend_0(Ecore_List *list, Ecore_List_Node *node); /* Remove functions */ static void * _ecore_list_remove_0(Ecore_List *list); static void * _ecore_list_first_remove(Ecore_List *list); static void * _ecore_list_last_remove(Ecore_List *list); /* Basic traversal functions */ static void * _ecore_list_next(Ecore_List *list); static void * _ecore_list_last_goto(Ecore_List *list); static void * _ecore_list_first_goto(Ecore_List *list); static void * _ecore_list_goto(Ecore_List *list, const void *data); static void * _ecore_list_index_goto(Ecore_List *list, int idx); /* Iterative functions */ static int _ecore_list_for_each(Ecore_List *list, Ecore_For_Each function, void *user_data); static void * _ecore_list_find(Ecore_List *list, Ecore_Compare_Cb function, const void *user_data); /* Sorting functions */ static Ecore_List_Node *_ecore_list_node_mergesort(Ecore_List_Node *first, int n, Ecore_Compare_Cb compare, int order); static Ecore_List_Node *_ecore_list_node_merge(Ecore_List_Node *first, Ecore_List_Node *second, Ecore_Compare_Cb compare, int order); static Ecore_List_Node *_ecore_dlist_node_mergesort(Ecore_List_Node *first, int n, Ecore_Compare_Cb compare, int order); static Ecore_List_Node *_ecore_dlist_node_merge(Ecore_List_Node *first, Ecore_List_Node *second, Ecore_Compare_Cb compare, int order); /* Private double linked list functions */ static void *_ecore_dlist_previous(Ecore_DList *list); static void *_ecore_dlist_first_remove(Ecore_DList *list); static void *_ecore_dlist_index_goto(Ecore_DList *list, int idx); /** @defgroup Ecore_Data_List_Creation_Group List Creation/Destruction Functions Functions that create, initialize and destroy Ecore_Lists. */ /** * Create and initialize a new list. * @return A new initialized list on success, @c NULL on failure. * @ingroup Ecore_Data_List_Creation_Group */ EAPI Ecore_List * ecore_list_new(void) { Ecore_List *list; list = (Ecore_List *)malloc(sizeof(Ecore_List)); if (!list) return NULL; if (!ecore_list_init(list)) { FREE(list); return NULL; } return list; } /** * Initialize a list to some sane starting values. * @param list The list to initialize. * @return @c TRUE if successful, @c FALSE if an error occurs. * @ingroup Ecore_Data_List_Creation_Group */ EAPI int ecore_list_init(Ecore_List *list) { CHECK_PARAM_POINTER_RETURN("list", list, FALSE); memset(list, 0, sizeof(Ecore_List)); return TRUE; } /** * Free a list and all of it's nodes. * @param list The list to be freed. * @ingroup Ecore_Data_List_Creation_Group */ EAPI void ecore_list_destroy(Ecore_List *list) { void *data; CHECK_PARAM_POINTER("list", list); while (list->first) { data = _ecore_list_first_remove(list); if (list->free_func) list->free_func(data); } FREE(list); } /** * Set the function for freeing data. * @param list The list that will use this function when nodes are * destroyed. * @param free_func The function that will free the key data. * @return @c TRUE on successful set, @c FALSE otherwise. */ EAPI int ecore_list_free_cb_set(Ecore_List *list, Ecore_Free_Cb free_func) { CHECK_PARAM_POINTER_RETURN("list", list, FALSE); list->free_func = free_func; return TRUE; } /** * Checks the list for any nodes. * @param list The list to check for nodes * @return @c TRUE if no nodes in list, @c FALSE if the list contains nodes */ EAPI int ecore_list_empty_is(Ecore_List *list) { int ret = TRUE; CHECK_PARAM_POINTER_RETURN("list", list, TRUE); if (list->nodes) ret = FALSE; return ret; } /** * Returns the number of the current node. * @param list The list to return the number of the current node. * @return The number of the current node in the list. */ EAPI int ecore_list_index(Ecore_List *list) { int ret; CHECK_PARAM_POINTER_RETURN("list", list, FALSE); ret = list->index; return ret; } /** * Find the number of nodes in the list. * @param list The list to find the number of nodes * @return The number of nodes in the list. */ EAPI int ecore_list_count(Ecore_List *list) { int ret = 0; CHECK_PARAM_POINTER_RETURN("list", list, FALSE); ret = list->nodes; return ret; } /** @defgroup Ecore_Data_List_Add_Item_Group List Item Adding Functions Functions that are used to add nodes to an Ecore_List. */ /** * Append data to the list. * @param list The list. * @param data The data to append. * @return @c FALSE if an error occurs, @c TRUE if appended successfully * @ingroup Ecore_Data_List_Add_Item_Group */ EAPI inline int ecore_list_append(Ecore_List *list, void *data) { int ret; Ecore_List_Node *node; CHECK_PARAM_POINTER_RETURN("list", list, FALSE); node = ecore_list_node_new(); node->data = data; ret = _ecore_list_append_0(list, node); return ret; } /* For adding items to the end of the list */ static int _ecore_list_append_0(Ecore_List *list, Ecore_List_Node *end) { if (list->last) list->last->next = end; list->last = end; if (!list->first) { list->first = end; list->index = 0; list->current = NULL; } if (list->index >= list->nodes) list->index++; list->nodes++; return TRUE; } /** * Prepend data to the beginning of the list. * @param list The list. * @param data The data to prepend. * @return @c FALSE if an error occurs, @c TRUE if prepended successfully. * @ingroup Ecore_Data_List_Add_Item_Group */ EAPI inline int ecore_list_prepend(Ecore_List *list, void *data) { int ret; Ecore_List_Node *node; CHECK_PARAM_POINTER_RETURN("list", list, FALSE); node = ecore_list_node_new(); node->data = data; ret = _ecore_list_prepend_0(list, node); return ret; } /* For adding items to the beginning of the list */ static int _ecore_list_prepend_0(Ecore_List *list, Ecore_List_Node *start) { /* Put it at the beginning of the list */ start->next = list->first; list->first = start; /* If no last node, then the first node is the last node */ if (!list->last) list->last = list->first; list->nodes++; list->index++; return TRUE; } /** * Insert data in front of the current point in the list. * @param list The list to hold the inserted @p data. * @param data The data to insert into @p list. * @return @c FALSE if there is an error, @c TRUE on success * @ingroup Ecore_Data_List_Add_Item_Group */ EAPI inline int ecore_list_insert(Ecore_List *list, void *data) { int ret; Ecore_List_Node *node; CHECK_PARAM_POINTER_RETURN("list", list, FALSE); node = ecore_list_node_new(); node->data = data; ret = _ecore_list_insert(list, node); return ret; } /* For adding items in front of the current position in the list */ static int _ecore_list_insert(Ecore_List *list, Ecore_List_Node *new_node) { /* * If the current point is at the beginning of the list, then it's the * same as prepending it to the list. */ if (list->current == list->first) return _ecore_list_prepend_0(list, new_node); if (!list->current) { int ret_value; ret_value = _ecore_list_append_0(list, new_node); list->current = list->last; return ret_value; } /* Setup the fields of the new node */ new_node->next = list->current; /* And hook the node into the list */ _ecore_list_index_goto(list, ecore_list_index(list) - 1); list->current->next = new_node; /* Now move the current item to the inserted item */ list->current = new_node; list->nodes++; return TRUE; } /** * Append a list to the list. * @param list The list. * @param append The list to append. * @return @c FALSE if an error occurs, @c TRUE if appended successfully * @ingroup Ecore_Data_List_Add_Item_Group */ EAPI int ecore_list_append_list(Ecore_List *list, Ecore_List *append) { CHECK_PARAM_POINTER_RETURN("list", list, FALSE); CHECK_PARAM_POINTER_RETURN("append", append, FALSE); if (ecore_list_empty_is(append)) return TRUE; if (ecore_list_empty_is(list)) { list->first = append->first; list->current = list->first; list->last = append->last; list->nodes = append->nodes; } else { list->last->next = append->first; list->last = append->last; list->nodes += append->nodes; } ecore_list_init(append); return TRUE; } /** * Prepend a list to the beginning of the list. * @param list The list. * @param prepend The list to prepend. * @return @c FALSE if an error occurs, @c TRUE if prepended successfully. * @ingroup Ecore_Data_List_Add_Item_Group */ EAPI int ecore_list_prepend_list(Ecore_List *list, Ecore_List *prepend) { CHECK_PARAM_POINTER_RETURN("list", list, FALSE); CHECK_PARAM_POINTER_RETURN("prepend", prepend, FALSE); if (ecore_list_empty_is(prepend)) return TRUE; if (ecore_list_empty_is(list)) { list->first = prepend->first; list->current = NULL; list->last = prepend->last; list->nodes = prepend->nodes; } else { prepend->last->next = list->first; list->first = prepend->first; list->nodes += prepend->nodes; list->index += prepend->nodes; } ecore_list_init(prepend); return TRUE; } /** @defgroup Ecore_Data_List_Remove_Item_Group List Item Removing Functions Functions that remove nodes from an Ecore_List. */ /** * Remove the current item from the list. * @param list The list to remove the current item * @return A pointer to the removed data on success, @c NULL on failure. * @ingroup Ecore_Data_List_Remove_Item_Group */ EAPI inline void * ecore_list_remove(Ecore_List *list) { void *ret; CHECK_PARAM_POINTER_RETURN("list", list, NULL); ret = _ecore_list_remove_0(list); return ret; } /* Remove the current item from the list */ static void * _ecore_list_remove_0(Ecore_List *list) { void *ret = NULL; Ecore_List_Node *old; if (!list) return NULL; if (ecore_list_empty_is(list)) return NULL; if (!list->current) return NULL; if (list->current == list->first) return _ecore_list_first_remove(list); if (list->current == list->last) return _ecore_list_last_remove(list); old = list->current; _ecore_list_index_goto(list, list->index - 1); list->current->next = old->next; old->next = NULL; ret = old->data; old->data = NULL; _ecore_list_next(list); ecore_list_node_destroy(old, NULL); list->nodes--; return ret; } /** * Remove and free the data in lists current position. * @param list The list to remove and free the current item. * @return @c TRUE on success, @c FALSE on error * @ingroup Ecore_Data_List_Remove_Item_Group */ EAPI int ecore_list_remove_destroy(Ecore_List *list) { void *data; CHECK_PARAM_POINTER_RETURN("list", list, FALSE); data = _ecore_list_remove_0(list); if (list->free_func) list->free_func(data); return TRUE; } /** * Remove the first item from the list. * @param list The list to remove the current item * @return Returns a pointer to the removed data on success, @c NULL on * failure. * @ingroup Ecore_Data_List_Remove_Item_Group */ EAPI inline void * ecore_list_first_remove(Ecore_List *list) { void *ret; CHECK_PARAM_POINTER_RETURN("list", list, NULL); ret = _ecore_list_first_remove(list); return ret; } /* Remove the first item from the list */ static void * _ecore_list_first_remove(Ecore_List *list) { void *ret = NULL; Ecore_List_Node *old; if (!list) return NULL; if (ecore_list_empty_is(list)) return NULL; old = list->first; list->first = list->first->next; if (list->current == old) list->current = list->first; else (list->index ? list->index-- : 0); if (list->last == old) list->last = list->first; ret = old->data; old->data = NULL; ecore_list_node_destroy(old, NULL); list->nodes--; return ret; } /** * Remove the last item from the list. * @param list The list to remove the last node from * @return A pointer to the removed data on success, @c NULL on failure. * @ingroup Ecore_Data_List_Remove_Item_Group */ EAPI inline void * ecore_list_last_remove(Ecore_List *list) { void *ret; CHECK_PARAM_POINTER_RETURN("list", list, NULL); ret = _ecore_list_last_remove(list); return ret; } /* Remove the last item from the list */ static void * _ecore_list_last_remove(Ecore_List *list) { void *ret = NULL; Ecore_List_Node *old, *prev; if (!list) return NULL; if (ecore_list_empty_is(list)) return NULL; old = list->last; if (list->current == old) list->current = NULL; if (list->first == old) list->first = NULL; for (prev = list->first; prev && prev->next != old; prev = prev->next) ; list->last = prev; if (prev) prev->next = NULL; old->next = NULL; ret = old->data; old->data = NULL; ecore_list_node_destroy(old, NULL); list->nodes--; return ret; } /** @defgroup Ecore_Data_List_Traverse_Group List Traversal Functions Functions that can be used to traverse an Ecore_List. */ /** * Make the current item the item with the given index number. * @param list The list. * @param idx The position to move the current item. * @return A pointer to new current item on success, @c NULL on failure. * @ingroup Ecore_Data_List_Traverse_Group */ EAPI inline void * ecore_list_index_goto(Ecore_List *list, int idx) { void *ret; CHECK_PARAM_POINTER_RETURN("list", list, NULL); ret = _ecore_list_index_goto(list, idx); return ret; } /* This is the non-threadsafe version, use this inside internal functions that * already lock the list */ static void * _ecore_list_index_goto(Ecore_List *list, int idx) { int i; if (!list) return NULL; if (ecore_list_empty_is(list)) return NULL; if (idx > ecore_list_count(list) || idx < 0) return NULL; if (idx < list->index) { _ecore_list_first_goto(list); i = 0; } else i = list->index; for (; i < idx && _ecore_list_next(list); i++) ; if (i >= list->nodes) return NULL; list->index = i; return list->current->data; } /** * Make the current item the node that contains @p data. * @param list The list. * @param data The data to find. * @return A pointer to @p data on success, @c NULL on failure. * @ingroup Ecore_Data_List_Traverse_Group */ EAPI inline void * ecore_list_goto(Ecore_List *list, const void *data) { void *ret; CHECK_PARAM_POINTER_RETURN("list", list, NULL); ret = _ecore_list_goto(list, data); return ret; } /* Set the current position to the node containing data */ static void * _ecore_list_goto(Ecore_List *list, const void *data) { int idx; Ecore_List_Node *node; if (!list) return NULL; idx = 0; node = list->first; while (node && node->data) { Ecore_List_Node *next; if (node->data == data) break; next = node->next; node = next; idx++; } if (!node) return NULL; list->current = node; list->index = idx; return list->current->data; } /** * Make the current item the first item in the list * @param list The list. * @return A pointer to the first item on success, @c NULL on failure * @ingroup Ecore_Data_List_Traverse_Group */ EAPI inline void * ecore_list_first_goto(Ecore_List *list) { void *ret; CHECK_PARAM_POINTER_RETURN("list", list, NULL); ret = _ecore_list_first_goto(list); return ret; } /* Set the current position to the start of the list */ static void * _ecore_list_first_goto(Ecore_List *list) { if (!list || !list->first) return NULL; list->current = list->first; list->index = 0; return list->current->data; } /** * Make the current item the last item in the list. * @param list The list. * @return A pointer to the last item on success, @c NULL on failure. * @ingroup Ecore_Data_List_Traverse_Group */ EAPI inline void * ecore_list_last_goto(Ecore_List *list) { void *ret; CHECK_PARAM_POINTER_RETURN("list", list, NULL); ret = _ecore_list_last_goto(list); return ret; } /* Set the current position to the end of the list */ static void * _ecore_list_last_goto(Ecore_List *list) { if (!list || !list->last) return NULL; list->current = list->last; list->index = (list->nodes - 1); return list->current->data; } /** * Retrieve the data pointed to by the current item in @p list. * @param list The list. * @return Returns the data at current position, can be @c NULL. */ EAPI inline void * ecore_list_current(Ecore_List *list) { void *ret; ret = _ecore_list_current(list); return ret; } /** * Retrieve the data pointed to by the first item in @p list. * @param list The list. * @return Returns the data at current position, can be @c NULL. */ EAPI inline void * ecore_list_first(Ecore_List *list) { void *ret; if (!list->first) return NULL; ret = list->first->data; return ret; } /** * Retrieve the data pointed to by the last item in @p list. * @param list The list. * @return Returns the data at current position, can be @c NULL. */ EAPI inline void * ecore_list_last(Ecore_List *list) { void *ret; if (!list->last) return NULL; ret = list->last->data; return ret; } /* Return the data of the current node without incrementing */ static void * _ecore_list_current(Ecore_List *list) { void *ret; if (!list->current) return NULL; ret = list->current->data; return ret; } /** * Retrieve the data pointed to by the current item, and make the next item * the current item. * @param list The list to retrieve data from. * @return The current item in the list on success, @c NULL on failure. */ EAPI inline void * ecore_list_next(Ecore_List *list) { void *data; CHECK_PARAM_POINTER_RETURN("list", list, NULL); data = _ecore_list_next(list); return data; } /* Return the data contained in the current node and go to the next node */ static void * _ecore_list_next(Ecore_List *list) { void *data; Ecore_List_Node *ret; Ecore_List_Node *next; if (!list->current) return NULL; ret = list->current; next = list->current->next; list->current = next; list->index++; data = ret->data; return data; } /** * Remove all nodes from @p list. * @param list The list. * @return Returns @c TRUE on success, @c FALSE on error. * @note The data for each item on the list is not freed by * @c ecore_list_clear(). */ EAPI int ecore_list_clear(Ecore_List *list) { CHECK_PARAM_POINTER_RETURN("list", list, FALSE); while (!ecore_list_empty_is(list)) _ecore_list_first_remove(list); return TRUE; } /** * Execute function for each node in @p list. * @param list The list. * @param function The function to pass each node from @p list to. * @return Returns @c TRUE on success, @c FALSE on failure. * @ingroup Ecore_Data_List_Traverse_Group */ EAPI int ecore_list_for_each(Ecore_List *list, Ecore_For_Each function, void *user_data) { int ret; CHECK_PARAM_POINTER_RETURN("list", list, FALSE); ret = _ecore_list_for_each(list, function, user_data); return ret; } /* The real meat of executing the function for each data node */ static int _ecore_list_for_each(Ecore_List *list, Ecore_For_Each function, void *user_data) { void *value; if (!list || !function) return FALSE; _ecore_list_first_goto(list); while ((value = _ecore_list_next(list))) function(value, user_data); return TRUE; } /** * Find data in @p list using the compare function @p func * @param list The list. * @param function The function to test each node of @p list with * @param user_data Data to match against (used by @p function) * @return the first matching data node, or NULL if none match */ EAPI void * ecore_list_find(Ecore_List *list, Ecore_Compare_Cb function, const void *user_data) { CHECK_PARAM_POINTER_RETURN("list", list, NULL); return _ecore_list_find(list, function, user_data); } /* The real meat of finding a node via a compare cb */ static void * _ecore_list_find(Ecore_List *list, Ecore_Compare_Cb function, const void *user_data) { void *value; if (!list || !function) return NULL; _ecore_list_first_goto(list); while ((value = _ecore_list_current(list))) { if (!function(value, user_data)) return value; ecore_list_next(list); } return NULL; } /** * Sort data in @p list using the compare function @p compare * @param list The list. * @param compare The function to compare the data of @p list * @param order The sort direction, possible values are ECORE_SORT_MIN and * ECORE_SORT_MAX * @return true on success * * This is a wrapper function for mergesort and heapsort. It * tries to choose the fastest algorithm depending on the * number of notes. Note: The sort may be unstable. */ EAPI int ecore_list_sort(Ecore_List *list, Ecore_Compare_Cb compare, char order) { CHECK_PARAM_POINTER_RETURN("list", list, 0); if (list->nodes < 2) return 1; if (list->nodes < ECORE_MERGESORT_LIMIT) return ecore_list_mergesort(list, compare, order); if (!ecore_list_heapsort(list, compare, order)) return ecore_list_mergesort(list, compare, order); return 1; } /** * Sort data in @p list using the compare function @p compare * @param list The list. * @param compare The function to compare the data of @p list * @param order The sort direction, possible values are ECORE_SORT_MIN and * ECORE_SORT_MAX * @return true on success * * Mergesort is a stable, in-place sorting algorithm */ EAPI int ecore_list_mergesort(Ecore_List *list, Ecore_Compare_Cb compare, char order) { Ecore_List_Node *node; CHECK_PARAM_POINTER_RETURN("list", list, 0); if (list->nodes < 2) return 1; if (order == ECORE_SORT_MIN) order = 1; else order = -1; node = _ecore_list_node_mergesort(list->first, list->nodes, compare, order); list->first = node; /* maybe there is a better way to do that but our last node has changed */ while (node->next) node = node->next; list->last = node; _ecore_list_first_goto(list); return 1; } /** * Merge the @p l2 into the @p list using the compare function @p compare. * Both lists need to be sorted else a corrupt list could be the result. * @param list The list. * @param l2 The second list, this list will be empty after the merge * @param compare The function to compare the data of @p list and @p l2 * @param order The sort direction, possible values are ECORE_SORT_MIN and * ECORE_SORT_MAX */ EAPI void ecore_list_merge(Ecore_List *list, Ecore_List *l2, Ecore_Compare_Cb compare, char order) { CHECK_PARAM_POINTER("list", list); CHECK_PARAM_POINTER("l2", l2); if (ecore_list_empty_is(l2)) return; if (ecore_list_empty_is(list)) { ecore_list_append_list(list, l2); return; } if (order == ECORE_SORT_MIN) order = 1; else order = -1; list->first = _ecore_list_node_merge(list->first, l2->first, compare, order); if ((order * compare(list->last->data, l2->last->data)) < 0) list->last = l2->last; list->nodes += l2->nodes; ecore_list_init(l2); } /* this is the internal recrusive function for the merge sort */ static Ecore_List_Node * _ecore_list_node_mergesort(Ecore_List_Node *first, int n, Ecore_Compare_Cb compare, int order) { Ecore_List_Node *middle; Ecore_List_Node *premid; int mid; int i; mid = n / 2; if (n < 2) return first; else if (n == 2) { if (compare(first->data, first->next->data) * order > 0) { /* swap the data */ void *data; data = first->next->data; first->next->data = first->data; first->data = data; } return first; } /* first find the premiddle node*/ for (premid = first, i = 0; i < mid - 1; i++) premid = premid->next; /* split the list */ middle = premid->next; premid->next = NULL; /* sort the the partial lists */ first = _ecore_list_node_mergesort(first, mid, compare, order); middle = _ecore_list_node_mergesort(middle, n - mid, compare, order); return _ecore_list_node_merge(first, middle, compare, order); } /* this function is used to merge the partial sorted lists */ static Ecore_List_Node * _ecore_list_node_merge(Ecore_List_Node *first, Ecore_List_Node *second, Ecore_Compare_Cb compare, int order) { Ecore_List_Node *list; Ecore_List_Node *l; /* select the first node outside the loop, because we need to keep * a pointer to it */ if (compare(first->data, second->data) * order > 0) { list = l = second; second = second->next; } else { list = l = first; first = first->next; } /* and now start the merging */ while (first && second) { if (compare(first->data, second->data) * order > 0) { l = l->next = second; second = second->next; } else { l = l->next = first; first = first->next; } } /* append the rest or set it to NULL */ if (first) l->next = first; else if (second) l->next = second; else l->next = NULL; return list; } /** * Sort data in @p list using the compare function @p compare * @param list The list. * @param compare The function to compare the data of @p list * @param order The sort direction, possible values are ECORE_SORT_MIN and * ECORE_SORT_MAX * @return true on success * * Heapsort is a unstable sorting algorithm, it needs to allocate extra memomry, * but there for it is for a great number of nodes faster than mergesort */ EAPI int ecore_list_heapsort(Ecore_List *list, Ecore_Compare_Cb compare, char order) { Ecore_Sheap *heap; Ecore_List_Node *node; void *data; CHECK_PARAM_POINTER_RETURN("list", list, 0); /* * Push the data into a heap. */ heap = ecore_sheap_new(compare, list->nodes); if (!heap) return 0; ecore_sheap_order_set(heap, order); _ecore_list_first_goto(list); while ((data = _ecore_list_next(list))) { ecore_sheap_insert(heap, data); } /* * Extract in sorted order. */ node = list->first; while (node) { node->data = ecore_sheap_extract(heap); node = node->next; } ecore_sheap_destroy(heap); _ecore_list_first_goto(list); return 1; } /* Initialize a node to starting values */ EAPI int ecore_list_node_init(Ecore_List_Node *node) { CHECK_PARAM_POINTER_RETURN("node", node, FALSE); node->next = NULL; node->data = NULL; return TRUE; } /** @defgroup Ecore_Data_List_Node_Group List Node Functions Functions that are used in the creation, maintenance and destruction of Ecore_List nodes. */ /** * Allocates and initializes a new list node. * @return A new Ecore_List_Node on success, @c NULL otherwise. * @ingroup Ecore_Data_List_Node_Group */ EAPI Ecore_List_Node * ecore_list_node_new() { Ecore_List_Node *new_node; new_node = malloc(sizeof(Ecore_List_Node)); if (!ecore_list_node_init(new_node)) { FREE(new_node); return NULL; } return new_node; } /** * Calls the function to free the data and the node. * @param node Node to destroy. * @param free_func Function to call if @p node points to data to free. * @return @c TRUE. * @ingroup Ecore_Data_List_Node_Group */ EAPI int ecore_list_node_destroy(Ecore_List_Node *node, Ecore_Free_Cb free_func) { CHECK_PARAM_POINTER_RETURN("node", node, FALSE); if (free_func && node->data) free_func(node->data); FREE(node); return TRUE; } /** * @defgroup Ecore_Data_DList_Creation_Group Doubly Linked List Creation/Destruction Functions * * Functions used to create, initialize and destroy @c Ecore_DLists. */ /** * Creates and initialises a new doubly linked list. * @return A new initialised doubly linked list on success, @c NULL * on failure. * @ingroup Ecore_Data_DList_Creation_Group */ EAPI Ecore_DList * ecore_dlist_new() { Ecore_DList *list = NULL; list = (Ecore_DList *)malloc(sizeof(Ecore_DList)); if (!list) return NULL; if (!ecore_dlist_init(list)) { IF_FREE(list); return NULL; } return list; } /** * Initialises a list to some sane starting values. * @param list The doubly linked list to initialise. * @return @c TRUE if successful, @c FALSE if an error occurs. * @ingroup Ecore_Data_DList_Creation_Group */ EAPI int ecore_dlist_init(Ecore_DList *list) { CHECK_PARAM_POINTER_RETURN("list", list, FALSE); memset(list, 0, sizeof(Ecore_DList)); return TRUE; } /** * Frees a doubly linked list and all of its nodes. * @param list The doubly linked list to be freed. * @ingroup Ecore_Data_DList_Creation_Group */ EAPI void ecore_dlist_destroy(Ecore_DList *list) { void *data; CHECK_PARAM_POINTER("list", list); while (list->first) { data = _ecore_dlist_first_remove(list); if (list->free_func) list->free_func(data); } FREE(list); } /** * Sets the function used for freeing data stored in a doubly linked list. * @param list The doubly linked list that will use this function when * nodes are destroyed. * @param free_func The function that will free the key data * @return @c TRUE on success, @c FALSE on failure. * @ingroup Ecore_Data_DList_Creation_Group */ EAPI int ecore_dlist_free_cb_set(Ecore_DList *list, Ecore_Free_Cb free_func) { CHECK_PARAM_POINTER_RETURN("list", list, FALSE); return ecore_list_free_cb_set(ECORE_LIST(list), free_func); } /** * Returns whether there is anything in the given doubly linked list. * @param list The given doubly linked list. * @return @c TRUE if there are nodes, @c FALSE otherwise. */ EAPI int ecore_dlist_empty_is(Ecore_DList *list) { CHECK_PARAM_POINTER_RETURN("list", list, FALSE); return ecore_list_empty_is(ECORE_LIST(list)); } /** * Retrieves the index of the current node of the given doubly linked list. * @param list The given doubly linked list. * @return The index of the current node. */ EAPI inline int ecore_dlist_index(Ecore_DList *list) { CHECK_PARAM_POINTER_RETURN("list", list, FALSE); return ecore_list_index(ECORE_LIST(list)); } /** * @defgroup Ecore_Data_DList_Add_Item_Group Doubly Linked List Adding Functions * * Functions that are used to add nodes to an Ecore_DList. */ /** * Appends data to the given doubly linked list. * @param list The given doubly linked list. * @param data The data to append. * @return @c TRUE if the data is successfully appended, @c FALSE otherwise. * @ingroup Ecore_Data_DList_Add_Item_Group */ EAPI int ecore_dlist_append(Ecore_DList *list, void *data) { int ret; Ecore_DList_Node *prev; Ecore_DList_Node *node; CHECK_PARAM_POINTER_RETURN("list", list, FALSE); node = ecore_dlist_node_new(); ECORE_LIST_NODE(node)->data = data; prev = ECORE_DLIST_NODE(ECORE_LIST(list)->last); ret = _ecore_list_append_0(ECORE_LIST(list), ECORE_LIST_NODE(node)); if (ret) node->previous = prev; return ret; } /** * Adds data to the very beginning of the given doubly linked list. * @param list The given doubly linked list. * @param data The data to prepend. * @return @c TRUE if the data is successfully prepended, @c FALSE otherwise. * @ingroup Ecore_Data_DList_Add_Item_Group */ EAPI int ecore_dlist_prepend(Ecore_DList *list, void *data) { int ret; Ecore_DList_Node *prev; Ecore_DList_Node *node; CHECK_PARAM_POINTER_RETURN("list", list, FALSE); node = ecore_dlist_node_new(); ECORE_LIST_NODE(node)->data = data; prev = ECORE_DLIST_NODE(ECORE_LIST(list)->first); ret = _ecore_list_prepend_0(ECORE_LIST(list), ECORE_LIST_NODE(node)); if (ret && prev) prev->previous = node; return ret; } /** * Inserts data at the current point in the given doubly linked list. * @param list The given doubly linked list. * @param data The data to be inserted. * @return @c TRUE on success, @c FALSE otherwise. * @ingroup Ecore_Data_DList_Add_Item_Group */ EAPI int ecore_dlist_insert(Ecore_DList *list, void *data) { int ret = TRUE; Ecore_DList_Node *prev; Ecore_DList_Node *node; CHECK_PARAM_POINTER_RETURN("list", list, FALSE); /* * Identify and shortcut the end cases. */ if (!ECORE_LIST(list)->current) return ecore_dlist_append(list, data); if (ECORE_LIST(list)->current == ECORE_LIST(list)->first) return ecore_dlist_prepend(list, data); node = ecore_dlist_node_new(); ECORE_LIST_NODE(node)->data = data; /* Setup the fields of the new node */ ECORE_LIST_NODE(node)->next = ECORE_LIST(list)->current; /* And hook the node into the list */ prev = ECORE_DLIST_NODE(ECORE_LIST(list)->current)->previous; ECORE_LIST_NODE(prev)->next = ECORE_LIST_NODE(node); ECORE_DLIST_NODE(ECORE_LIST(list)->current)->previous = node; node->previous = prev; /* Now move the current item to the inserted item */ ECORE_LIST(list)->current = ECORE_LIST_NODE(node); ECORE_LIST(list)->nodes++; return ret; } /** * Appends a list to the given doubly linked list. * @param list The given doubly linked list. * @param append The list to append. * @return @c TRUE if the data is successfully appended, @c FALSE otherwise. * @ingroup Ecore_Data_DList_Add_Item_Group */ EAPI int ecore_dlist_append_list(Ecore_DList *list, Ecore_DList *append) { CHECK_PARAM_POINTER_RETURN("list", list, FALSE); CHECK_PARAM_POINTER_RETURN("append", append, FALSE); if (ecore_dlist_empty_is(append)) return TRUE; if (ecore_dlist_empty_is(list)) { list->first = append->first; list->current = NULL; list->last = append->last; list->nodes = append->nodes; } else { list->last->next = append->first; ECORE_DLIST_NODE(append->first)->previous = ECORE_DLIST_NODE(list->last); list->last = append->last; list->nodes += append->nodes; } ecore_dlist_init(append); return TRUE; } /** * Adds a list to the very beginning of the given doubly linked list. * @param list The given doubly linked list. * @param prepend The list to prepend. * @return @c TRUE if the data is successfully prepended, @c FALSE otherwise. * @ingroup Ecore_Data_DList_Add_Item_Group */ EAPI int ecore_dlist_prepend_list(Ecore_DList *list, Ecore_DList *prepend) { CHECK_PARAM_POINTER_RETURN("list", list, FALSE); CHECK_PARAM_POINTER_RETURN("prepend", prepend, FALSE); if (ecore_dlist_empty_is(prepend)) return TRUE; if (ecore_dlist_empty_is(list)) { list->first = prepend->first; list->current = NULL; list->last = prepend->last; list->nodes = prepend->nodes; } else { prepend->last->next = list->first; ECORE_DLIST_NODE(list->first)->previous = ECORE_DLIST_NODE( prepend->last); list->first = prepend->first; list->nodes += prepend->nodes; list->index += prepend->nodes; } ecore_dlist_init(prepend); return TRUE; } /** * @defgroup Ecore_Data_DList_Remove_Item_Group Doubly Linked List Removing Functions * * Functions that remove nodes from an @c Ecore_DList. */ /** * Removes the current item from the given doubly linked list. * @param list The given doubly linked list. * @return A pointer to the removed data on success, @c NULL otherwise. * @ingroup Ecore_Data_DList_Remove_Item_Group */ EAPI void * ecore_dlist_remove(Ecore_DList *list) { void *ret; Ecore_List *l2 = ECORE_LIST(list); Ecore_DList_Node *node; CHECK_PARAM_POINTER_RETURN("list", list, NULL); if (l2->current) { node = ECORE_DLIST_NODE(list->current->next); if (node) node->previous = ECORE_DLIST_NODE(l2->current)->previous; } ret = _ecore_list_remove_0(list); return ret; } /** * Removes the first item from the given doubly linked list. * @param list The given doubly linked list. * @return A pointer to the removed data on success, @c NULL on failure. * @ingroup Ecore_Data_DList_Remove_Item_Group */ EAPI void * ecore_dlist_first_remove(Ecore_DList *list) { void *ret; CHECK_PARAM_POINTER_RETURN("list", list, NULL); ret = _ecore_dlist_first_remove(list); return ret; } /** * Removes and frees the data at the current position in the given doubly * linked list. * @param list The given doubly linked list. * @return @c TRUE on success, @c FALSE otherwise. * @ingroup Ecore_Data_DList_Remove_Item_Group */ EAPI int ecore_dlist_remove_destroy(Ecore_DList *list) { void *data; CHECK_PARAM_POINTER_RETURN("list", list, FALSE); data = ecore_dlist_remove(list); if (!data) return FALSE; if (list->free_func) list->free_func(data); return TRUE; } static void * _ecore_dlist_first_remove(Ecore_DList *list) { void *ret; if (!list) return NULL; ret = _ecore_list_first_remove(list); if (ret && ECORE_LIST(list)->first) ECORE_DLIST_NODE(ECORE_LIST(list)->first)->previous = NULL; return ret; } /** * Removes the last item from the given doubly linked list. * @param list The given doubly linked list. * @return A pointer to the removed data on success, @c NULL otherwise. * @ingroup Ecore_Data_DList_Remove_Item_Group */ EAPI void * ecore_dlist_last_remove(Ecore_DList *list) { void *ret; Ecore_List_Node *node; CHECK_PARAM_POINTER_RETURN("list", list, NULL); if (ecore_list_empty_is(list)) return NULL; node = list->last; list->last = ECORE_LIST_NODE(ECORE_DLIST_NODE(node)->previous); if (list->last) list->last->next = NULL; if (list->first == node) list->first = NULL; if (list->current == node) list->current = NULL; ret = node->data; ecore_list_node_destroy(node, NULL); list->nodes--; if (list->index >= list->nodes) list->index--; return ret; } /** * Moves the current item to the index number in the given doubly linked list. * @param list The given doubly linked list. * @param idx The position to move the current item * @return The node at specified index on success, @c NULL on error. */ EAPI void * ecore_dlist_index_goto(Ecore_DList *list, int idx) { void *ret; CHECK_PARAM_POINTER_RETURN("list", list, NULL); ret = _ecore_dlist_index_goto(list, idx); return ret; } /* This is the non-threadsafe version, use this inside internal functions that * already lock the list */ static void * _ecore_dlist_index_goto(Ecore_DList *list, int idx) { int i, increment; if (!list) return NULL; if (ecore_list_empty_is(ECORE_LIST(list))) return NULL; if (idx > ecore_list_count(ECORE_LIST(list)) || idx < 0) return NULL; if (ECORE_LIST(list)->index >= ECORE_LIST(list)->nodes) _ecore_list_last_goto(ECORE_LIST(list)); if (idx < ECORE_LIST(list)->index) increment = -1; else increment = 1; for (i = ECORE_LIST(list)->index; i != idx; i += increment) { if (increment > 0) _ecore_list_next(list); else _ecore_dlist_previous(list); } return _ecore_list_current(list); } /** * @brief Move the current item to the node that contains data * @param list: the list to move the current item in * @param data: the data to find and set the current item to * * @return Returns specified data on success, NULL on error */ EAPI void * ecore_dlist_goto(Ecore_DList *list, void *data) { void *ret; CHECK_PARAM_POINTER_RETURN("list", list, NULL); ret = _ecore_list_goto(ECORE_LIST(list), data); return ret; } /** * @brief Move the current pointer to the first item in the list * @param list: the list to change the current to the first item * * @return Returns a pointer to the first item on success, NULL on failure. */ EAPI void * ecore_dlist_first_goto(Ecore_DList *list) { void *ret; CHECK_PARAM_POINTER_RETURN("list", list, NULL); ret = _ecore_list_first_goto(list); return ret; } /** * @brief Move the pointer to the current item to the last item * @param list: the list to move the current item pointer to the last * @return Returns a pointer to the last item in the list , NULL if empty. */ EAPI void * ecore_dlist_last_goto(Ecore_DList *list) { void *ret; CHECK_PARAM_POINTER_RETURN("list", list, NULL); ret = _ecore_list_last_goto(ECORE_LIST(list)); return ret; } /** * @brief Return the data in the current list item * @param list: the list to the return the current data * @return Returns value of the current data item, NULL if no current item */ EAPI void * ecore_dlist_current(Ecore_DList *list) { void *ret; ret = _ecore_list_current(ECORE_LIST(list)); return ret; } /** * @brief Move to the next item in the list and return current item * @param list: the list to move to the next item in. * @return Returns data in the current list node, or NULL on error */ EAPI void * ecore_dlist_next(Ecore_DList *list) { void *data; data = _ecore_list_next(list); return data; } /** * @brief Move to the previous item and return current item * @param list: the list to move to the previous item in. * @return Returns data in the current list node, or NULL on error */ EAPI void * ecore_dlist_previous(Ecore_DList *list) { void *data; data = _ecore_dlist_previous(list); return data; } static void * _ecore_dlist_previous(Ecore_DList *list) { void *data = NULL; if (!list) return NULL; if (ECORE_LIST(list)->current) { data = ECORE_LIST(list)->current->data; ECORE_LIST(list)-> current = ECORE_LIST_NODE(ECORE_DLIST_NODE( ECORE_LIST(list)-> current)->previous); ECORE_LIST(list)->index --; } else _ecore_list_last_goto( ECORE_LIST(list)); return data; } /** * @brief Remove all nodes from the list. * @param list: the list to remove all nodes from * * @return Returns TRUE on success, FALSE on errors */ EAPI int ecore_dlist_clear(Ecore_DList *list) { CHECK_PARAM_POINTER_RETURN("list", list, FALSE); ecore_list_clear(ECORE_LIST(list)); return TRUE; } /** * Sort data in @p list using the compare function @p compare * @param list The list. * @param compare The function to compare the data of @p list * @param order The sort direction, possible values are ECORE_SORT_MIN and * ECORE_SORT_MAX * @return true on success * * This is a wrapper function for mergesort and heapsort. It * tries to choose the fastest algorithm depending on the * number of notes. Note: The sort may be unstable. */ EAPI int ecore_dlist_sort(Ecore_List *list, Ecore_Compare_Cb compare, char order) { CHECK_PARAM_POINTER_RETURN("list", list, 0); if (list->nodes < 2) return 1; if (list->nodes < ECORE_MERGESORT_LIMIT) return ecore_dlist_mergesort(list, compare, order); if (!ecore_dlist_heapsort(list, compare, order)) return ecore_dlist_mergesort(list, compare, order); return 1; } /** * Sort data in @p list using the compare function @p compare * @param list The list. * @param compare The function to compare the data of @p list * @param order The sort direction, possible values are ECORE_SORT_MIN and * ECORE_SORT_MAX * @return true on success * * Mergesort is a stable, in-place sorting algorithm */ EAPI int ecore_dlist_mergesort(Ecore_DList *list, Ecore_Compare_Cb compare, char order) { Ecore_List_Node *node; CHECK_PARAM_POINTER_RETURN("list", list, 0); if (list->nodes < 2) return 1; if (order == ECORE_SORT_MIN) order = 1; else order = -1; node = _ecore_dlist_node_mergesort(list->first, list->nodes, compare, order); list->first = node; /* maybe there is a better way to do that but our last node has changed */ while (node->next) node = node->next; list->last = node; _ecore_list_first_goto(list); return 1; } /** * Merge the @p l2 into the @p list using the compare function @p compare. * Both lists need to be sorted else a corrupt list could be the result. * @param list The list. * @param l2 The second list, this list will be empty after the merge * @param compare The function to compare the data of @p list and @p l2 * @param order The sort direction, possible values are ECORE_SORT_MIN and * ECORE_SORT_MAX */ EAPI void ecore_dlist_merge(Ecore_DList *list, Ecore_DList *l2, Ecore_Compare_Cb compare, char order) { CHECK_PARAM_POINTER("list", list); CHECK_PARAM_POINTER("l2", l2); if (ecore_dlist_empty_is(l2)) return; if (ecore_dlist_empty_is(list)) { ecore_dlist_append_list(list, l2); return; } if (order == ECORE_SORT_MIN) order = 1; else order = -1; list->first = _ecore_dlist_node_merge(list->first, l2->first, compare, order); if ((order * compare(list->last->data, l2->last->data)) < 0) list->last = l2->last; list->nodes += l2->nodes; ecore_dlist_init(l2); } /* this is the internal recrusive function for the merge sort */ static Ecore_List_Node * _ecore_dlist_node_mergesort(Ecore_List_Node *first, int n, Ecore_Compare_Cb compare, int order) { Ecore_List_Node *middle; Ecore_List_Node *premid; int mid; int i; mid = n / 2; if (n < 2) return first; else if (n == 2) { if (compare(first->data, first->next->data) * order > 0) { /* swap the data */ void *data; data = first->next->data; first->next->data = first->data; first->data = data; } return first; } /* first find the premiddle node*/ for (premid = first, i = 0; i < mid - 1; i++) premid = premid->next; /* split the list */ middle = premid->next; premid->next = NULL; ECORE_DLIST_NODE(middle)->previous = NULL; /* sort the the partial lists */ first = _ecore_dlist_node_mergesort(first, mid, compare, order); middle = _ecore_dlist_node_mergesort(middle, n - mid, compare, order); return _ecore_dlist_node_merge(first, middle, compare, order); } /* this function is used to merge the partial sorted lists */ static Ecore_List_Node * _ecore_dlist_node_merge(Ecore_List_Node *first, Ecore_List_Node *second, Ecore_Compare_Cb compare, int order) { Ecore_List_Node *list; Ecore_List_Node *l; /* select the first node outside the loop, because we need to keep * a pointer to it */ if (compare(first->data, second->data) * order > 0) { list = l = second; second = second->next; } else { list = l = first; first = first->next; } /* and now start the merging */ while (first && second) { if (compare(first->data, second->data) * order > 0) { ECORE_DLIST_NODE(second)->previous = ECORE_DLIST_NODE(l); l = l->next = second; second = second->next; } else { ECORE_DLIST_NODE(first)->previous = ECORE_DLIST_NODE(l); l = l->next = first; first = first->next; } } /* append the rest or set it to NULL */ if (first) { ECORE_DLIST_NODE(first)->previous = ECORE_DLIST_NODE(l); l->next = first; } else if (second) { ECORE_DLIST_NODE(second)->previous = ECORE_DLIST_NODE(l); l->next = second; } else l->next = NULL; return list; } /* * @brief Initialize a node to sane starting values * @param node: the node to initialize * @return Returns TRUE on success, FALSE on errors */ EAPI int ecore_dlist_node_init(Ecore_DList_Node *node) { int ret; CHECK_PARAM_POINTER_RETURN("node", node, FALSE); ret = ecore_list_node_init(ECORE_LIST_NODE(node)); if (ret) node->previous = NULL; return ret; } /* * @brief Allocate and initialize a new list node * @return Returns NULL on error, new list node on success */ EAPI Ecore_DList_Node * ecore_dlist_node_new() { Ecore_DList_Node *new_node; new_node = malloc(sizeof(Ecore_DList_Node)); if (!new_node) return NULL; if (!ecore_dlist_node_init(new_node)) { FREE(new_node); return NULL; } return new_node; } /* * @brief Call the data's free callback function, then free the node * @param node: the node to be freed * @param free_func: the callback function to execute on the data * @return Returns TRUE on success, FALSE on error */ EAPI int ecore_dlist_node_destroy(Ecore_DList_Node *node, Ecore_Free_Cb free_func) { CHECK_PARAM_POINTER_RETURN("node", node, FALSE); return ecore_list_node_destroy(ECORE_LIST_NODE(node), free_func); }