From: caro Date: Sun, 24 Jan 2010 10:54:07 +0000 (+0000) Subject: make the ecore tests stand alone (no ecore installation needed) X-Git-Tag: 2.0_alpha~70^2~702 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=de6a34725fb8b7bed9ce0365972a4f42fdc38146;p=framework%2Fuifw%2Feina.git make the ecore tests stand alone (no ecore installation needed) so no cyclic dependency git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/eina@45512 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33 --- diff --git a/configure.ac b/configure.ac index fcbc3cf..06721ac 100644 --- a/configure.ac +++ b/configure.ac @@ -419,7 +419,6 @@ fi EFL_CHECK_BENCHMARK([enable_benchmark="yes"], [enable_benchmark="no"]) EINA_BENCH_MODULE([evas], [${enable_benchmark}], [evas], [enable_benchmark_evas="yes"], [enable_benchmark_evas="no"]) -EINA_BENCH_MODULE([ecore], [${enable_benchmark}], [ecore], [enable_benchmark_ecore="yes"], [enable_benchmark_ecore="no"]) EINA_BENCH_MODULE([glib], [${enable_benchmark}], [glib-2.0], [enable_benchmark_glib="yes"], [enable_benchmark_glib="no"]) AC_SUBST(requirement_eina) @@ -490,7 +489,6 @@ echo " Benchmark............: ${enable_benchmark}" if test "x${enable_benchmark}" = "xyes" ; then echo " Glib...............: ${enable_benchmark_glib}" echo " Evas...............: ${enable_benchmark_evas}" -echo " Ecore..............: ${enable_benchmark_ecore}" echo " E17 real data......: ${enable_benchmark_e17}" fi echo diff --git a/src/tests/Ecore_Data.h b/src/tests/Ecore_Data.h new file mode 100644 index 0000000..532403b --- /dev/null +++ b/src/tests/Ecore_Data.h @@ -0,0 +1,506 @@ +#ifndef _ECORE_DATA_H +# define _ECORE_DATA_H + +#include +/* we need this for size_t */ +#include + +#ifdef EAPI +# undef EAPI +#endif + +#ifdef _WIN32 +# ifdef EFL_ECORE_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_ECORE_BUILD */ +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif /* ! _WIN32 */ + +/** + * @file Ecore_Data.h + * @brief Contains threading, list, hash, debugging and tree functions. + */ + +# ifdef __cplusplus +extern "C" { +# endif + + +#ifndef TRUE +# define TRUE 1 +#endif + +#ifndef FALSE +# define FALSE 0 +#endif + +#ifdef FREE +# undef FREE +#endif +#define FREE(ptr) free(ptr); ptr = NULL; + +#ifdef IF_FREE +# undef IF_FREE +#endif +#define IF_FREE(ptr) if (ptr) free(ptr); ptr = NULL; + +/* convenience macros for checking pointer parameters for non-NULL */ +#undef CHECK_PARAM_POINTER_RETURN +#define CHECK_PARAM_POINTER_RETURN(sparam, param, ret) \ + if (!(param)) \ + { \ + printf("***** Developer Warning ***** :\n" \ + "\tThis program is calling:\n\n" \ + "\t%s();\n\n" \ + "\tWith the parameter:\n\n" \ + "\t%s\n\n" \ + "\tbeing NULL. Please fix your program.", __FUNCTION__, sparam); \ + if (getenv("ECORE_ERROR_ABORT")) abort(); \ + return ret; \ + } + +#undef CHECK_PARAM_POINTER +#define CHECK_PARAM_POINTER(sparam, param) \ + if (!(param)) \ + { \ + printf("***** Developer Warning ***** :\n" \ + "\tThis program is calling:\n\n" \ + "\t%s();\n\n" \ + "\tWith the parameter:\n\n" \ + "\t%s\n\n" \ + "\tbeing NULL. Please fix your program.", __FUNCTION__, sparam); \ + if (getenv("ECORE_ERROR_ABORT")) abort(); \ + return; \ + } + + +# ifdef __sgi +# define __FUNCTION__ "unknown" +# ifndef __cplusplus +# define inline +# endif +# endif + +# define ECORE_SORT_MIN 0 +# define ECORE_SORT_MAX 1 + + typedef void (*Ecore_For_Each) (void *value, void *user_data); +# define ECORE_FOR_EACH(function) ((Ecore_For_Each)function) + + typedef void (*Ecore_Free_Cb) (void *data); +# define ECORE_FREE_CB(func) ((Ecore_Free_Cb)func) + + typedef unsigned int (*Ecore_Hash_Cb) (const void *key); +# define ECORE_HASH_CB(function) ((Ecore_Hash_Cb)function) + + typedef int (*Ecore_Compare_Cb) (const void *data1, const void *data2); +# define ECORE_COMPARE_CB(function) ((Ecore_Compare_Cb)function) + + typedef struct _ecore_list Ecore_List; +# define ECORE_LIST(list) ((Ecore_List *)list) + + typedef struct _ecore_list_node Ecore_List_Node; +# define ECORE_LIST_NODE(node) ((Ecore_List_Node *)node) + + typedef struct _ecore_strbuf Ecore_Strbuf; +# define ECORE_STRBUF(buf) ((Ecore_Strbuf *)buf) + + struct _ecore_list_node { + void *data; + struct _ecore_list_node *next; + }; + + struct _ecore_list { + Ecore_List_Node *first; /* The first node in the list */ + Ecore_List_Node *last; /* The last node in the list */ + Ecore_List_Node *current; /* The current node in the list */ + + Ecore_Free_Cb free_func; /* The callback to free data in nodes */ + + int nodes; /* The number of nodes in the list */ + int index; /* The position from the front of the + list of current node */ + }; + + EAPI int ecore_direct_compare(const void *key1, const void *key2); + EAPI int ecore_str_compare(const void *key1, const void *key2); + + EAPI unsigned int ecore_direct_hash(const void *key); + EAPI unsigned int ecore_str_hash(const void *key); + + /* Creating and initializing new list structures */ + EAPI Ecore_List *ecore_list_new(void); + EAPI int ecore_list_init(Ecore_List *list); + + /* Adding items to the list */ + EAPI int ecore_list_append(Ecore_List * list, void *_data); + EAPI int ecore_list_prepend(Ecore_List * list, void *_data); + EAPI int ecore_list_insert(Ecore_List * list, void *_data); + EAPI int ecore_list_append_list(Ecore_List * list, Ecore_List * append); + EAPI int ecore_list_prepend_list(Ecore_List * list, Ecore_List * prepend); + + /* Removing items from the list */ + EAPI int ecore_list_remove_destroy(Ecore_List *list); + EAPI void *ecore_list_remove(Ecore_List * list); + EAPI void *ecore_list_first_remove(Ecore_List * list); + EAPI void *ecore_list_last_remove(Ecore_List * list); + + /* Retrieve the current position in the list */ + EAPI void *ecore_list_current(Ecore_List * list); + EAPI void *ecore_list_first(Ecore_List * list); + EAPI void *ecore_list_last(Ecore_List * list); + EAPI int ecore_list_index(Ecore_List * list); + EAPI int ecore_list_count(Ecore_List * list); + + /* Traversing the list */ + EAPI int ecore_list_for_each(Ecore_List *list, Ecore_For_Each function, + void *user_data); + EAPI void *ecore_list_first_goto(Ecore_List * list); + EAPI void *ecore_list_last_goto(Ecore_List * list); + EAPI void *ecore_list_index_goto(Ecore_List * list, int index); + EAPI void *ecore_list_goto(Ecore_List * list, const void *_data); + + /* Traversing the list and returning data */ + EAPI void *ecore_list_next(Ecore_List * list); + EAPI void *ecore_list_find(Ecore_List *list, Ecore_Compare_Cb function, + const void *user_data); + + /* Sorting the list */ + EAPI int ecore_list_sort(Ecore_List *list, Ecore_Compare_Cb compare, + char order); + EAPI int ecore_list_mergesort(Ecore_List *list, Ecore_Compare_Cb compare, + char order); + EAPI int ecore_list_heapsort(Ecore_List *list, Ecore_Compare_Cb compare, + char order); + EAPI void ecore_list_merge(Ecore_List *list, Ecore_List *l2, + Ecore_Compare_Cb, char order); + + /* Check to see if there is any data in the list */ + EAPI int ecore_list_empty_is(Ecore_List * list); + + /* Remove every node in the list without freeing the list itself */ + EAPI int ecore_list_clear(Ecore_List * list); + /* Free the list and it's contents */ + EAPI void ecore_list_destroy(Ecore_List *list); + + /* Creating and initializing list nodes */ + EAPI Ecore_List_Node *ecore_list_node_new(void); + EAPI int ecore_list_node_init(Ecore_List_Node *newNode); + + /* Destroying nodes */ + EAPI int ecore_list_node_destroy(Ecore_List_Node * _e_node, Ecore_Free_Cb free_func); + + EAPI int ecore_list_free_cb_set(Ecore_List * list, Ecore_Free_Cb free_func); + + typedef Ecore_List Ecore_DList; +# define ECORE_DLIST(dlist) ((Ecore_DList *)dlist) + + typedef struct _ecore_dlist_node Ecore_DList_Node; +# define ECORE_DLIST_NODE(dlist) ((Ecore_DList_Node *)dlist) + + struct _ecore_dlist_node { + Ecore_List_Node single; + Ecore_DList_Node *previous; + }; + + /* Creating and initializing new list structures */ + EAPI Ecore_DList *ecore_dlist_new(void); + EAPI int ecore_dlist_init(Ecore_DList *list); + EAPI void ecore_dlist_destroy(Ecore_DList *list); + + /* Adding items to the list */ + EAPI int ecore_dlist_append(Ecore_DList * _e_dlist, void *_data); + EAPI int ecore_dlist_prepend(Ecore_DList * _e_dlist, void *_data); + EAPI int ecore_dlist_insert(Ecore_DList * _e_dlist, void *_data); + EAPI int ecore_dlist_append_list(Ecore_DList * _e_dlist, Ecore_DList * append); + EAPI int ecore_dlist_prepend_list(Ecore_DList * _e_dlist, Ecore_DList * prepend); + + /* Info about list's state */ +# define ecore_dlist_first(list) ecore_list_first(list) +# define ecore_dlist_last(list) ecore_list_last(list) + EAPI void *ecore_dlist_current(Ecore_DList *list); + EAPI int ecore_dlist_index(Ecore_DList *list); +# define ecore_dlist_count(list) ecore_list_count(list) + + /* Removing items from the list */ + EAPI void *ecore_dlist_remove(Ecore_DList * _e_dlist); + EAPI void *ecore_dlist_first_remove(Ecore_DList * _e_dlist); + EAPI int ecore_dlist_remove_destroy(Ecore_DList *list); + EAPI void *ecore_dlist_last_remove(Ecore_DList * _e_dlist); + + /* Traversing the list */ +# define ecore_dlist_for_each(list, function, user_data) \ + ecore_list_for_each(list, function, user_data) + EAPI void *ecore_dlist_first_goto(Ecore_DList * _e_dlist); + EAPI void *ecore_dlist_last_goto(Ecore_DList * _e_dlist); + EAPI void *ecore_dlist_index_goto(Ecore_DList * _e_dlist, int index); + EAPI void *ecore_dlist_goto(Ecore_DList * _e_dlist, void *_data); + + /* Traversing the list and returning data */ + EAPI void *ecore_dlist_next(Ecore_DList * list); + EAPI void *ecore_dlist_previous(Ecore_DList * list); + + /* Sorting the list */ + EAPI int ecore_dlist_sort(Ecore_DList *list, Ecore_Compare_Cb compare, + char order); + EAPI int ecore_dlist_mergesort(Ecore_DList *list, Ecore_Compare_Cb compare, + char order); +# define ecore_dlist_heapsort(list, compare, order) \ + ecore_list_heapsort(list, compare, order) + EAPI void ecore_dlist_merge(Ecore_DList *list, Ecore_DList *l2, + Ecore_Compare_Cb, char order); + + /* Check to see if there is any data in the list */ + EAPI int ecore_dlist_empty_is(Ecore_DList * _e_dlist); + + /* Remove every node in the list without free'ing it */ + EAPI int ecore_dlist_clear(Ecore_DList * _e_dlist); + + /* Creating and initializing list nodes */ + EAPI int ecore_dlist_node_init(Ecore_DList_Node * node); + EAPI Ecore_DList_Node *ecore_dlist_node_new(void); + + /* Destroying nodes */ + EAPI int ecore_dlist_node_destroy(Ecore_DList_Node * node, Ecore_Free_Cb free_func); + + EAPI int ecore_dlist_free_cb_set(Ecore_DList * dlist, Ecore_Free_Cb free_func); + + + + /* + * Hash Table Implementation: + * + * Traditional hash table implementation. I had tried a list of tables + * approach to save on the realloc's but it ended up being much slower than + * the traditional approach. + */ + + typedef struct _ecore_hash_node Ecore_Hash_Node; +# define ECORE_HASH_NODE(hash) ((Ecore_Hash_Node *)hash) + + struct _ecore_hash_node { + Ecore_Hash_Node *next; /* Pointer to the next node in the bucket list */ + void *key; /* The key for the data node */ + void *value; /* The value associated with this node */ + }; + + typedef struct _ecore_hash Ecore_Hash; +# define ECORE_HASH(hash) ((Ecore_Hash *)hash) + + struct _ecore_hash { + Ecore_Hash_Node **buckets; + int size; /* An index into the table of primes to + determine size */ + int nodes; /* The number of nodes currently in the hash */ + + int index; /* The current index into the bucket table */ + + Ecore_Compare_Cb compare; /* The function used to compare node values */ + Ecore_Hash_Cb hash_func; /* The callback function to determine hash */ + + Ecore_Free_Cb free_key; /* The callback function to free key */ + Ecore_Free_Cb free_value; /* The callback function to free value */ + }; + + /* Create and initialize a hash */ + EAPI Ecore_Hash *ecore_hash_new(Ecore_Hash_Cb hash_func, Ecore_Compare_Cb compare); + EAPI int ecore_hash_init(Ecore_Hash *hash, Ecore_Hash_Cb hash_func, Ecore_Compare_Cb compare); + + /* Functions related to freeing the data in the hash table */ + EAPI int ecore_hash_free_key_cb_set(Ecore_Hash *hash, Ecore_Free_Cb function); + EAPI int ecore_hash_free_value_cb_set(Ecore_Hash *hash, Ecore_Free_Cb function); + EAPI void ecore_hash_destroy(Ecore_Hash *hash); + + EAPI int ecore_hash_count(Ecore_Hash *hash); + EAPI int ecore_hash_for_each_node(Ecore_Hash *hash, Ecore_For_Each for_each_func, + void *user_data); + EAPI Ecore_List *ecore_hash_keys(Ecore_Hash *hash); + + /* Retrieve and store data into the hash */ + EAPI void *ecore_hash_get(Ecore_Hash *hash, const void *key); + EAPI int ecore_hash_set(Ecore_Hash *hash, void *key, void *value); + EAPI int ecore_hash_hash_set(Ecore_Hash *hash, Ecore_Hash *set); + EAPI void *ecore_hash_remove(Ecore_Hash *hash, const void *key); + EAPI void *ecore_hash_find(Ecore_Hash *hash, Ecore_Compare_Cb compare, const void *value); + EAPI void ecore_hash_dump_graph(Ecore_Hash *hash); + EAPI void ecore_hash_dump_stats(Ecore_Hash *hash); + + + typedef struct _ecore_heap Ecore_Sheap; +# define ECORE_HEAP(heap) ((Ecore_Sheap *)heap) + + struct _ecore_heap { + void **data; + int size; + int space; + + char order, sorted; + + /* Callback for comparing node values, default is direct comparison */ + Ecore_Compare_Cb compare; + + /* Callback for freeing node data, default is NULL */ + Ecore_Free_Cb free_func; + }; + + EAPI Ecore_Sheap *ecore_sheap_new(Ecore_Compare_Cb compare, int size); + EAPI void ecore_sheap_destroy(Ecore_Sheap *heap); + EAPI int ecore_sheap_init(Ecore_Sheap *heap, Ecore_Compare_Cb compare, int size); + EAPI int ecore_sheap_free_cb_set(Ecore_Sheap *heap, Ecore_Free_Cb free_func); + EAPI int ecore_sheap_insert(Ecore_Sheap *heap, void *data); + EAPI void *ecore_sheap_extract(Ecore_Sheap *heap); + EAPI void *ecore_sheap_extreme(Ecore_Sheap *heap); + EAPI int ecore_sheap_change(Ecore_Sheap *heap, void *item, void *newval); + EAPI int ecore_sheap_compare_set(Ecore_Sheap *heap, Ecore_Compare_Cb compare); + EAPI void ecore_sheap_order_set(Ecore_Sheap *heap, char order); + EAPI void ecore_sheap_sort(Ecore_Sheap *heap); + + EAPI void *ecore_sheap_item(Ecore_Sheap *heap, int i); + + + typedef struct _ecore_string Ecore_String; + struct _ecore_string { + char *string; + int references; + }; + + EAPI int ecore_string_init(); + EAPI void ecore_string_shutdown(); + EAPI const char *ecore_string_instance(const char *string); + EAPI void ecore_string_release(const char *string); + + typedef struct _Ecore_Tree_Node Ecore_Tree_Node; +# define ECORE_TREE_NODE(object) ((Ecore_Tree_Node *)object) + struct _Ecore_Tree_Node { + + /* The actual data for each node */ + void *key; + void *value; + + /* Pointers to surrounding nodes */ + Ecore_Tree_Node *parent; + Ecore_Tree_Node *left_child; + Ecore_Tree_Node *right_child; + + /* Book keeping information for quicker balancing of the tree */ + int max_right; + int max_left; + }; + + typedef struct _Ecore_Tree Ecore_Tree; +# define ECORE_TREE(object) ((Ecore_Tree *)object) + struct _Ecore_Tree { + /* Nodes of the tree */ + Ecore_Tree_Node *tree; + + /* Callback for comparing node values, default is direct comparison */ + Ecore_Compare_Cb compare_func; + + /* Callback for freeing node data, default is NULL */ + Ecore_Free_Cb free_value; + /* Callback for freeing node key, default is NULL */ + Ecore_Free_Cb free_key; + }; + + /* Some basic tree functions */ + /* Allocate and initialize a new tree */ + EAPI Ecore_Tree *ecore_tree_new(Ecore_Compare_Cb compare_func); + /* Initialize a new tree */ + EAPI int ecore_tree_init(Ecore_Tree * tree, Ecore_Compare_Cb compare_func); + + /* Free the tree */ + EAPI int ecore_tree_destroy(Ecore_Tree * tree); + /* Check to see if the tree has any nodes in it */ + EAPI int ecore_tree_empty_is(Ecore_Tree * tree); + + /* Retrieve the value associated with key */ + EAPI void *ecore_tree_get(Ecore_Tree * tree, const void *key); + EAPI Ecore_Tree_Node *ecore_tree_get_node(Ecore_Tree * tree, const void *key); + /* Retrieve the value of node with key greater than or equal to key */ + EAPI void *ecore_tree_closest_larger_get(Ecore_Tree * tree, const void *key); + /* Retrieve the value of node with key less than or equal to key */ + EAPI void *ecore_tree_closest_smaller_get(Ecore_Tree * tree, const void *key); + + /* Set the value associated with key to value */ + EAPI int ecore_tree_set(Ecore_Tree * tree, void *key, void *value); + /* Remove the key from the tree */ + EAPI int ecore_tree_remove(Ecore_Tree * tree, const void *key); + + /* Add a node to the tree */ + EAPI int ecore_tree_node_add(Ecore_Tree * tree, Ecore_Tree_Node * node); + /* Remove a node from the tree */ + EAPI int ecore_tree_node_remove(Ecore_Tree * tree, Ecore_Tree_Node * node); + + /* For each node in the tree perform the for_each_func function */ + /* For this one pass in the node */ + EAPI int ecore_tree_for_each_node(Ecore_Tree * tree, Ecore_For_Each for_each_func, + void *user_data); + /* And here pass in the node's value */ + EAPI int ecore_tree_for_each_node_value(Ecore_Tree * tree, + Ecore_For_Each for_each_func, + void *user_data); + + /* Some basic node functions */ + /* Initialize a node */ + EAPI int ecore_tree_node_init(Ecore_Tree_Node * new_node); + /* Allocate and initialize a new node */ + EAPI Ecore_Tree_Node *ecore_tree_node_new(void); + /* Free the desired node */ + EAPI int ecore_tree_node_destroy(Ecore_Tree_Node * node, + Ecore_Free_Cb free_value, Ecore_Free_Cb free_key); + + /* Set the node's key to key */ + EAPI int ecore_tree_node_key_set(Ecore_Tree_Node * node, void *key); + /* Retrieve the key in node */ + EAPI void *ecore_tree_node_key_get(Ecore_Tree_Node * node); + + /* Set the node's value to value */ + EAPI int ecore_tree_node_value_set(Ecore_Tree_Node * node, void *value); + /* Retrieve the value in node */ + EAPI void *ecore_tree_node_value_get(Ecore_Tree_Node * node); + + /* Add a function to free the data stored in nodes */ + EAPI int ecore_tree_free_value_cb_set(Ecore_Tree * tree, Ecore_Free_Cb free_value); + /* Add a function to free the keys stored in nodes */ + EAPI int ecore_tree_free_key_cb_set(Ecore_Tree * tree, Ecore_Free_Cb free_key); + + + EAPI Ecore_Strbuf * ecore_strbuf_new(void); + EAPI void ecore_strbuf_free(Ecore_Strbuf *buf); + EAPI void ecore_strbuf_append(Ecore_Strbuf *buf, const char *str); + EAPI void ecore_strbuf_append_char(Ecore_Strbuf *buf, char c); + EAPI void ecore_strbuf_insert(Ecore_Strbuf *buf, const char *str, + size_t pos); +# define ecore_strbuf_prepend(buf, str) ecore_strbuf_insert(buf, str, 0) + EAPI const char * ecore_strbuf_string_get(Ecore_Strbuf *buf); + EAPI size_t ecore_strbuf_length_get(Ecore_Strbuf *buf); + EAPI int ecore_strbuf_replace(Ecore_Strbuf *buf, const char *str, + const char *with, unsigned int n); +# define ecore_strbuf_replace_first(buf, str, with) \ + ecore_strbuf_replace(buf, str, with, 1) + EAPI int ecore_strbuf_replace_all(Ecore_Strbuf *buf, const char *str, + const char *with); + + extern int ecore_str_compare(const void *key1, const void *key2); + extern int ecore_direct_compare(const void *key1, const void *key2); + extern unsigned int ecore_str_hash(const void *key); + +#ifdef __cplusplus +} +#endif +#endif /* _ECORE_DATA_H */ diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index cb5d3d8..30d69b1 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -13,8 +13,7 @@ AM_CPPFLAGS = \ -DPACKAGE_BUILD_DIR=\"`pwd`/$(top_builddir)\" \ @CHECK_CFLAGS@ \ @GLIB_CFLAGS@ \ -@EVAS_CFLAGS@ \ -@ECORE_CFLAGS@ +@EVAS_CFLAGS@ if EINA_HAVE_GLIB @@ -28,12 +27,6 @@ AM_CPPFLAGS += -DEINA_BENCH_HAVE_EVAS endif -if EINA_HAVE_ECORE - -AM_CPPFLAGS += -DEINA_BENCH_HAVE_ECORE - -endif - if EINA_ENABLE_BENCHMARK_E17 AM_CPPFLAGS += -DEINA_ENABLE_BENCH_E17 @@ -96,9 +89,13 @@ eina_bench_convert.c \ eina_bench_mempool.c \ eina_bench_stringshare_e17.c \ eina_bench_array.c \ -eina_bench_rectangle_pool.c +eina_bench_rectangle_pool.c \ +ecore_list.c \ +ecore_strings.c \ +ecore_hash.c \ +ecore_sheap.c -eina_bench_LDADD = @ECORE_LIBS@ @EVAS_LIBS@ @GLIB_LIBS@ $(top_builddir)/src/lib/libeina.la +eina_bench_LDADD = @EVAS_LIBS@ @GLIB_LIBS@ $(top_builddir)/src/lib/libeina.la endif diff --git a/src/tests/ecore_hash.c b/src/tests/ecore_hash.c new file mode 100644 index 0000000..56caa3e --- /dev/null +++ b/src/tests/ecore_hash.c @@ -0,0 +1,918 @@ +/* + * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2 + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "Ecore_Data.h" + +#define PRIME_TABLE_MAX 21 +#define PRIME_MIN 17 +#define PRIME_MAX 16777213 + +#define ECORE_HASH_CHAIN_MAX 3 + +#define ECORE_COMPUTE_HASH(hash, key) hash->hash_func(key) % \ + ecore_prime_table[hash->size]; + +#define ECORE_HASH_INCREASE(hash) ((hash && ecore_prime_table[hash->size] < PRIME_MAX) ? \ + (hash->nodes / ecore_prime_table[hash->size]) > \ + ECORE_HASH_CHAIN_MAX : FALSE) +#define ECORE_HASH_REDUCE(hash) ((hash && ecore_prime_table[hash->size] > PRIME_MIN) ? \ + (double)hash->nodes / (double)ecore_prime_table[hash->size-1] \ + < ((double)ECORE_HASH_CHAIN_MAX * 0.375) : FALSE) + + +static const unsigned int ecore_prime_table[] = +{ + 17, 31, 61, 127, 257, 509, 1021, + 2053, 4093, 8191, 16381, 32771, 65537, 131071, 262147, 524287, 1048573, + 2097143, 4194301, 8388617, 16777213 +}; + + +/* Private hash manipulation functions */ +static int _ecore_hash_node_add(Ecore_Hash *hash, Ecore_Hash_Node *node); +static Ecore_Hash_Node * _ecore_hash_node_get(Ecore_Hash *hash, const void *key); +static int _ecore_hash_increase(Ecore_Hash *hash); +static int _ecore_hash_decrease(Ecore_Hash *hash); +static inline int _ecore_hash_rehash(Ecore_Hash *hash, Ecore_Hash_Node **old_table, int old_size); +static int _ecore_hash_bucket_destroy(Ecore_Hash_Node *list, Ecore_Free_Cb keyd, + Ecore_Free_Cb valued); +static inline Ecore_Hash_Node * _ecore_hash_bucket_get(Ecore_Hash *hash, + Ecore_Hash_Node *bucket, const void *key); + +static Ecore_Hash_Node *_ecore_hash_node_new(void *key, void *value); +static int _ecore_hash_node_init(Ecore_Hash_Node *node, void *key, void *value); +static int _ecore_hash_node_destroy(Ecore_Hash_Node *node, Ecore_Free_Cb keyd, + Ecore_Free_Cb valued); + +/** + * @defgroup Ecore_Data_Hash_ADT_Creation_Group Hash Creation Functions + * + * Functions that create hash tables. + */ + +/** + * Creates and initializes a new hash + * @param hash_func The function for determining hash position. + * @param compare The function for comparing node keys. + * @return @c NULL on error, a new hash on success. + * @ingroup Ecore_Data_Hash_ADT_Creation_Group + */ +EAPI Ecore_Hash * +ecore_hash_new(Ecore_Hash_Cb hash_func, Ecore_Compare_Cb compare) +{ + Ecore_Hash *new_hash = (Ecore_Hash *)malloc(sizeof(Ecore_Hash)); + if (!new_hash) + return NULL; + + if (!ecore_hash_init(new_hash, hash_func, compare)) + { + FREE(new_hash); + return NULL; + } + + return new_hash; +} + +/** + * Initializes the given hash. + * @param hash The given hash. + * @param hash_func The function used for hashing node keys. + * @param compare The function used for comparing node keys. + * @return @c TRUE on success, @c FALSE on an error. + * @ingroup Ecore_Data_Hash_ADT_Creation_Group + */ +EAPI int +ecore_hash_init(Ecore_Hash *hash, Ecore_Hash_Cb hash_func, Ecore_Compare_Cb compare) +{ + CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE); + + memset(hash, 0, sizeof(Ecore_Hash)); + + hash->hash_func = hash_func; + hash->compare = compare; + + hash->buckets = (Ecore_Hash_Node **)calloc(ecore_prime_table[0], + sizeof(Ecore_Hash_Node *)); + + return TRUE; +} + +/** + * @defgroup Ecore_Data_Hash_ADT_Destruction_Group Hash Destruction Functions + * + * Functions that destroy hash tables and their contents. + */ + +/** + * Sets the function to destroy the keys of the given hash. + * @param hash The given hash. + * @param function The function used to free the node keys. NULL is a + * valid value and means that no function will be called. + * @return @c TRUE on success, @c FALSE on error. + * @ingroup Ecore_Data_Hash_ADT_Destruction_Group + */ +EAPI int +ecore_hash_free_key_cb_set(Ecore_Hash *hash, Ecore_Free_Cb function) +{ + CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE); + + hash->free_key = function; + + return TRUE; +} + +/** + * Sets the function to destroy the values in the given hash. + * @param hash The given hash. + * @param function The function that will free the node values. NULL is a + * valid value and means that no function will be called. + * @return @c TRUE on success, @c FALSE on error + * @ingroup Ecore_Data_Hash_ADT_Destruction_Group + */ +EAPI int +ecore_hash_free_value_cb_set(Ecore_Hash *hash, Ecore_Free_Cb function) +{ + CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE); + + hash->free_value = function; + + return TRUE; +} + +/** + * @defgroup Ecore_Data_Hash_ADT_Data_Group Hash Data Functions + * + * Functions that set, access and delete values from the hash tables. + */ + +/** + * Sets a key-value pair in the given hash table. + * @param hash The given hash table. + * @param key The key. + * @param value The value. + * @return @c TRUE if successful, @c FALSE if not. + * @ingroup Ecore_Data_Hash_ADT_Data_Group + */ +EAPI int +ecore_hash_set(Ecore_Hash *hash, void *key, void *value) +{ + int ret = FALSE; + Ecore_Hash_Node *node; + + CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE); + + node = _ecore_hash_node_get(hash, key); + if (node) + { + if (hash->free_key) hash->free_key(key); + if (node->value && hash->free_value) hash->free_value(node->value); + node->value = value; + ret = TRUE; + } + else + { + node = _ecore_hash_node_new(key, value); + if (node) + ret = _ecore_hash_node_add(hash, node); + } + + return ret; +} + +/** + * Sets all key-value pairs from set in the given hash table. + * @param hash The given hash table. + * @param set The hash table to import. + * @return @c TRUE if successful, @c FALSE if not. + * @ingroup Ecore_Data_Hash_ADT_Data_Group + */ +EAPI int +ecore_hash_hash_set(Ecore_Hash *hash, Ecore_Hash *set) +{ + unsigned int i; + Ecore_Hash_Node *node, *old; + + CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE); + CHECK_PARAM_POINTER_RETURN("set", set, FALSE); + + for (i = 0; i < ecore_prime_table[set->size]; i++) + { + /* Hash into a new list to avoid loops of rehashing the same nodes */ + while ((old = set->buckets[i])) + { + set->buckets[i] = old->next; + old->next = NULL; + node = _ecore_hash_node_get(hash, old->key); + if (node) + { + /* This key already exists. Delete the old and add the new + * value */ + if (hash->free_key) hash->free_key(node->key); + if (hash->free_value) hash->free_key(node->value); + node->key = old->key; + node->value = old->value; + free(old); + } + else + _ecore_hash_node_add(hash, old); + } + } + FREE(set->buckets); + ecore_hash_init(set, set->hash_func, set->compare); + return TRUE; +} + +/** + * Frees the hash table and the data contained inside it. + * @param hash The hash table to destroy. + * @return @c TRUE on success, @c FALSE on error. + * @ingroup Ecore_Data_Hash_ADT_Destruction_Group + */ +EAPI void +ecore_hash_destroy(Ecore_Hash *hash) +{ + unsigned int i = 0; + + CHECK_PARAM_POINTER("hash", hash); + + if (hash->buckets) + { + while (i < ecore_prime_table[hash->size]) + { + if (hash->buckets[i]) + { + Ecore_Hash_Node *bucket; + + /* + * Remove the bucket list to avoid possible recursion + * on the free callbacks. + */ + bucket = hash->buckets[i]; + hash->buckets[i] = NULL; + _ecore_hash_bucket_destroy(bucket, + hash->free_key, + hash->free_value); + } + i++; + } + + FREE(hash->buckets); + } + FREE(hash); + + return; +} + +/** + * @defgroup Ecore_Data_Hash_ADT_Traverse_Group Hash Traverse Functions + * + * Functions that iterate through hash tables. + */ + +/** + * Counts the number of nodes in a hash table. + * @param hash The hash table to count current nodes. + * @return The number of nodes in the hash. + * @ingroup Ecore_Data_Hash_ADT_Destruction_Group + */ +EAPI int +ecore_hash_count(Ecore_Hash *hash) +{ + CHECK_PARAM_POINTER_RETURN("hash", hash, 0); + + return hash->nodes; +} + +/** + * Runs the @p for_each_func function on each entry in the given hash. + * @param hash The given hash. + * @param for_each_func The function that each entry is passed to. + * @param user_data a pointer passed to calls of for_each_func + * @return TRUE on success, FALSE otherwise. + * @ingroup Ecore_Data_Hash_ADT_Traverse_Group + */ +EAPI int +ecore_hash_for_each_node(Ecore_Hash *hash, Ecore_For_Each for_each_func, void *user_data) +{ + unsigned int i = 0; + + CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE); + CHECK_PARAM_POINTER_RETURN("for_each_func", for_each_func, FALSE); + + while (i < ecore_prime_table[hash->size]) + { + if (hash->buckets[i]) + { + Ecore_Hash_Node *node; + + for (node = hash->buckets[i]; node; node = node->next) + { + for_each_func(node, user_data); + } + } + i++; + } + + return TRUE; +} + +/** + * Retrieves an ecore_list of all keys in the given hash. + * @param hash The given hash. + * @return new ecore_list on success, NULL otherwise + * @ingroup Ecore_Data_Hash_ADT_Traverse_Group + */ +EAPI Ecore_List * +ecore_hash_keys(Ecore_Hash *hash) +{ + unsigned int i = 0; + Ecore_List *keys; + + CHECK_PARAM_POINTER_RETURN("hash", hash, NULL); + + keys = ecore_list_new(); + while (i < ecore_prime_table[hash->size]) + { + if (hash->buckets[i]) + { + Ecore_Hash_Node *node; + + for (node = hash->buckets[i]; node; node = node->next) + { + ecore_list_append(keys, node->key); + } + } + i++; + } + ecore_list_first_goto(keys); + + return keys; +} + +/** + * Prints the distribution of the given hash table for graphing. + * @param hash The given hash table. + */ +EAPI void +ecore_hash_dump_graph(Ecore_Hash *hash) +{ + unsigned int i; + + for (i = 0; i < ecore_prime_table[hash->size]; i++) + if (hash->buckets[i]) + { + int n = 0; + Ecore_Hash_Node *node; + for (node = hash->buckets[i]; node; node = node->next) + n++; + printf("%d\t%u", i, n); + } + else + printf("%d\t0", i); +} + +/** + * Prints the distribution of the given hash table for graphing. + * @param hash The given hash table. + */ +EAPI void +ecore_hash_dump_stats(Ecore_Hash *hash) +{ + unsigned int i; + double variance, sum_n_2 = 0, sum_n = 0; + + for (i = 0; i < ecore_prime_table[hash->size]; i++) + { + if (hash->buckets[i]) + { + int n = 0; + Ecore_Hash_Node *node; + for (node = hash->buckets[i]; node; node = node->next) + n++; + sum_n_2 += ((double)n * (double)n); + sum_n += (double)n; + } + } + variance = (sum_n_2 - ((sum_n * sum_n) / (double)i)) / (double)i; + printf("Average length: %f\n\tvariance^2: %f", (sum_n / (double)i), + variance); +} + +static int +_ecore_hash_bucket_destroy(Ecore_Hash_Node *list, Ecore_Free_Cb keyd, Ecore_Free_Cb valued) +{ + Ecore_Hash_Node *node; + + CHECK_PARAM_POINTER_RETURN("list", list, FALSE); + + for (node = list; node; node = list) + { + list = list->next; + _ecore_hash_node_destroy(node, keyd, valued); + } + + return TRUE; +} + +/* + * @brief Add the node to the hash table + * @param hash: the hash table to add the key + * @param node: the node to add to the hash table + * @return Returns FALSE on error, TRUE on success + */ +static int +_ecore_hash_node_add(Ecore_Hash *hash, Ecore_Hash_Node *node) +{ + unsigned long hash_val; + + CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE); + CHECK_PARAM_POINTER_RETURN("node", node, FALSE); + + /* Check to see if the hash needs to be resized */ + if (ECORE_HASH_INCREASE(hash)) + _ecore_hash_increase(hash); + + /* Compute the position in the table */ + if (!hash->hash_func) + hash_val = (unsigned long)node->key % ecore_prime_table[hash->size]; + else + hash_val = ECORE_COMPUTE_HASH(hash, node->key); + + /* Prepend the node to the list at the index position */ + node->next = hash->buckets[hash_val]; + hash->buckets[hash_val] = node; + hash->nodes++; + + return TRUE; +} + +/** + * Retrieves the value associated with the given key from the given hash + * table. + * @param hash The given hash table. + * @param key The key to search for. + * @return The value corresponding to key on success, @c NULL otherwise. + * @ingroup Ecore_Data_Hash_ADT_Data_Group + */ +EAPI void * +ecore_hash_get(Ecore_Hash *hash, const void *key) +{ + void *data; + Ecore_Hash_Node *node; + + CHECK_PARAM_POINTER_RETURN("hash", hash, NULL); + + node = _ecore_hash_node_get(hash, key); + if (!node) + return NULL; + + data = node->value; + + return data; +} + +/** + * Removes the value associated with the given key in the given hash + * table. + * @param hash The given hash table. + * @param key The key to search for. + * @return The value corresponding to the key on success. @c NULL is + * returned if there is an error. + * @ingroup Ecore_Data_Hash_ADT_Data_Group + */ +EAPI void * +ecore_hash_remove(Ecore_Hash *hash, const void *key) +{ + Ecore_Hash_Node *node = NULL; + Ecore_Hash_Node *list; + unsigned long hash_val; + void *ret = NULL; + + CHECK_PARAM_POINTER_RETURN("hash", hash, NULL); + + /* Compute the position in the table */ + if (!hash->hash_func) + hash_val = (unsigned long )key % ecore_prime_table[hash->size]; + else + hash_val = ECORE_COMPUTE_HASH(hash, key); + + /* + * If their is a list that could possibly hold the key/value pair + * traverse it and remove the hash node. + */ + if (hash->buckets[hash_val]) + { + list = hash->buckets[hash_val]; + + /* + * Traverse the list to find the specified key + */ + node = list; + if (hash->compare) + { + while ((node) && (hash->compare(node->key, key) != 0)) + { + list = node; + node = node->next; + } + } + else + { + while ((node) && (node->key != key)) + { + list = node; + node = node->next; + } + } + + /* + * Remove the node with the matching key and free it's memory + */ + if (node) + { + if (list == node) + hash->buckets[hash_val] = node->next; + else + list->next = node->next; + ret = node->value; + node->value = NULL; + _ecore_hash_node_destroy(node, hash->free_key, NULL); + hash->nodes--; + } + } + + if (ECORE_HASH_REDUCE(hash)) + _ecore_hash_decrease(hash); + + return ret; +} + +/** + * Retrieves the first value that matches + * table. + * @param hash The given hash table. + * @param key The key to search for. + * @return The value corresponding to key on success, @c NULL otherwise. + * @ingroup Ecore_Data_Hash_ADT_Data_Group + */ +EAPI void * +ecore_hash_find(Ecore_Hash *hash, Ecore_Compare_Cb compare, const void *value) +{ + unsigned int i = 0; + + CHECK_PARAM_POINTER_RETURN("hash", hash, NULL); + CHECK_PARAM_POINTER_RETURN("compare", compare, NULL); + CHECK_PARAM_POINTER_RETURN("value", value, NULL); + + while (i < ecore_prime_table[hash->size]) + { + if (hash->buckets[i]) + { + Ecore_Hash_Node *node; + + for (node = hash->buckets[i]; node; node = node->next) + { + if (!compare(node->value, value)) return node->value; + } + } + i++; + } + + return NULL; +} + +/* + * @brief Retrieve the node associated with key + * @param hash: the hash table to search for the key + * @param key: the key to search for in the hash table + * @return Returns NULL on error, node corresponding to key on success + */ +static Ecore_Hash_Node * +_ecore_hash_node_get(Ecore_Hash *hash, const void *key) +{ + unsigned long hash_val; + Ecore_Hash_Node *node = NULL; + + CHECK_PARAM_POINTER_RETURN("hash", hash, NULL); + + if (!hash->buckets) + { + return NULL; + } + + /* Compute the position in the table */ + if (!hash->hash_func) + hash_val = (unsigned long)key % ecore_prime_table[hash->size]; + else + hash_val = ECORE_COMPUTE_HASH(hash, key); + + /* Grab the bucket at the specified position */ + if (hash->buckets[hash_val]) + { + node = _ecore_hash_bucket_get(hash, hash->buckets[hash_val], key); + /* + * Move matched node to the front of the list as it's likely + * to be searched for again soon. + */ + if (node && node != hash->buckets[hash_val]) + { + node->next = hash->buckets[hash_val]; + hash->buckets[hash_val] = node; + } + } + + return node; +} + +/* + * @brief Search the hash bucket for a specified key + * @param hash: the hash table to retrieve the comparison function + * @param bucket: the list to search for the key + * @param key: the key to search for in the list + * @return Returns NULL on error or not found, the found node on success + */ +static inline Ecore_Hash_Node * +_ecore_hash_bucket_get(Ecore_Hash *hash, Ecore_Hash_Node *bucket, const void *key) +{ + Ecore_Hash_Node *prev = NULL; + Ecore_Hash_Node *node = NULL; + + /* + * Traverse the list to find the desired node, if the node is in the + * list, then return the node. + */ + if (hash->compare) + { + for (node = bucket; node; node = node->next) + { + if (hash->compare(node->key, key) == 0) + break; + prev = node; + } + } + else + { + for (node = bucket; node; node = node->next) + { + if (node->key == key) + break; + prev = node; + } + } + + /* + * Remove node from the list to replace it at the beginning. + */ + if (node && prev) + { + prev->next = node->next; + node->next = NULL; + } + + return node; +} + +/* + * @brief Increase the size of the hash table by approx. 2 * current size + * @param hash: the hash table to increase the size of + * @return Returns TRUE on success, FALSE on error + */ +static int +_ecore_hash_increase(Ecore_Hash *hash) +{ + void *old; + + CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE); + + /* Max size reached so return FALSE */ + if ((ecore_prime_table[hash->size] == PRIME_MAX) || (hash->size == PRIME_TABLE_MAX)) + return FALSE; + + /* + * Increase the size of the hash and save a pointer to the old data + */ + hash->size++; + old = hash->buckets; + + /* + * Allocate a new bucket area, of the new larger size + */ + hash->buckets = calloc(ecore_prime_table[hash->size], sizeof(Ecore_Hash_Node *)); + + /* + * Make sure the allocation succeeded, if not replace the old data and + * return a failure. + */ + if (!hash->buckets) + { + hash->buckets = old; + hash->size--; + return FALSE; + } + hash->nodes = 0; + + /* + * Now move all of the old data into the new bucket area + */ + if (_ecore_hash_rehash(hash, old, hash->size - 1)) + { + FREE(old); + return TRUE; + } + + /* + * Free the old buckets regardless of success. + */ + FREE(old); + + return FALSE; +} + +/* + * @brief Decrease the size of the hash table by < 1/2 * current size + * @param hash: the hash table to decrease the size of + * @return Returns TRUE on success, FALSE on error + */ +static int +_ecore_hash_decrease(Ecore_Hash *hash) +{ + Ecore_Hash_Node **old; + + CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE); + + if (ecore_prime_table[hash->size] == PRIME_MIN) + return FALSE; + + /* + * Decrease the hash size and store a pointer to the old data + */ + hash->size--; + old = hash->buckets; + + /* + * Allocate a new area to store the data + */ + hash->buckets = (Ecore_Hash_Node **)calloc(ecore_prime_table[hash->size], + sizeof(Ecore_Hash_Node *)); + + /* + * Make sure allocation succeeded otherwise rreturn to the previous + * state + */ + if (!hash->buckets) + { + hash->buckets = old; + hash->size++; + return FALSE; + } + + hash->nodes = 0; + + if (_ecore_hash_rehash(hash, old, hash->size + 1)) + { + FREE(old); + return TRUE; + } + + return FALSE; +} + +/* + * @brief Rehash the nodes of a table into the hash table + * @param hash: the hash to place the nodes of the table + * @param table: the table to remove the nodes from and place in hash + * @return Returns TRUE on success, FALSE on error + */ +static inline int +_ecore_hash_rehash(Ecore_Hash *hash, Ecore_Hash_Node **old_table, int old_size) +{ + unsigned int i; + Ecore_Hash_Node *old; + + CHECK_PARAM_POINTER_RETURN("hash", hash, FALSE); + CHECK_PARAM_POINTER_RETURN("old_table", old_table, FALSE); + + for (i = 0; i < ecore_prime_table[old_size]; i++) + { + /* Hash into a new list to avoid loops of rehashing the same nodes */ + while ((old = old_table[i])) + { + old_table[i] = old->next; + old->next = NULL; + _ecore_hash_node_add(hash, old); + } + } + + return TRUE; +} + +/* + * @brief Create a new hash node for key and value storage + * @param key: the key for this node + * @param value: the value that the key references + * @return Returns NULL on error, a new hash node on success + */ +static Ecore_Hash_Node * +_ecore_hash_node_new(void *key, void *value) +{ + Ecore_Hash_Node *node; + + node = (Ecore_Hash_Node *)malloc(sizeof(Ecore_Hash_Node)); + if (!node) + return NULL; + + if (!_ecore_hash_node_init(node, key, value)) + { + FREE(node); + return NULL; + } + + return node; +} + +/* + * @brief Initialize a hash node to some sane default values + * @param node: the node to set the values + * @param key: the key to reference this node + * @param value: the value that key refers to + * @return Returns TRUE on success, FALSE on error + */ +static int +_ecore_hash_node_init(Ecore_Hash_Node *node, void *key, void *value) +{ + CHECK_PARAM_POINTER_RETURN("node", node, FALSE); + + node->key = key; + node->value = value; + + return TRUE; +} + +/* + * @brief Destroy a node and call the specified callbacks to free data + * @param node: the node to be destroyed + * @param keyd: the function to free the key + * @param valued: the function to free the value + * @return Returns TRUE on success, FALSE on error + */ +static int +_ecore_hash_node_destroy(Ecore_Hash_Node *node, Ecore_Free_Cb keyd, Ecore_Free_Cb valued) +{ + CHECK_PARAM_POINTER_RETURN("node", node, FALSE); + + if (keyd) + keyd(node->key); + + if (valued) + valued(node->value); + + FREE(node); + + return TRUE; +} + +int +ecore_str_compare(const void *key1, const void *key2) +{ + const char *k1, *k2; + + if (!key1 || !key2) + return ecore_direct_compare(key1, key2); + else if (key1 == key2) + return 0; + + k1 = key1; + k2 = key2; + + return strcmp(k1, k2); +} + +unsigned int +ecore_str_hash(const void *key) +{ + int i; + unsigned int mask; + unsigned int value = 0; + const char *k = key; + + if (!k) + return 0; + + mask = (sizeof(unsigned int) * 8) - 1; + + for (i = 0; k[i] != '\0'; i++) + { + value ^= ((unsigned int) k[i] << ((i * 5) & mask)); + } + + return value; +} diff --git a/src/tests/ecore_list.c b/src/tests/ecore_list.c new file mode 100644 index 0000000..487143f --- /dev/null +++ b/src/tests/ecore_list.c @@ -0,0 +1,2116 @@ +/* + * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2 + */ + +#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 index); + +/* 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 index); + +/** +@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 == NULL) + { + 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 == NULL) + 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 == NULL) + { + 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 index 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 index) +{ + void *ret; + + CHECK_PARAM_POINTER_RETURN("list", list, NULL); + + ret = _ecore_list_index_goto(list, index); + + 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 index) +{ + int i; + + if (!list) + return NULL; + + if (ecore_list_empty_is(list)) + return NULL; + + if (index > ecore_list_count(list) || index < 0) + return NULL; + + if (index < list->index) + { + _ecore_list_first_goto(list); + i = 0; + } + else + i = list->index; + + for (; i < index && _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 index; + Ecore_List_Node *node; + + if (!list) + return NULL; + + index = 0; + + node = list->first; + while (node && node->data) + { + Ecore_List_Node *next; + + if (node->data == data) + break; + + next = node->next; + + node = next; + + index++; + } + + if (!node) + return NULL; + + list->current = node; + list->index = index; + + 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)) != NULL) + 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)) != NULL) + { + 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 index 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 index) +{ + void *ret; + + CHECK_PARAM_POINTER_RETURN("list", list, NULL); + + ret = _ecore_dlist_index_goto(list, index); + + 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 index) +{ + int i, increment; + + if (!list) + return NULL; + + if (ecore_list_empty_is(ECORE_LIST(list))) + return NULL; + + if (index > ecore_list_count(ECORE_LIST(list)) || index < 0) + return NULL; + + if (ECORE_LIST(list)->index >= ECORE_LIST(list)->nodes) + _ecore_list_last_goto(ECORE_LIST(list)); + + if (index < ECORE_LIST(list)->index) + increment = -1; + else + increment = 1; + + for (i = ECORE_LIST(list)->index; i != index; 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); +} diff --git a/src/tests/ecore_sheap.c b/src/tests/ecore_sheap.c new file mode 100644 index 0000000..a39d358 --- /dev/null +++ b/src/tests/ecore_sheap.c @@ -0,0 +1,472 @@ +/* + * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2 + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "Ecore_Data.h" + +#define HEAP_INCREMENT 4096 + +#define PARENT(i) (i / 2) +#define LEFT(i) (2 * i) +#define RIGHT(i) (2 * i + 1) + +static void _ecore_sheap_heapify(Ecore_Sheap *heap, int i); +static void _ecore_sheap_update_data(Ecore_Sheap *heap); + +/** + * Allocate and initialize a new binary heap + * @param compare The function for comparing keys, NULL for direct comparison + * @param size The number of elements to allow in the heap + * @return A pointer to the newly allocated binary heap on success, NULL on + * failure. + */ +EAPI Ecore_Sheap * +ecore_sheap_new(Ecore_Compare_Cb compare, int size) +{ + Ecore_Sheap *heap = NULL; + + heap = (Ecore_Sheap *)malloc(sizeof(Ecore_Sheap)); + if (!heap) + return NULL; + memset(heap, 0, sizeof(Ecore_Sheap)); + + if (!ecore_sheap_init(heap, compare, size)) + { + FREE(heap); + return NULL; + } + + return heap; +} + +/** + * Initialize a binary heap to default values + * @param heap The heap to initialize + * @param compare The function for comparing keys, NULL for direct comparison + * @param size The number of elements to allow in the heap + * @return TRUE on success, FALSE on failure + */ +EAPI int +ecore_sheap_init(Ecore_Sheap *heap, Ecore_Compare_Cb compare, int size) +{ + CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE); + + heap->space = size; + if (!compare) + heap->compare = ecore_direct_compare; + else + heap->compare = compare; + heap->order = ECORE_SORT_MIN; + + heap->data = (void **)malloc(heap->space * sizeof(void *)); + if (!heap->data) + return FALSE; + memset(heap->data, 0, heap->space * sizeof(void *)); + + return TRUE; +} + +/** + * Free up the memory used by the heap + * + * Frees the memory used by @a heap, calls the destroy function on each data + * item if necessary. + * + * @param heap The heap to be freed + */ +EAPI void +ecore_sheap_destroy(Ecore_Sheap *heap) +{ + int i; + + CHECK_PARAM_POINTER("heap", heap); + + /* + * Free data in heap + */ + if (heap->free_func) + for (i = 0; i < heap->size; i++) + heap->free_func(heap->data[i]); + + FREE(heap->data); + + FREE(heap); +} + +/** + * Set the function for freeing data. + * @param heap The heap 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_sheap_free_cb_set(Ecore_Sheap *heap, Ecore_Free_Cb free_func) +{ + CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE); + + heap->free_func = free_func; + + return TRUE; +} + +/** + * Insert new data into the heap. + * @param heap The heap to insert @a data. + * @param data The data to add to @a heap. + * @return TRUE on success, NULL on failure. Increases the size of the heap if + * it becomes larger than available space. + */ +EAPI int +ecore_sheap_insert(Ecore_Sheap *heap, void *data) +{ + int i; + void *temp; + int parent; + int position; + + CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE); + + /* + * Increase the size of the allocated data area if there isn't enough + * space available to add this data + */ + if (heap->size >= heap->space) + return FALSE; + + heap->sorted = FALSE; + + /* + * Place the data at the end of the heap initially. Then determine the + * parent and position in the array of it's parent. + */ + heap->data[heap->size] = data; + position = heap->size; + heap->size++; + i = heap->size; + parent = PARENT(i) - 1; + + /* + * Check the order of the heap to decide where to place the inserted + * data. The loop is placed inside the if statement to reduce the + * number of branching decisions that must be predicted. + */ + if (heap->order == ECORE_SORT_MIN) + { + while ((position > 0) && heap->compare(heap->data[parent], + heap->data[position]) > 0) + { + + /* + * Swap the data with it's parents to move it up in + * the heap. + */ + temp = heap->data[position]; + heap->data[position] = heap->data[parent]; + heap->data[parent] = temp; + + /* + * Now determine the new position for the next + * iteration of the loop, as well as it's parents + * position. + */ + i = PARENT(i); + position = i - 1; + parent = PARENT(i) - 1; + } + } + else + { + while ((position > 0) && heap->compare(heap->data[parent], + heap->data[position]) < 0) + { + + /* + * Swap the data with it's parents to move it up in + * the heap. + */ + temp = heap->data[position]; + heap->data[position] = heap->data[PARENT(i) - 1]; + heap->data[PARENT(i) - 1] = temp; + + /* + * Now determine the new position for the next + * iteration of the loop, as well as it's parents + * position. + */ + i = PARENT(i); + position = i - 1; + parent = PARENT(i) - 1; + } + } + + return TRUE; +} + +/** + * Extract the item at the top of the heap + * @param heap The heap to remove the top item + * @return The top item of the heap on success, NULL on failure. + * @note The extract function maintains the heap properties after the + * extract. + */ +EAPI void * +ecore_sheap_extract(Ecore_Sheap *heap) +{ + void *extreme; + + if (heap->size < 1) + return NULL; + + heap->sorted = FALSE; + + extreme = heap->data[0]; + heap->size--; + heap->data[0] = heap->data[heap->size]; + + _ecore_sheap_heapify(heap, 1); + + return extreme; +} + +/** + * Examine the item at the top of the heap + * @param heap The heap to examine the top item + * @return The top item of the heap on success, NULL on failure. + * @note The function does not alter the heap. + */ +EAPI void * +ecore_sheap_extreme(Ecore_Sheap *heap) +{ + if (heap->size < 1) + return NULL; + + return heap->data[0]; +} + +/** + * Change the value of the specified item in the heap + * @param heap The heap to search for the item to change + * @param item The item in the heap to change + * @param newval The new value assigned to the item in the heap + * @return TRUE on success, FALSE on failure. + * @note The heap does not free the old data since it must be passed + * in, so the caller can perform the free if desired. + */ +EAPI int +ecore_sheap_change(Ecore_Sheap *heap, void *item, void *newval) +{ + int i; + + CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE); + + for (i = 0; i < heap->size && heap->compare(heap->data[i], item); i++); + + if (i < heap->size) + heap->data[i] = newval; + else + return FALSE; + + /* + * FIXME: This is not the correct procedure when a change occurs. + */ + _ecore_sheap_heapify(heap, 1); + + return TRUE; +} + +/** + * Change the comparison function for the heap + * @param heap The heap to change comparison function + * @param compare The new function for comparing nodes + * @return TRUE on success, FALSE on failure. + * + * The comparison function is changed to @compare and the heap is heapified + * by the new comparison. + */ +EAPI int +ecore_sheap_compare_set(Ecore_Sheap *heap, Ecore_Compare_Cb compare) +{ + CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE); + + if (!compare) + heap->compare = ecore_direct_compare; + else + heap->compare = compare; + + _ecore_sheap_update_data(heap); + + return TRUE; +} + +/** + * Change the order of the heap + * @param heap The heap to change the order + * @param order The new order of the heap + * + * Changes the heap order of @heap and re-heapifies the data to this new + * order. The default order is a min heap. + */ +EAPI void +ecore_sheap_order_set(Ecore_Sheap *heap, char order) +{ + CHECK_PARAM_POINTER("heap", heap); + + heap->order = order; + + _ecore_sheap_update_data(heap); +} + +/** + * Sort the data in the heap + * @param heap The heap to be sorted + * + * Sorts the data in the heap into the order that is used for the heap's + * data. + */ +EAPI void +ecore_sheap_sort(Ecore_Sheap *heap) +{ + int i = 0; + void **new_data; + + CHECK_PARAM_POINTER("heap", heap); + + new_data = (void **)malloc(heap->size * sizeof(void *)); + + /* + * Extract the heap and insert into the new data array in order. + */ + while (heap->size > 0) + new_data[i++] = ecore_sheap_extract(heap); + + /* + * Free the old data array and update the heap with the new data, also + * mark as sorted. + */ + FREE(heap->data); + heap->data = new_data; + heap->size = i; + heap->sorted = TRUE; +} + +/* + * Access the item at the ith position in the heap + * @param heap The heap to access the internal data + * @param i The index of the data within the heap + * @return The data located at the ith position within @heap on success, + * NULL on failure. + * @note The data is guaranteed to be in sorted order. + */ +EAPI inline void * +ecore_sheap_item(Ecore_Sheap *heap, int i) +{ + if (i >= heap->size) + return NULL; + + /* + * Make sure the data is sorted so we return the correct value. + */ + if (!heap->sorted) + ecore_sheap_sort(heap); + + return heap->data[i]; +} + +/* + * Regain the heap properties starting at position i + * @param heap The heap to regain heap properties + * @param i The position to start heapifying + */ +static void +_ecore_sheap_heapify(Ecore_Sheap *heap, int i) +{ + int extreme; + int left = LEFT(i); + int right = RIGHT(i); + + if (heap->order == ECORE_SORT_MIN) + { + if (left <= heap->size && heap->compare(heap->data[left - 1], + heap->data[i - 1]) < 0) + extreme = left; + else + extreme = i; + + if (right <= heap->size && heap->compare(heap->data[right - 1], + heap->data[extreme - 1]) < 0) + extreme = right; + } + else + { + if (left <= heap->size && heap->compare(heap->data[left - 1], + heap->data[i - 1]) > 0) + extreme = left; + else + extreme = i; + + if (right <= heap->size && heap->compare(heap->data[right - 1], + heap->data[extreme - 1]) > 0) + extreme = right; + } + + /* + * If the data needs to be swapped down the heap, recurse on + * heapifying it's new placement. + */ + if (extreme != i) + { + void *temp; + + temp = heap->data[extreme - 1]; + heap->data[extreme - 1] = heap->data[i - 1]; + heap->data[i - 1] = temp; + + _ecore_sheap_heapify(heap, extreme); + } +} + +static void +_ecore_sheap_update_data(Ecore_Sheap *heap) +{ + int i, old_size; + void **data; + + /* + * Track the old values from the heap + */ + old_size = heap->size; + data = heap->data; + + heap->size = 0; + heap->data = malloc(heap->space * sizeof(void *)); + + for (i = 0; i < old_size; i++) + ecore_sheap_insert(heap, data[i]); + + FREE(data); +} + +int +ecore_direct_compare(const void *key1, const void *key2) +{ + unsigned long k1, k2; + + k1 = (unsigned long) key1; + k2 = (unsigned long) key2; + + if (k1 > k2) + return 1; + + if (k1 < k2) + return -1; + + return 0; +} diff --git a/src/tests/ecore_strings.c b/src/tests/ecore_strings.c new file mode 100644 index 0000000..7395e00 --- /dev/null +++ b/src/tests/ecore_strings.c @@ -0,0 +1,157 @@ +#include +#include + +#include "Ecore_Data.h" + +static void ecore_string_free_cb(void *data); + +static Ecore_Hash *ecore_strings = NULL; +static int ecore_string_init_count = 0; + +/** + * @defgroup Ecore_String_Group String Instance Functions + * + * These functions allow you to store one copy of a string, and use it + * throughout your program. + * + * This is a method to reduce the number of duplicated strings kept in + * memory. It's pretty common for the same strings to be dynamically + * allocated repeatedly between applications and libraries, especially in + * circumstances where you could have multiple copies of a structure that + * allocates the string. So rather than duplicating and freeing these + * strings, you request a read-only pointer to an existing string and + * only incur the overhead of a hash lookup. + * + * It sounds like micro-optimizing, but profiling has shown this can have + * a significant impact as you scale the number of copies up. It improves + * string creation/destruction speed, reduces memory use and decreases + * memory fragmentation, so a win all-around. + */ + +/** + * Initialize the ecore string internal structure. + * @return Zero on failure, non-zero on successful initialization. + */ +EAPI int +ecore_string_init() +{ + /* + * No strings have been loaded at this point, so create the hash + * table for storing string info for later. + */ + if (!ecore_string_init_count) + { + ecore_strings = ecore_hash_new(ecore_str_hash, ecore_str_compare); + if (!ecore_strings) + return 0; + ecore_hash_free_value_cb_set(ecore_strings, ecore_string_free_cb); + } + ecore_string_init_count++; + + return 1; +} + +/** + * Retrieves an instance of a string for use in an ecore program. + * @param string The string to retrieve an instance of. + * @return A pointer to an instance of the string on success. + * @c NULL on failure. + * @ingroup Ecore_String_Group + */ +EAPI const char * +ecore_string_instance(const char *string) +{ + Ecore_String *str; + + CHECK_PARAM_POINTER_RETURN("string", string, NULL); + + /* + * Check for a previous instance of the string, if not found, create + * it. + */ + str = ecore_hash_get(ecore_strings, string); + if (!str) + { + int length; + + /* + * Allocate and initialize a new string reference. + */ + length = strlen(string) + 1; + + str = (Ecore_String *)malloc(sizeof(Ecore_String) + length * sizeof(char)); + + str->string = (char*)(str + 1); + str->references = 0; + + memcpy(str->string, string, length); + + ecore_hash_set(ecore_strings, str->string, str); + } + + str->references++; + + return str->string; +} + +/** + * Notes that the given string has lost an instance. + * + * It will free the string if no other instances are left. + * + * @param string The given string. + * @ingroup Ecore_String_Group + */ +EAPI void +ecore_string_release(const char *string) +{ + Ecore_String *str; + + CHECK_PARAM_POINTER("string", string); + + str = ecore_hash_get(ecore_strings, (char *)string); + if (!str) + return; + + str->references--; + if (str->references < 1) + { + ecore_hash_remove(ecore_strings, (char *)string); + FREE(str); + } +} + +EAPI void +ecore_string_hash_dump_graph(void) +{ + ecore_hash_dump_graph(ecore_strings); +} + +EAPI void +ecore_string_hash_dump_stats(void) +{ + ecore_hash_dump_stats(ecore_strings); +} + +/** + * Shutdown the ecore string internal structures + */ +EAPI void +ecore_string_shutdown() +{ + --ecore_string_init_count; + if (!ecore_string_init_count) + { + ecore_hash_destroy(ecore_strings); + ecore_strings = NULL; + } +} + +static void +ecore_string_free_cb(void *data) +{ + Ecore_String *str; + + str = data; + FREE(str); +} diff --git a/src/tests/eina_bench_array.c b/src/tests/eina_bench_array.c index b2ebabf..87ee52a 100644 --- a/src/tests/eina_bench_array.c +++ b/src/tests/eina_bench_array.c @@ -32,10 +32,7 @@ # include #endif -#ifdef EINA_BENCH_HAVE_ECORE -# include -# include -#endif +# include "Ecore_Data.h" #include "eina_bench.h" #include "eina_array.h" @@ -633,8 +630,6 @@ eina_bench_evas_list_4evas_render(int request) #endif #endif -#ifdef EINA_BENCH_HAVE_ECORE -#if 0 static void _eina_ecore_for_each_remove(void *value, void *user_data) { @@ -661,7 +656,6 @@ eina_bench_ecore_list_4evas_render(int request) int i; int j; - ecore_init(); list = ecore_list_new(); ecore_list_free_cb_set(list, free); @@ -693,11 +687,7 @@ eina_bench_ecore_list_4evas_render(int request) } ecore_list_destroy(list); - - ecore_shutdown(); } -#endif -#endif void eina_bench_array(Eina_Benchmark *bench) { @@ -716,10 +706,6 @@ void eina_bench_array(Eina_Benchmark *bench) eina_benchmark_register(bench, "evas", EINA_BENCHMARK(eina_bench_evas_list_4evas_render), 200, 4000, 100); #endif #endif -#ifdef EINA_BENCH_HAVE_ECORE -#if 0 - eina_benchmark_register(bench, "ecore", EINA_BENCHMARK(eina_bench_ecore_list_4evas_render), 200, 4000, 100); -#endif -#endif + eina_benchmark_register(bench, "ecore", EINA_BENCHMARK(eina_bench_ecore_list_4evas_render), 200, 1000, 100); } diff --git a/src/tests/eina_bench_hash.c b/src/tests/eina_bench_hash.c index 268bf9a..d90abf7 100644 --- a/src/tests/eina_bench_hash.c +++ b/src/tests/eina_bench_hash.c @@ -33,9 +33,7 @@ # include #endif -#ifdef EINA_BENCH_HAVE_ECORE # include -#endif #include "eina_hash.h" #include "eina_array.h" @@ -332,7 +330,6 @@ eina_bench_lookup_evas(int request) #endif #endif -#ifdef EINA_BENCH_HAVE_ECORE typedef struct _Eina_Bench_Ecore Eina_Bench_Ecore; struct _Eina_Bench_Ecore { @@ -379,7 +376,6 @@ eina_bench_lookup_ecore(int request) ecore_hash_destroy(hash); } -#endif void eina_bench_hash(Eina_Benchmark *bench) { @@ -395,7 +391,5 @@ void eina_bench_hash(Eina_Benchmark *bench) eina_benchmark_register(bench, "evas-lookup", EINA_BENCHMARK(eina_bench_lookup_evas), 10, 3000, 10); #endif #endif -#ifdef EINA_BENCH_HAVE_ECORE eina_benchmark_register(bench, "ecore-lookup", EINA_BENCHMARK(eina_bench_lookup_ecore), 10, 3000, 10); -#endif } diff --git a/src/tests/eina_bench_sort.c b/src/tests/eina_bench_sort.c index bc6bc18..65caf2e 100644 --- a/src/tests/eina_bench_sort.c +++ b/src/tests/eina_bench_sort.c @@ -31,10 +31,7 @@ # include #endif -#ifdef EINA_BENCH_HAVE_ECORE -# include -# include -#endif +# include "Ecore_Data.h" #include "eina_bench.h" #include "Eina.h" @@ -137,14 +134,12 @@ eina_bench_sort_glist(int request) } #endif -#ifdef EINA_BENCH_HAVE_ECORE static void eina_bench_sort_ecore_default(int request) { Ecore_List *list = NULL; int i; - ecore_init(); list = ecore_list_new(); ecore_list_free_cb_set(list, free); @@ -160,8 +155,6 @@ eina_bench_sort_ecore_default(int request) ecore_list_sort(list, ECORE_COMPARE_CB(_eina_cmp_str), 0); ecore_list_destroy(list); - - ecore_shutdown(); } static void @@ -170,7 +163,6 @@ eina_bench_sort_ecore_merge(int request) Ecore_List *list = NULL; int i; - ecore_init(); list = ecore_list_new(); ecore_list_free_cb_set(list, free); @@ -186,8 +178,6 @@ eina_bench_sort_ecore_merge(int request) ecore_list_mergesort(list, ECORE_COMPARE_CB(_eina_cmp_str), 0); ecore_list_destroy(list); - - ecore_shutdown(); } static void @@ -196,7 +186,6 @@ eina_bench_sort_ecore_heap(int request) Ecore_List *list = NULL; int i; - ecore_init(); list = ecore_list_new(); ecore_list_free_cb_set(list, free); @@ -212,10 +201,7 @@ eina_bench_sort_ecore_heap(int request) ecore_list_heapsort(list, ECORE_COMPARE_CB(_eina_cmp_str), 0); ecore_list_destroy(list); - - ecore_shutdown(); } -#endif void eina_bench_sort(Eina_Benchmark *bench) { @@ -223,11 +209,9 @@ void eina_bench_sort(Eina_Benchmark *bench) #ifdef EINA_BENCH_HAVE_GLIB eina_benchmark_register(bench, "glist", EINA_BENCHMARK(eina_bench_sort_glist), 10, 10000, 100); #endif -#ifdef EINA_BENCH_HAVE_ECORE eina_benchmark_register(bench, "ecore", EINA_BENCHMARK(eina_bench_sort_ecore_default), 10, 10000, 100); eina_benchmark_register(bench, "ecore-merge", EINA_BENCHMARK(eina_bench_sort_ecore_merge), 10, 10000, 100); eina_benchmark_register(bench, "ecore-heap", EINA_BENCHMARK(eina_bench_sort_ecore_heap), 10, 10000, 100); -#endif #ifdef EINA_BENCH_HAVE_EVAS #if 0 eina_benchmark_register(bench, "evas", EINA_BENCHMARK(eina_bench_sort_evas), 10, 10000, 100); diff --git a/src/tests/eina_bench_stringshare.c b/src/tests/eina_bench_stringshare.c index 092d807..acea043 100644 --- a/src/tests/eina_bench_stringshare.c +++ b/src/tests/eina_bench_stringshare.c @@ -32,9 +32,7 @@ # include #endif -#ifdef EINA_BENCH_HAVE_ECORE -# include -#endif +# include "Ecore_Data.h" #include "eina_stringshare.h" #include "eina_bench.h" @@ -141,7 +139,6 @@ eina_bench_evas_job(int request) #endif #endif -#ifdef EINA_BENCH_HAVE_ECORE static void eina_bench_ecore_job(int request) { @@ -172,7 +169,6 @@ eina_bench_ecore_job(int request) ecore_string_shutdown(); } -#endif void eina_bench_stringshare(Eina_Benchmark *bench) { @@ -185,7 +181,5 @@ void eina_bench_stringshare(Eina_Benchmark *bench) eina_benchmark_register(bench, "stringshare (evas)", EINA_BENCHMARK(eina_bench_evas_job), 100, 20100, 500); #endif #endif -#ifdef EINA_BENCH_HAVE_ECORE eina_benchmark_register(bench, "stringshare (ecore)", EINA_BENCHMARK(eina_bench_ecore_job), 100, 20100, 500); -#endif }