From: antognolli Date: Tue, 14 Jun 2011 17:43:53 +0000 (+0000) Subject: eina: Add detailed description and some examples to Eina_Inlist. X-Git-Tag: 2.0_alpha~69^2~50 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9a506cc3aa48193f16964385e57e04904eb82039;p=framework%2Fuifw%2Feina.git eina: Add detailed description and some examples to Eina_Inlist. git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/eina@60310 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33 --- diff --git a/doc/img/eina_inlist-node.png b/doc/img/eina_inlist-node.png new file mode 100644 index 0000000..195497b Binary files /dev/null and b/doc/img/eina_inlist-node.png differ diff --git a/doc/img/eina_inlist-node_eg1-inlist.png b/doc/img/eina_inlist-node_eg1-inlist.png new file mode 100644 index 0000000..48c0a47 Binary files /dev/null and b/doc/img/eina_inlist-node_eg1-inlist.png differ diff --git a/doc/img/eina_inlist-node_eg1-my-struct.png b/doc/img/eina_inlist-node_eg1-my-struct.png new file mode 100644 index 0000000..4393f2c Binary files /dev/null and b/doc/img/eina_inlist-node_eg1-my-struct.png differ diff --git a/doc/img/eina_inlist-node_eg2-list-inlist.png b/doc/img/eina_inlist-node_eg2-list-inlist.png new file mode 100644 index 0000000..4eb3235 Binary files /dev/null and b/doc/img/eina_inlist-node_eg2-list-inlist.png differ diff --git a/doc/img/eina_inlist-node_eg2-my-struct.png b/doc/img/eina_inlist-node_eg2-my-struct.png new file mode 100644 index 0000000..9183197 Binary files /dev/null and b/doc/img/eina_inlist-node_eg2-my-struct.png differ diff --git a/doc/img/eina_inlist-node_eg3-my-struct.png b/doc/img/eina_inlist-node_eg3-my-struct.png new file mode 100644 index 0000000..f669afd Binary files /dev/null and b/doc/img/eina_inlist-node_eg3-my-struct.png differ diff --git a/doc/img/eina_inlist-node_eg3-two-inlists.png b/doc/img/eina_inlist-node_eg3-two-inlists.png new file mode 100644 index 0000000..c8d2c8b Binary files /dev/null and b/doc/img/eina_inlist-node_eg3-two-inlists.png differ diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am index fb0fa2f..e8b14b4 100644 --- a/src/examples/Makefile.am +++ b/src/examples/Makefile.am @@ -20,7 +20,10 @@ SRCS = \ eina_list_01.c \ eina_list_02.c \ eina_list_03.c \ - eina_list_04.c + eina_list_04.c \ + eina_inlist_01.c \ + eina_inlist_02.c \ + eina_inlist_03.c pkglib_PROGRAMS = @@ -40,6 +43,9 @@ pkglib_PROGRAMS += \ eina_list_01 \ eina_list_02 \ eina_list_03 \ - eina_list_04 + eina_list_04 \ + eina_inlist_01 \ + eina_inlist_02 \ + eina_inlist_03 endif diff --git a/src/examples/eina_inlist_01.c b/src/examples/eina_inlist_01.c new file mode 100644 index 0000000..fa51553 --- /dev/null +++ b/src/examples/eina_inlist_01.c @@ -0,0 +1,96 @@ +// Compile with: +// gcc -g `pkg-config --cflags --libs eina` eina_inlist_01.c -o eina_inlist_01 +#include +#include + +struct my_struct { + EINA_INLIST; + int a, b; +}; + +int +sort_cb(const void *d1, const void *d2) +{ + const Eina_Inlist *l1, *l2; + const struct my_struct *x1, *x2; + + l1 = d1; + l2 = d2; + + x1 = EINA_INLIST_CONTAINER_GET(l1, struct my_struct); + x2 = EINA_INLIST_CONTAINER_GET(l2, struct my_struct); + + return x1->a - x2->a; +} + +int +main(void) +{ + struct my_struct *d, *cur; + Eina_Inlist *list, *itr, *tmp; + + eina_init(); + + d = malloc(sizeof(*d)); + d->a = 1; + d->b = 10; + list = eina_inlist_append(NULL, EINA_INLIST_GET(d)); + + d = malloc(sizeof(*d)); + d->a = 2; + d->b = 20; + list = eina_inlist_append(list, EINA_INLIST_GET(d)); + + d = malloc(sizeof(*d)); + d->a = 3; + d->b = 30; + list = eina_inlist_prepend(list, EINA_INLIST_GET(d)); + + printf("list=%p\n", list); + EINA_INLIST_FOREACH(list, cur) + printf("\ta=%d, b=%d\n", cur->a, cur->b); + + list = eina_inlist_promote(list, EINA_INLIST_GET(d)); + + d = malloc(sizeof(*d)); + d->a = 4; + d->b = 40; + list = eina_inlist_append_relative(list, EINA_INLIST_GET(d), list); + + list = eina_inlist_demote(list, EINA_INLIST_GET(d)); + + list = eina_inlist_sort(list, sort_cb); + + printf("list after sort=%p\n", list); + EINA_INLIST_FOREACH(list, cur) + printf("\ta=%d, b=%d\n", cur->a, cur->b); + + tmp = eina_inlist_find(list, EINA_INLIST_GET(d)); + if (tmp) + cur = EINA_INLIST_CONTAINER_GET(tmp, struct my_struct); + else + cur = NULL; + + if (d != cur) + printf("wrong node! cur=%p\n", cur); + + list = eina_inlist_remove(list, EINA_INLIST_GET(d)); + free(d); + printf("list=%p\n", list); + for (itr = list; itr != NULL; itr = itr->next) + { + cur = EINA_INLIST_CONTAINER_GET(itr, struct my_struct); + printf("\ta=%d, b=%d\n", cur->a, cur->b); + } + + while (list) + { + Eina_Inlist *aux = list; + list = eina_inlist_remove(list, list); + free(aux); + } + + eina_shutdown(); + + return 0; +} diff --git a/src/examples/eina_inlist_02.c b/src/examples/eina_inlist_02.c new file mode 100644 index 0000000..7d23f13 --- /dev/null +++ b/src/examples/eina_inlist_02.c @@ -0,0 +1,64 @@ +// Compile with: +// gcc -g `pkg-config --cflags --libs eina` eina_inlist_01.c -o eina_inlist_01 +#include +#include + +struct my_struct { + EINA_INLIST; + int a, b; +}; + +int +main(void) +{ + struct my_struct *d, *cur; + int i; + + Eina_Inlist *inlist = NULL; + Eina_List *list = NULL, *l_itr, *l_next; + + eina_init(); + + for (i = 0; i < 100; i++) + { + d = malloc(sizeof(*d)); + d->a = i; + d->b = i * 10; + inlist = eina_inlist_append(inlist, EINA_INLIST_GET(d)); + if ((i % 2) == 0) + list = eina_list_prepend(list, d); + } + + printf("inlist=%p\n", inlist); + EINA_INLIST_FOREACH(inlist, cur) + printf("\ta=%d, b=%d\n", cur->a, cur->b); + + printf("list=%p\n", list); + EINA_LIST_FOREACH(list, l_itr, cur) + printf("\ta=%d, b=%d\n", cur->a, cur->b); + + printf("inlist count=%d\n", eina_inlist_count(inlist)); + printf("list count=%d\n\n", eina_list_count(list)); + + EINA_LIST_FOREACH_SAFE(list, l_itr, l_next, cur) + { + if ((cur->a % 3) == 0) + list = eina_list_remove_list(list, l_itr); + } + + printf("inlist count=%d\n", eina_inlist_count(inlist)); + printf("list count=%d\n\n", eina_list_count(list)); + + eina_list_free(list); + + while (inlist) + { + Eina_Inlist *aux = inlist; + inlist = eina_inlist_remove(inlist, inlist); + free(aux); + } + + eina_shutdown(); + + return 0; +} diff --git a/src/examples/eina_inlist_03.c b/src/examples/eina_inlist_03.c new file mode 100644 index 0000000..a39a784 --- /dev/null +++ b/src/examples/eina_inlist_03.c @@ -0,0 +1,73 @@ +// Compile with: +// gcc -g `pkg-config --cflags --libs eina` eina_inlist_01.c -o eina_inlist_01 +#include +#include + +struct my_struct { + EINA_INLIST; + Eina_Inlist even; + int a, b; +}; + +#define EVEN_INLIST_GET(Inlist) (& ((Inlist)->even)) + +#define EVEN_INLIST_CONTAINER_GET(ptr, type) \ + ((type *)((char *)ptr - offsetof(type, even))) + +int +main(void) +{ + struct my_struct *d, *cur; + int i; + + Eina_Inlist *list = NULL, *list_even = NULL, *itr; + + eina_init(); + + for (i = 0; i < 100; i++) + { + d = malloc(sizeof(*d)); + d->a = i; + d->b = i * 10; + list = eina_inlist_append(list, EINA_INLIST_GET(d)); + if ((i % 2) == 0) + list_even = eina_inlist_prepend(list_even, EVEN_INLIST_GET(d)); + } + + printf("list=%p\n", list); + EINA_INLIST_FOREACH(list, cur) + printf("\ta=%d, b=%d\n", cur->a, cur->b); + + printf("list_even=%p\n", list_even); + for (itr = list_even; itr != NULL; itr = itr->next) + { + cur = EVEN_INLIST_CONTAINER_GET(itr, struct my_struct); + printf("\ta=%d, b=%d\n", cur->a, cur->b); + } + + printf("list count=%d\n", eina_inlist_count(list)); + printf("list_even count=%d\n\n", eina_inlist_count(list_even)); + + itr = list_even; + while (itr) + { + Eina_Inlist *next = itr->next; + cur = EVEN_INLIST_CONTAINER_GET(itr, struct my_struct); + if ((cur->a % 3) == 0) + list_even = eina_inlist_remove(list_even, itr); + itr = next; + } + printf("list count=%d\n", eina_inlist_count(list)); + printf("list_even count=%d\n\n", eina_inlist_count(list_even)); + + while (list) + { + Eina_Inlist *aux = list; + list = eina_inlist_remove(list, list); + free(aux); + } + + eina_shutdown(); + + return 0; +} diff --git a/src/include/eina_inlist.h b/src/include/eina_inlist.h index eef0788..694226e 100644 --- a/src/include/eina_inlist.h +++ b/src/include/eina_inlist.h @@ -25,16 +25,274 @@ #include /** + * @page inlist_01_example_page Eina_Inlist basic usage + * @dontinclude eina_inlist_01.c + * + * To see the full source for this example, click here: @ref + * eina_inlist_01_c + * + * As explained before, inline lists mean its nodes pointers are part of same + * memory block/blob. This is done by using the macro @ref EINA_INLIST inside the + * data structure that will be used: + * + * @skip struct + * @until }; + * + * The resulting node representing this struct can be exemplified by the + * following picture: + * + * @image html eina_inlist-node_eg1-my-struct.png + * @image rtf eina_inlist-node_eg1-my-struct.png + * + * Let's define a comparison function that will be used later during the + * sorting of the list: + * + * @skip int + * @until } + * + * The @ref Eina_Inlist can be used exactly the same way as @ref Eina_List when + * appending, prepend and removing items. But since we already have the node + * pointers inside the structure, they need to be retrieved with the macro @ref + * EINA_INLIST_GET : + * + * @skip malloc + * @until append + * + * Notice that @ref eina_inlist_append always receives the head of the list as + * first argument, and its return value should be used as the list pointer + * (head): + * + * @skip malloc + * @until append + * + * After appending 3 items, the list now should look similar to this: + * + * @image html eina_inlist-node_eg1-inlist.png + * @image rtf eina_inlist-node_eg1-inlist.png + * + * The macro @ref EINA_INLIST_FOREACH can be used to iterate over the list: + * + * @skip printf + * @until cur->a + * + * @ref eina_inlist_promote(), @ref eina_inlist_demote(), @ref + * eina_inlist_append_relative() and similar functions all work in the same way + * as the @ref Eina_List : + * + * @skip eina_inlist_promote + * @until eina_inlist_demote + * + * Now let's use the @c sort_cb function declared above to sort our list: + * + * @skipline eina_inlist_sort + * + * Removing an element from the inlist is also similar to @ref Eina_List : + * + * @skip inlist_remove + * @until free + * + * Another way of walking through the inlist. + * + * @skip for + * @until } + * + * Notice that in the previous piece of code, since we only have the pointers to + * the inlist nodes, we have to use the @ref EINA_INLIST_CONTAINER_GET macro + * that will return the pointer to the entire structure. Of course, in this case + * it is the same as the list pointer, since the @ref EINA_INLIST macro was used + * in the beginning of the structure. + * + * Now to finish this example, lets delete this list: + * + * @skip while + * @until } + */ + +/** + * @page inlist_02_example_page Eina_Inlist advanced usage - lists and inlists + * @dontinclude eina_inlist_02.c + * + * This example describes the usage of @ref Eina_Inlist mixed with @ref + * Eina_List . We create and add elements to an inlist, and the even members + * are also added to a normal list. Later we remove the elements divisible by 3 + * from this normal list. + * + * The struct that is going to be used is the same used in @ref + * inlist_01_example_page , since we still need the @ref EINA_INLIST macro to + * declare the inlist node info: + * + * @skip struct + * @until }; + * + * The resulting node representing this struct can be exemplified by the + * following picture: + * + * @image html eina_inlist-node_eg2-my-struct.png + * @image rtf eina_inlist-node_eg2-my-struct.png + * + * Now we need some pointers and auxiliar variables that will help us iterate on + * the lists: + * + * @skip struct + * @until l_next; + * + * Allocating 100 elements and putting them into an inlist, and the even + * elements also go to the normal list: + * + * @skip for + * @until } + * + * After this point, what we have are two distinct lists that share some + * elements. The first list (inlist) is defined by the pointers inside the + * elements data structure, while the second list (normal list) has its own node + * data structure that is kept outside of the elements. + * + * The two lists, sharing some elements, can be represented by the following + * picture: + * + * + * @image rtf eina_inlist-node_eg2-list-inlist.png + * + * Accessing both lists is done normally, as if they didn't have any elements in + * common: + * + * @skip printf + * @until eina_list_count + * + * We can remove elements from the normal list, but we just don't free them + * because they are still stored in the inlist: + * + * @skip EINA_LIST_FOREACH_SAFE + * @until eina_list_count + * + * To finish this example, we want to free both lists, we can't just free all + * elements on the second list (normal list) because they are still being used + * in the inlist. So we first discard the normal list without freeing its + * elements, then we free all elements in the inlist (that contains all elements + * allocated until now): + * + * @skip eina_list_free + * @until } + * + * Here is the full source code for this example: @ref eina_inlist_02_c + */ + +/** + * @page inlist_03_example_page Eina_Inlist advanced usage - multi-inlists + * @dontinclude eina_inlist_03.c + * + * This example describes the usage of multiple inlists storing the same data. + * It means that some data may appear in more than one inlist at the same time. + * We will demonstrate this by creating an inlist with 100 numbers, and adding + * the odd numbers to the second inlist, then remove the numbers divisible by 3 + * from the second list. + * + * To accomplish this, it is necessary to have two inlist pointers in the struct + * that is going to be stored. We are using the default inlist member @ref + * EINA_INLIST, and adding another member @c even that is of type @ref + * Eina_Inlist too: + * + * @skip struct + * @until }; + * + * The representation for this struct is: + * + * @image html eina_inlist-node_eg3-my-struct.png + * @image rtf eina_inlist-node_eg3-my-struct.png + * + * And we will define some convenience macros that are equivalent to @ref + * EINA_INLIST_GET and @ref EINA_INLIST_CONTAINER_GET : + * + * @skip define + * @until offsetof + * + * We need two pointers, one for each list, and a pointer that will be used as + * an iterator: + * + * @skipline Eina_Inlist + * + * Now we allocate and add to the first list every number from 0 to 99. These + * nodes data also have the @ref Eina_Inlist node info for the second list (@c + * even). We will use them to add just the even numbers to the second list, the + * @c list_even. Also notice that we are using our macro @c EVEN_INLIST_GET to + * get the pointer to the even list node info: + * + * @skip for + * @until } + * + * And the resulting lists will be as follow: + * + * + * @image rtf eina_inlist-node_eg3-two-inlists.png + * + * For the first list, we can use the macro @ref EINA_INLIST_FOREACH to iterate + * over its elements: + * + * @skip FOREACH + * @until printf + * + * But for the second list, we have to do it manually. Of course we could create + * a similar macro to @ref EINA_INLIST_FOREACH, but since this macro is more + * complex than the other two and we are using it only once, it's better to just + * do it manually: + * + * @skip for + * @until } + * + * Let's just check that the two lists have the expected number of elements: + * + * @skip list count + * @until list_even count + * + * And removing the numbers divisible by 3 only from the second list: + * + * @skip itr + * @until list_even count + * + * Now that we don't need the two lists anymore, we can just free all the items. + * Since all of the allocated data was put into the first list, and both lists + * are made of pointers to inside the data structures, we can free only the + * first list (that contains all the elements) and the second list will be gone + * with it: + * + * @skip while + * @until free + * + * To see the full source code for this example, click here: @ref + * eina_inlist_03_c + * + */ + +/** + * @page eina_inlist_01_c eina_inlist_01.c + * @include eina_inlist_01.c + */ + +/** + * @page eina_inlist_02_c eina_inlist_02.c + * @include eina_inlist_02.c + */ + +/** + * @page eina_inlist_03_c eina_inlist_03.c + * @include eina_inlist_03.c + */ + +/** * @addtogroup Eina_Inline_List_Group Inline List * * @brief These functions provide inline list management. * * Inline lists mean its nodes pointers are part of same memory as * data. This has the benefit of fragmenting memory less and avoiding - * @c node->data indirection, but has the drawback of elements only - * being able to be part of one single inlist at same time. But it is - * possible to have inlist nodes to be part of regular lists created - * with eina_list_append() or eina_list_prepend(). + * @c node->data indirection, but has the drawback of higher cost for some + * common operations like count and sort. + * + * It is possible to have inlist nodes to be part of regular lists, created with + * @ref eina_list_append() or @ref eina_list_prepend(). It's also possible to + * have a structure with two inlist pointers, thus be part of two different + * inlists at the same time, but the current convenience macros provided won't + * work for both of them. Consult @ref inlist_advanced for more info. * * Inline lists have their purposes, but if you don't know what those purposes are, go with * regular lists instead. @@ -46,61 +304,62 @@ * This lets the compiler to do type checking and let the programmer know * exactly what type this list is. * - * @code - * #include - * #include - * - * int - * main(void) - * { - * struct my_struct { - * EINA_INLIST; - * int a, b; - * } *d, *cur; - * Eina_Inlist *list, *itr; - * - * eina_init(); - * - * d = malloc(sizeof(*d)); - * d->a = 1; - * d->b = 10; - * list = eina_inlist_append(NULL, EINA_INLIST_GET(d)); - * - * d = malloc(sizeof(*d)); - * d->a = 2; - * d->b = 20; - * list = eina_inlist_append(list, EINA_INLIST_GET(d)); - * - * d = malloc(sizeof(*d)); - * d->a = 3; - * d->b = 30; - * list = eina_inlist_prepend(list, EINA_INLIST_GET(d)); - * - * printf("list=%p\n", list); - * EINA_INLIST_FOREACH(list, cur) - * printf("\ta=%d, b=%d\n", cur->a, cur->b); - * - * list = eina_inlist_remove(list, EINA_INLIST_GET(d)); - * free(d); - * printf("list=%p\n", list); - * for (itr = list; itr != NULL; itr = itr->next) - * { - * cur = EINA_INLIST_CONTAINER_GET(itr, struct my_struct); - * printf("\ta=%d, b=%d\n", cur->a, cur->b); - * } - * - * while (list) - * { - * Eina_Inlist *aux = list; - * list = eina_inlist_remove(list, list); - * free(aux); - * } - * - * eina_shutdown(); - * - * return 0; - * } - * @endcode + * A simple example demonstrating the basic usage of an inlist can be found + * here: @ref inlist_01_example_page + * + * @section inlist_algo Algorithm + * + * The basic structure can be represented by the following picture: + * + * @image html eina_inlist-node.png + * @image rtf eina_inlist-node.png + * + * One data structure will also have the node information, with three pointers: + * @a prev, @a next and @a last. The @a last pointer is just valid for the first + * element (the list head), otherwise each insertion in the list would have to + * be done updating every node with the correct pointer. This means that it's + * always very important to keep a pointer to the first element of the list, + * since it is the only one that has the correct information to allow a proper + * O(1) append to the list. + * + * @section inlist_perf Performance + * + * Due to the nature of the inlist, there's no accounting information, and no + * easy access to the last element from each list node. This means that @ref + * eina_inlist_count() is order-N, while @ref eina_list_count() is order-1 (constant + * time). + * + * For the same reasons, @ref eina_inlist_sort() is slower than @ref + * eina_list_sort() . If the list is intended to have faster access, be + * sorted/merged frequently, or needs to have other complex operations, consider + * using @ref Eina_List instead. + * + * @section inlist_advanced Advanced Usage + * + * The basic usage considers a struct that will have the user data, and also + * have an inlist node information (prev, next and last pointers) created with + * @ref EINA_INLIST during the struct declaration. This allows one to use the + * convenience macros @ref EINA_INLIST_GET(), @ref EINA_INLIST_CONTAINER_GET(), + * @ref EINA_INLIST_FOREACH() and so. This happens because the @ref EINA_INLIST + * macro declares a struct member with the name @a __inlist, and all the other + * macros assume that this struct member has this name. + * + * It may be the case that someone needs to have some inlist nodes added to a + * @ref Eina_List too. If this happens, the inlist nodes can be added to the + * @ref Eina_List without any problems. This example demonstrates this case: + * @ref inlist_02_example_page + * + * It's also possible to have some data that is part of two different inlists. + * If this is the case, then it won't be possible to use the convenience macros + * to both of the lists. It will be necessary to create a new set of macros that + * will allow access to the second list node info. An example for this usage can + * be found here: + * @ref inlist_03_example_page + * + * List of examples: + * @li @ref inlist_01_example_page + * @li @ref inlist_02_example_page + * @li @ref inlist_03_example_page * * @{ */