From 74491e8781d87b4321470bea5414a3a750a695e8 Mon Sep 17 00:00:00 2001 From: Marcel Hollerbach Date: Fri, 10 Jan 2020 16:35:57 +0100 Subject: [PATCH] eina: introduce eina_iterator_process this brings a functional-map function to the iterator implementations. https://en.wikipedia.org/wiki/Map_(higher-order_function) Reviewed-by: Cedric BAIL Differential Revision: https://phab.enlightenment.org/D11062 --- src/lib/eina/eina_iterator.c | 59 +++++++++++++++++++++++++++++ src/lib/eina/eina_iterator.h | 18 +++++++++ src/lib/eina/eina_types.h | 12 ++++++ src/tests/eina/eina_test_iterator.c | 44 +++++++++++++++++++++ 4 files changed, 133 insertions(+) diff --git a/src/lib/eina/eina_iterator.c b/src/lib/eina/eina_iterator.c index 1043a9c6e0..26d05f4045 100644 --- a/src/lib/eina/eina_iterator.c +++ b/src/lib/eina/eina_iterator.c @@ -429,3 +429,62 @@ eina_iterator_filter_new(Eina_Iterator *iterator, Eina_Each_Cb filter, Eina_Free return &it->iterator; } + +typedef struct { + Eina_Iterator iterator; + + void *data; + Eina_Iterator *original; + Eina_Process_Cb cb; + Eina_Free_Cb free; +} Eina_Iterator_Processor; + +static Eina_Bool +eina_iterator_process_next(Eina_Iterator_Processor *it, void **data) +{ + if (!eina_iterator_next(it->original, data)) + return EINA_FALSE; + + *data = it->cb(it->original, *data, it->data); + + return EINA_TRUE; +} + +static void* +eina_iterator_process_get_container(Eina_Iterator_Processor *it) +{ + return it->original; +} + +static void +eina_iterator_process_free(Eina_Iterator_Processor *it) +{ + if (it->free) + it->free(it->data); + eina_iterator_free(it->original); + free(it); +} + +EAPI Eina_Iterator* +eina_iterator_processed_new(Eina_Iterator *iterator, Eina_Process_Cb process, Eina_Free_Cb free_cb, void *data) +{ + Eina_Iterator_Processor *it; + + EINA_SAFETY_ON_NULL_RETURN_VAL(iterator, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(process, NULL); + + it = calloc(1, sizeof(Eina_Iterator_Processor)); + it->data = data; + it->cb = process; + it->free = free_cb; + it->original = iterator; + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(eina_iterator_process_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(eina_iterator_process_get_container); + it->iterator.free = FUNC_ITERATOR_FREE(eina_iterator_process_free); + + return &it->iterator; +} diff --git a/src/lib/eina/eina_iterator.h b/src/lib/eina/eina_iterator.h index cb254c7e8e..21194d59c3 100644 --- a/src/lib/eina/eina_iterator.h +++ b/src/lib/eina/eina_iterator.h @@ -365,6 +365,24 @@ EAPI Eina_Iterator* eina_iterator_filter_new(Eina_Iterator *original, Eina_Each_ */ EAPI Eina_Iterator *eina_multi_iterator_internal_new(Eina_Iterator *it, ...) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +/** + * @brief Calls the process method on each node of iterator, producing new "processed" + * nodes and returning a new iterator which contains them. + * + * @param[in] original Iterator containing the nodes to process. + * @param[in] process Method to call on each node. + * @param[in] free_cb Method called when all nodes have been processed. It receives "data" as a parameter. + * @param[in] data Additional data passed to the process method. + * + * Processes every node in the input iterator and returns a new iterator containing + * the processed nodes. This is akin to a Map function: + * @see https://en.wikipedia.org/wiki/Map_(higher-order_function) + * + * @since 1.24 + */ +EAPI Eina_Iterator* eina_iterator_processed_new(Eina_Iterator *iterator, Eina_Process_Cb process, Eina_Free_Cb free_cb, void *fdata) EINA_WARN_UNUSED_RESULT; + /** * @def eina_multi_iterator_new * @brief Creates an Eina_Iterator that iterates through a series diff --git a/src/lib/eina/eina_types.h b/src/lib/eina/eina_types.h index c049baf4dd..ad797d3d91 100644 --- a/src/lib/eina/eina_types.h +++ b/src/lib/eina/eina_types.h @@ -539,6 +539,18 @@ typedef int (*Eina_Random_Cb)(const int min, const int max); */ #define EINA_RANDOM_CB(function) ((Eina_Random_Cb)function) +/** + * @typedef Eina_Process_Cb + * Method that processes some data and returns new data. + * It's meant to be used as a callback to process all nodes inside a container (See eina_iterator_processed_new, for example.) + */ +typedef void* (*Eina_Process_Cb)(const void *container, void *data, void *fdata); + +/** + * @def EINA_PROCESS_CB + * Macro to cast to Eina_Process. + */ +#define EINA_PROCESS_CB(Function) ((Eina_Process_Cb)Function) /** * @typedef Eina_Each_Cb * A callback type used when iterating over a container. diff --git a/src/tests/eina/eina_test_iterator.c b/src/tests/eina/eina_test_iterator.c index 28fe0dfc5b..8eb37f2488 100644 --- a/src/tests/eina/eina_test_iterator.c +++ b/src/tests/eina/eina_test_iterator.c @@ -654,6 +654,49 @@ EFL_START_TEST(eina_iterator_multi) } EFL_END_TEST +static void* +_return_x(const void *container EINA_UNUSED, void *data, void *fdata) +{ + Eina_Rectangle *rect = data; + ck_assert_int_eq(*((int*)fdata), 1337); + + return &rect->x; +} + +static void +_free_cb(void *data) +{ + int *free_data = data; + + *free_data = 0; +} + +EFL_START_TEST(eina_iterator_process) +{ + Eina_Inarray *rects = eina_inarray_new(sizeof(Eina_Rectangle), 5); + Eina_Rectangle rect_arr[] = {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}, {12, 13, 14, 15}, {16, 17, 18, 19}}; + Eina_Iterator *it; + int free = 1337; + int *a, i = 0; + + eina_inarray_push(rects, &rect_arr[0]); + eina_inarray_push(rects, &rect_arr[1]); + eina_inarray_push(rects, &rect_arr[2]); + eina_inarray_push(rects, &rect_arr[3]); + eina_inarray_push(rects, &rect_arr[4]); + + it = eina_iterator_processed_new(eina_inarray_iterator_new(rects), _return_x, _free_cb, &free); + EINA_ITERATOR_FOREACH(it, a) + { + ck_assert_int_eq(*a, i*4); + i++; + } + ck_assert_int_eq(i, 5); + eina_iterator_free(it); + ck_assert_int_eq(free, 0); +} +EFL_END_TEST + void eina_test_iterator(TCase *tc) { @@ -667,4 +710,5 @@ eina_test_iterator(TCase *tc) tcase_add_test(tc, eina_iterator_filter_free); tcase_add_test(tc, eina_iterator_carray_length); tcase_add_test(tc, eina_iterator_multi); + tcase_add_test(tc, eina_iterator_process); }