From e3cce7b79dc7f92f72bb99a242e07749cc251831 Mon Sep 17 00:00:00 2001 From: Gustavo Sverzut Barbieri Date: Fri, 27 Feb 2009 16:32:22 +0000 Subject: [PATCH] New macros, documentation and consistency for iterators and accessors. EINA_ITERATOR_FOREACH() and EINA_ACCESSOR_FOREACH() are new macros to help us forget about nasty C details (like cast to (void **)). Document most iterators and accessors. All iterators now set EINA_ERROR_OUT_OF_MEMORY if it's the case. SVN revision: 39267 --- legacy/eina/src/include/eina_accessor.h | 49 +++++++++++++++ legacy/eina/src/include/eina_iterator.h | 44 ++++++++++++++ legacy/eina/src/include/eina_list.h | 38 ++++++++++-- legacy/eina/src/lib/eina_hash.c | 80 +++++++++++++++++++++++-- legacy/eina/src/lib/eina_list.c | 18 ++++-- legacy/eina/src/lib/eina_rbtree.c | 69 ++++++++++++++++++++- 6 files changed, 283 insertions(+), 15 deletions(-) diff --git a/legacy/eina/src/include/eina_accessor.h b/legacy/eina/src/include/eina_accessor.h index 4c4015e3e9..6539bbe240 100644 --- a/legacy/eina/src/include/eina_accessor.h +++ b/legacy/eina/src/include/eina_accessor.h @@ -66,6 +66,55 @@ EAPI void eina_accessor_over (Eina_Accessor *accessor, unsigned int end, const void *fdata) EINA_ARG_NONNULL(1, 2); +/** + * @def EINA_ACCESSOR_FOREACH + * @brief Macro to iterate over all elements easily. + * + * @param accessor The accessor to use. + * @param data Where to store * data, must be a pointer support getting + * its address since * eina_accessor_data_get() requires a pointer + * to pointer! + * + * This macro is a convenient way to loop over all elements in an + * accessor, very similar to EINA_LIST_FOREACH(). + * + * This macro can be used for freeing the data of a list, like in the + * following example. It has the same goal as the one documented in + * EINA_LIST_FOREACH(), but using accessors: + * + * @code + * Eina_List *list; + * Eina_Accessor *accessor; + * unsigned int i; + * char *data; + * + * // list is already filled, + * // its elements are just duplicated strings + * + * accessor = eina_list_accessor_new(list); + * EINA_ACCESSOR_FOREACH(accessor, i, data) + * free(data); + * eina_accessor_free(accessor); + * eina_list_free(list); + * @endcode + * + * @note if the datatype provides both iterators and accessors prefer + * to use iterators to iterate over, as they're likely to be more + * optimized for such task. + * + * @note this example is not optimal algorithm to release a list since + * it will walk the list twice, but it serves as an example. For + * optimized version use EINA_LIST_FREE() + * + * @warning unless explicitly stated in functions returning accessors, + * do not modify the accessed object while you walk it, in this + * example using lists, do not remove list nodes or you might + * crash! This is not a limitiation of accessors themselves, + * rather in the accessors implementations to keep them as simple + * and fast as possible. + */ +#define EINA_ACCESSOR_FOREACH(accessor, counter, data) for ((counter) = 0; eina_accessor_data_get((accessor), (counter), (void **)&(data)); (counter)++) + /** * @} */ diff --git a/legacy/eina/src/include/eina_iterator.h b/legacy/eina/src/include/eina_iterator.h index ff0bbe38ba..7309b2bbff 100644 --- a/legacy/eina/src/include/eina_iterator.h +++ b/legacy/eina/src/include/eina_iterator.h @@ -64,6 +64,50 @@ EAPI void eina_iterator_foreach (Eina_Iterator *iterator, Eina_Each callback, const void *fdata) EINA_ARG_NONNULL(1, 2); +/** + * @def EINA_ITERATOR_FOREACH + * @brief Macro to iterate over all elements easily. + * + * @param iterator The iterator to use. + * @param data Where to store * data, must be a pointer support getting + * its address since * eina_iterator_next() requires a pointer + * to pointer! + * + * This macro is a convenient way to use iterators, very similar to + * EINA_LIST_FOREACH(). + * + * This macro can be used for freeing the data of a list, like in the + * following example. It has the same goal as the one documented in + * EINA_LIST_FOREACH(), but using iterators: + * + * @code + * Eina_List *list; + * Eina_Iterator *itr; + * char *data; + * + * // list is already filled, + * // its elements are just duplicated strings + * + * itr = eina_list_iterator_new(list); + * EINA_ITERATOR_FOREACH(itr, data) + * free(data); + * eina_iterator_free(itr); + * eina_list_free(list); + * @endcode + * + * @note this example is not optimal algorithm to release a list since + * it will walk the list twice, but it serves as an example. For + * optimized version use EINA_LIST_FREE() + * + * @warning unless explicitly stated in functions returning iterators, + * do not modify the iterated object while you walk it, in this + * example using lists, do not remove list nodes or you might + * crash! This is not a limitiation of iterators themselves, + * rather in the iterators implementations to keep them as simple + * and fast as possible. + */ +#define EINA_ITERATOR_FOREACH(itr, data) while (eina_iterator_next((itr), (void **)&(data))) + /** * @} */ diff --git a/legacy/eina/src/include/eina_list.h b/legacy/eina/src/include/eina_list.h index 59e9c96fa9..6caec87cbf 100644 --- a/legacy/eina/src/include/eina_list.h +++ b/legacy/eina/src/include/eina_list.h @@ -137,10 +137,16 @@ EAPI Eina_Accessor *eina_list_accessor_new(const Eina_List *list) EINA_MALLOC EI * * EINA_LIST_FOREACH(list, l, data) * free(data); + * eina_list_free(list); * @endcode * - * @warning do not delete list nodes, specially the current node, while - * iterating. If you wish to do so, use EINA_LIST_FOREACH_SAFE(). + * @note this example is not optimal algorithm to release a list since + * it will walk the list twice, but it serves as an example. For + * optimized version use EINA_LIST_FREE() + * + * @warning do not delete list nodes, specially the current node, + * while iterating. If you wish to do so, use + * EINA_LIST_FOREACH_SAFE(). */ #define EINA_LIST_FOREACH(list, l, data) for (l = list, data = eina_list_data_get(l); l; l = eina_list_next(l), data = eina_list_data_get(l)) @@ -171,10 +177,16 @@ EAPI Eina_Accessor *eina_list_accessor_new(const Eina_List *list) EINA_MALLOC EI * * EINA_LIST_REVERSE_FOREACH(list, l, data) * free(data); + * eina_list_free(list); * @endcode * - * @warning do not delete list nodes, specially the current node, while - * iterating. If you wish to do so, use EINA_LIST_REVERSE_FOREACH_SAFE(). + * @note this example is not optimal algorithm to release a list since + * it will walk the list twice, but it serves as an example. For + * optimized version use EINA_LIST_FREE() + * + * @warning do not delete list nodes, specially the current node, + * while iterating. If you wish to do so, use + * EINA_LIST_REVERSE_FOREACH_SAFE(). */ #define EINA_LIST_REVERSE_FOREACH(list, l, data) for (l = eina_list_last(list), data = eina_list_data_get(l); l; l = eina_list_prev(l), data = eina_list_data_get(l)) @@ -260,6 +272,24 @@ EAPI Eina_Accessor *eina_list_accessor_new(const Eina_List *list) EINA_MALLOC EI */ #define EINA_LIST_REVERSE_FOREACH_SAFE(list, l, l_prev, data) for (l = list, l_prev = eina_list_prev(l), data = eina_list_data_get(l); l; l = l_prev, l_prev = eina_list_prev(l), data = eina_list_data_get(l)) +/** + * Easy way to free the while list while being able to release its pointed data. + * + * @code + * Eina_List *list; + * char *data; + * + * // list is already filled, + * // its elements are just duplicated strings, + * + * EINA_LIST_FREE(list, data) + * free(data); + * @endcode + * + * If you do not need to release node data then use eina_list_free(). + * + * @see eina_list_free() + */ #define EINA_LIST_FREE(list, data) for (data = list ? eina_list_data_get(list) : NULL; list; list = eina_list_remove_list(list, list), data = list ? eina_list_data_get(list) : NULL) #include "eina_inline_list.x" diff --git a/legacy/eina/src/lib/eina_hash.c b/legacy/eina/src/lib/eina_hash.c index 56d36b4e8c..1dc2388788 100644 --- a/legacy/eina/src/lib/eina_hash.c +++ b/legacy/eina/src/lib/eina_hash.c @@ -277,7 +277,7 @@ _eina_hash_rbtree_each(__UNUSED__ const Eina_Rbtree *container, const Eina_Hash_ Eina_Bool found = EINA_TRUE; it = eina_rbtree_iterator_prefix(eh->head); - while (eina_iterator_next(it, (void**) &el)) + EINA_ITERATOR_FOREACH(it, el) { if (el->tuple.data == data->data) { @@ -1293,6 +1293,25 @@ eina_hash_foreach(const Eina_Hash *hash, eina_iterator_free(it); } +/** + * @brief Returned a new iterator asociated to hash data. + * + * @param hash The hash. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * hash. If @p hash is not populated, this function still returns a + * valid iterator that will always return false on + * eina_iterator_next(), thus keeping API sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the hash structure changes then the iterator becomes + * invalid! That is, if you add or remove items this iterator + * behavior is undefined and your program may crash! + */ EAPI Eina_Iterator * eina_hash_iterator_data_new(const Eina_Hash *hash) { @@ -1301,8 +1320,12 @@ eina_hash_iterator_data_new(const Eina_Hash *hash) EINA_MAGIC_CHECK_HASH(hash); EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); + eina_error_set(0); it = calloc(1, sizeof (Eina_Iterator_Hash)); - if (!it) return NULL; + if (!it) { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } it->hash = hash; it->get_content = FUNC_ITERATOR_GET_CONTENT(_eina_hash_iterator_data_get_content); @@ -1317,6 +1340,25 @@ eina_hash_iterator_data_new(const Eina_Hash *hash) return &it->iterator; } +/** + * @brief Returned a new iterator asociated to hash keys. + * + * @param hash The hash. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * hash. If @p hash is not populated, this function still returns a + * valid iterator that will always return false on + * eina_iterator_next(), thus keeping API sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the hash structure changes then the iterator becomes + * invalid! That is, if you add or remove items this iterator + * behavior is undefined and your program may crash! + */ EAPI Eina_Iterator * eina_hash_iterator_key_new(const Eina_Hash *hash) { @@ -1325,8 +1367,12 @@ eina_hash_iterator_key_new(const Eina_Hash *hash) EINA_MAGIC_CHECK_HASH(hash); EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); + eina_error_set(0); it = calloc(1, sizeof (Eina_Iterator_Hash)); - if (!it) return NULL; + if (!it) { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } it->hash = hash; it->get_content = FUNC_ITERATOR_GET_CONTENT(_eina_hash_iterator_key_get_content); @@ -1341,6 +1387,28 @@ eina_hash_iterator_key_new(const Eina_Hash *hash) return &it->iterator; } +/** + * @brief Returned a new iterator asociated to hash keys and data. + * + * @param hash The hash. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * hash. If @p hash is not populated, this function still returns a + * valid iterator that will always return false on + * eina_iterator_next(), thus keeping API sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @note iterator data will provide values as Eina_Hash_Tuple that should not + * be modified! + * + * @warning if the hash structure changes then the iterator becomes + * invalid! That is, if you add or remove items this iterator + * behavior is undefined and your program may crash! + */ EAPI Eina_Iterator * eina_hash_iterator_tuple_new(const Eina_Hash *hash) { @@ -1349,8 +1417,12 @@ eina_hash_iterator_tuple_new(const Eina_Hash *hash) EINA_MAGIC_CHECK_HASH(hash); EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); + eina_error_set(0); it = calloc(1, sizeof (Eina_Iterator_Hash)); - if (!it) return NULL; + if (!it) { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } it->hash = hash; it->get_content = FUNC_ITERATOR_GET_CONTENT(_eina_hash_iterator_tuple_get_content); diff --git a/legacy/eina/src/lib/eina_list.c b/legacy/eina/src/lib/eina_list.c index 3629b09b9f..637be0e85f 100644 --- a/legacy/eina/src/lib/eina_list.c +++ b/legacy/eina/src/lib/eina_list.c @@ -1555,11 +1555,19 @@ eina_list_search_unsorted(const Eina_List *list, Eina_Compare_Cb func, const voi * @param list The list. * @return A new iterator. * - * This function returns a newly allocated iterator associated to - * @p list. If @p list is @c NULL or the count member of @p list is - * less or equal than 0, this function returns NULL. If the memory can - * not be allocated, NULL is returned and #EINA_ERROR_OUT_OF_MEMORY is - * set. Otherwise, a valid iterator is returned. + * This function returns a newly allocated iterator associated to @p + * list. If @p list is @c NULL or the count member of @p list is less + * or equal than 0, this function still returns a valid iterator that + * will always return false on eina_iterator_next(), thus keeping API + * sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the list structure changes then the iterator becomes + * invalid! That is, if you add or remove nodes this iterator + * behavior is undefined and your program may crash! */ EAPI Eina_Iterator * eina_list_iterator_new(const Eina_List *list) diff --git a/legacy/eina/src/lib/eina_rbtree.c b/legacy/eina/src/lib/eina_rbtree.c index b245757a06..8354670180 100644 --- a/legacy/eina/src/lib/eina_rbtree.c +++ b/legacy/eina/src/lib/eina_rbtree.c @@ -62,8 +62,12 @@ _eina_rbtree_iterator_list_new(const Eina_Rbtree *tree) { Eina_Iterator_Rbtree_List *new; + eina_error_set(0); new = malloc(sizeof (Eina_Iterator_Rbtree_List)); - if (!new) return NULL; + if (!new) { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } new->tree = (Eina_Rbtree*) tree; new->dir = EINA_RBTREE_RIGHT; @@ -162,8 +166,12 @@ _eina_rbtree_iterator_build(const Eina_Rbtree *root, unsigned char mask) Eina_Iterator_Rbtree_List *first; Eina_Iterator_Rbtree *it; + eina_error_set(0); it = calloc(1, sizeof (Eina_Iterator_Rbtree)); - if (!it) return NULL; + if (!it) { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } it->stack = eina_array_new(8); if (!it->stack) goto on_error; @@ -437,18 +445,75 @@ eina_rbtree_inline_remove(Eina_Rbtree *root, Eina_Rbtree *node, Eina_Rbtree_Cmp_ return root; } +/** + * @brief Returned a new prefix iterator asociated to a rbtree. + * + * @param root The root of rbtree. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * root. It will iterate the tree using prefix walk. If @p root is @c + * NULL, this function still returns a valid iterator that will always + * return false on eina_iterator_next(), thus keeping API sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the rbtree structure changes then the iterator becomes + * invalid! That is, if you add or remove nodes this iterator + * behavior is undefined and your program may crash! + */ EAPI Eina_Iterator * eina_rbtree_iterator_prefix(const Eina_Rbtree *root) { return _eina_rbtree_iterator_build(root, EINA_RBTREE_ITERATOR_PREFIX_MASK); } +/** + * @brief Returned a new prefix iterator asociated to a rbtree. + * + * @param root The root of rbtree. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * root. It will iterate the tree using infix walk. If @p root is @c + * NULL, this function still returns a valid iterator that will always + * return false on eina_iterator_next(), thus keeping API sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the rbtree structure changes then the iterator becomes + * invalid! That is, if you add or remove nodes this iterator + * behavior is undefined and your program may crash! + */ EAPI Eina_Iterator * eina_rbtree_iterator_infix(const Eina_Rbtree *root) { return _eina_rbtree_iterator_build(root, EINA_RBTREE_ITERATOR_INFIX_MASK); } +/** + * @brief Returned a new prefix iterator asociated to a rbtree. + * + * @param root The root of rbtree. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * root. It will iterate the tree using postfix walk. If @p root is @c + * NULL, this function still returns a valid iterator that will always + * return false on eina_iterator_next(), thus keeping API sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the rbtree structure changes then the iterator becomes + * invalid! That is, if you add or remove nodes this iterator + * behavior is undefined and your program may crash! + */ EAPI Eina_Iterator * eina_rbtree_iterator_postfix(const Eina_Rbtree *root) {