diff --git a/legacy/eina/src/include/Eina.h b/legacy/eina/src/include/Eina.h index 7dcddca636..f175bb49bd 100644 --- a/legacy/eina/src/include/Eina.h +++ b/legacy/eina/src/include/Eina.h @@ -148,6 +148,7 @@ extern "C" { #include "eina_main.h" #include "eina_fp.h" #include "eina_rectangle.h" +#include "eina_clist.h" #include "eina_inlist.h" #include "eina_file.h" #include "eina_list.h" diff --git a/legacy/eina/src/include/Makefile.am b/legacy/eina/src/include/Makefile.am index 5204db51b8..41a37cbe04 100644 --- a/legacy/eina/src/include/Makefile.am +++ b/legacy/eina/src/include/Makefile.am @@ -13,6 +13,7 @@ eina_inline_fp.x \ eina_hash.h \ eina_inline_hash.x \ eina_lalloc.h \ +eina_clist.h \ eina_inlist.h \ eina_list.h \ eina_file.h \ diff --git a/legacy/eina/src/include/eina_clist.h b/legacy/eina/src/include/eina_clist.h new file mode 100644 index 0000000000..ffac398478 --- /dev/null +++ b/legacy/eina/src/include/eina_clist.h @@ -0,0 +1,251 @@ +/* + * Linked lists support + * + * Copyright (C) 2002 Alexandre Julliard + * Copyright (C) 2011 Mike McCormack (adapted for Eina) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __EINA_CLIST_H__ +#define __EINA_CLIST_H__ + +/* + * Eina_Clist is a compact inline list implementation + * + * Advantages over Eina_List and Eina_Inlist: + * - uses less memory (two machine words per item) + * - allows removing items without knowing which list they're in using O(1) time + * - no need to keep updating the head pointer as the list is changed + * + * Disadvantages: + * - O(N) time to calculate list length + * - requires one list entry in a struct per list (i.e. it's an inlist) + * - requires a head/tail pointer + * + * Things to note: + * - there's no NULL at the end of the list, the last item points to the head + */ + +/* Define a list like so: + * + * struct gadget + * { + * struct Eina_Clist entry; <-- doesn't have to be the first item in the struct + * int a, b; + * }; + * + * static Eina_Clist global_gadgets = EINA_CLIST_INIT( global_gadgets ); + * + * or + * + * struct some_global_thing + * { + * Eina_Clist gadgets; + * }; + * + * eina_clist_init( &some_global_thing->gadgets ); + * + * Manipulate it like this: + * + * eina_clist_add_head( &global_gadgets, &new_gadget->entry ); + * eina_clist_remove( &new_gadget->entry ); + * eina_clist_add_after( &some_random_gadget->entry, &new_gadget->entry ); + * + * And to iterate over it: + * + * struct gadget *gadget; + * EINA_CLIST_FOR_EACH_ENTRY( gadget, &global_gadgets, struct gadget, entry ) + * { + * ... + * } + * + */ + +typedef struct _Eina_Clist Eina_Clist; +struct _Eina_Clist +{ + Eina_Clist *next; + Eina_Clist *prev; +}; + +/* add an element after the specified one */ +static inline void eina_clist_add_after(Eina_Clist *elem, Eina_Clist *to_add) +{ + to_add->next = elem->next; + to_add->prev = elem; + elem->next->prev = to_add; + elem->next = to_add; +} + +/* add an element before the specified one */ +static inline void eina_clist_add_before(Eina_Clist *elem, Eina_Clist *to_add) +{ + to_add->next = elem; + to_add->prev = elem->prev; + elem->prev->next = to_add; + elem->prev = to_add; +} + +/* add element at the head of the list */ +static inline void eina_clist_add_head(Eina_Clist *list, Eina_Clist *elem) +{ + eina_clist_add_after(list, elem); +} + +/* add element at the tail of the list */ +static inline void eina_clist_add_tail(Eina_Clist *list, Eina_Clist *elem) +{ + eina_clist_add_before(list, elem); +} + +/* remove an element from its list */ +static inline void eina_clist_remove(Eina_Clist *elem) +{ + elem->next->prev = elem->prev; + elem->prev->next = elem->next; +} + +/* get the next element */ +static inline Eina_Clist *eina_clist_next(const Eina_Clist *list, const Eina_Clist *elem) +{ + Eina_Clist *ret = elem->next; + if (elem->next == list) ret = NULL; + return ret; +} + +/* get the previous element */ +static inline Eina_Clist *eina_clist_prev(const Eina_Clist *list, const Eina_Clist *elem) +{ + Eina_Clist *ret = elem->prev; + if (elem->prev == list) ret = NULL; + return ret; +} + +/* get the first element */ +static inline Eina_Clist *eina_clist_head(const Eina_Clist *list) +{ + return eina_clist_next(list, list); +} + +/* get the last element */ +static inline Eina_Clist *eina_clist_tail(const Eina_Clist *list) +{ + return eina_clist_prev(list, list); +} + +/* check if a list is empty */ +static inline int eina_clist_empty(const Eina_Clist *list) +{ + return list->next == list; +} + +/* initialize a list */ +static inline void eina_clist_init(Eina_Clist *list) +{ + list->next = list->prev = list; +} + +/* count the elements of a list */ +static inline unsigned int eina_clist_count(const Eina_Clist *list) +{ + unsigned count = 0; + const Eina_Clist *ptr; + for (ptr = list->next; ptr != list; ptr = ptr->next) count++; + return count; +} + +/* move all elements from src to the tail of dst */ +static inline void eina_clist_move_tail(Eina_Clist *dst, Eina_Clist *src) +{ + if (eina_clist_empty(src)) return; + + dst->prev->next = src->next; + src->next->prev = dst->prev; + dst->prev = src->prev; + src->prev->next = dst; + eina_clist_init(src); +} + +/* move all elements from src to the head of dst */ +static inline void eina_clist_move_head(Eina_Clist *dst, Eina_Clist *src) +{ + if (eina_clist_empty(src)) return; + + dst->next->prev = src->prev; + src->prev->next = dst->next; + dst->next = src->next; + src->next->prev = dst; + eina_clist_init(src); +} + +/* iterate through the list */ +#define EINA_CLIST_FOR_EACH(cursor,list) \ + for ((cursor) = (list)->next; (cursor) != (list); (cursor) = (cursor)->next) + +/* iterate through the list, with safety against removal */ +#define EINA_CLIST_FOR_EACH_SAFE(cursor, cursor2, list) \ + for ((cursor) = (list)->next, (cursor2) = (cursor)->next; \ + (cursor) != (list); \ + (cursor) = (cursor2), (cursor2) = (cursor)->next) + +/* iterate through the list using a list entry */ +#define EINA_CLIST_FOR_EACH_ENTRY(elem, list, type, field) \ + for ((elem) = EINA_CLIST_ENTRY((list)->next, type, field); \ + &(elem)->field != (list); \ + (elem) = EINA_CLIST_ENTRY((elem)->field.next, type, field)) + +/* iterate through the list using a list entry, with safety against removal */ +#define EINA_CLIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, list, type, field) \ + for ((cursor) = EINA_CLIST_ENTRY((list)->next, type, field), \ + (cursor2) = EINA_CLIST_ENTRY((cursor)->field.next, type, field); \ + &(cursor)->field != (list); \ + (cursor) = (cursor2), \ + (cursor2) = EINA_CLIST_ENTRY((cursor)->field.next, type, field)) + +/* iterate through the list in reverse order */ +#define EINA_CLIST_FOR_EACH_REV(cursor,list) \ + for ((cursor) = (list)->prev; (cursor) != (list); (cursor) = (cursor)->prev) + +/* iterate through the list in reverse order, with safety against removal */ +#define EINA_CLIST_FOR_EACH_SAFE_REV(cursor, cursor2, list) \ + for ((cursor) = (list)->prev, (cursor2) = (cursor)->prev; \ + (cursor) != (list); \ + (cursor) = (cursor2), (cursor2) = (cursor)->prev) + +/* iterate through the list in reverse order using a list entry */ +#define EINA_CLIST_FOR_EACH_ENTRY_REV(elem, list, type, field) \ + for ((elem) = EINA_CLIST_ENTRY((list)->prev, type, field); \ + &(elem)->field != (list); \ + (elem) = EINA_CLIST_ENTRY((elem)->field.prev, type, field)) + +/* iterate through the list in reverse order using a list entry, with safety against removal */ +#define EINA_CLIST_FOR_EACH_ENTRY_SAFE_REV(cursor, cursor2, list, type, field) \ + for ((cursor) = EINA_CLIST_ENTRY((list)->prev, type, field), \ + (cursor2) = EINA_CLIST_ENTRY((cursor)->field.prev, type, field); \ + &(cursor)->field != (list); \ + (cursor) = (cursor2), \ + (cursor2) = EINA_CLIST_ENTRY((cursor)->field.prev, type, field)) + +/* macros for statically initialized lists */ +#undef EINA_CLIST_INIT +#define EINA_CLIST_INIT(list) { &(list), &(list) } + +/* get pointer to object containing list element */ +#undef EINA_CLIST_ENTRY +#define EINA_CLIST_ENTRY(elem, type, field) \ + ((type *)((char *)(elem) - (unsigned long)(&((type *)0)->field))) + +#endif /* __EINA_CLIST_H__ */ diff --git a/legacy/eina/src/tests/Makefile.am b/legacy/eina/src/tests/Makefile.am index feb9762ff4..a413e6e64b 100644 --- a/legacy/eina/src/tests/Makefile.am +++ b/legacy/eina/src/tests/Makefile.am @@ -39,6 +39,7 @@ eina_test_ustr.c \ eina_test_binshare.c \ eina_test_binbuf.c \ eina_test_array.c \ +eina_test_clist.c \ eina_test_error.c \ eina_test_sched.c \ eina_test_log.c \ diff --git a/legacy/eina/src/tests/eina_suite.c b/legacy/eina/src/tests/eina_suite.c index 6ff859aa77..9b748fcbde 100644 --- a/legacy/eina/src/tests/eina_suite.c +++ b/legacy/eina/src/tests/eina_suite.c @@ -47,6 +47,7 @@ static const Eina_Test_Case etc[] = { { "Counter", eina_test_counter }, { "Hash", eina_test_hash }, { "List", eina_test_list }, + { "CList", eina_test_clist }, { "Iterator", eina_test_iterator }, { "Accessor", eina_test_accessor }, { "Module", eina_test_module }, diff --git a/legacy/eina/src/tests/eina_suite.h b/legacy/eina/src/tests/eina_suite.h index 4ef1e8328e..643d6cca45 100644 --- a/legacy/eina/src/tests/eina_suite.h +++ b/legacy/eina/src/tests/eina_suite.h @@ -29,6 +29,7 @@ void eina_test_log(TCase *tc); void eina_test_error(TCase *tc); void eina_test_magic(TCase *tc); void eina_test_inlist(TCase *tc); +void eina_test_clist(TCase *tc); void eina_test_lalloc(TCase *tc); void eina_test_main(TCase *tc); void eina_test_counter(TCase *tc); diff --git a/legacy/eina/src/tests/eina_test_clist.c b/legacy/eina/src/tests/eina_test_clist.c new file mode 100644 index 0000000000..901c0e8a5c --- /dev/null +++ b/legacy/eina/src/tests/eina_test_clist.c @@ -0,0 +1,89 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include +#include "eina_suite.h" + +Eina_Clist string_list = EINA_CLIST_INIT(string_list); + +struct test_string +{ + Eina_Clist entry; + const char *string; +}; + +static void add_string(const char *foo) +{ + struct test_string *t; + + t = malloc(sizeof *t); + assert(t != NULL); + + t->string = foo; + eina_clist_add_tail(&string_list, &t->entry); +} + +static void print_strings(void) +{ + struct test_string *str; + + EINA_CLIST_FOR_EACH_ENTRY(str, &string_list, struct test_string, entry) + { + printf("%s ", str->string); + } + printf("\n"); +} + +static void free_list(void) +{ + struct test_string *str, *tmp; + + EINA_CLIST_FOR_EACH_ENTRY_SAFE(str, tmp, &string_list, struct test_string, entry) + { + eina_clist_remove(&str->entry); + } +} + +START_TEST(eina_clist_basic) +{ + unsigned int n = 0; + + add_string("this"); + n++; + add_string("is"); + n++; + add_string("a"); + n++; + add_string("test"); + n++; + add_string("of"); + n++; + add_string("clists"); + n++; + add_string("-"); + n++; + add_string("hello"); + n++; + add_string("world"); + n++; + + fail_if(eina_clist_count(&string_list) != n); + + print_strings(); + + free_list(); + + fail_if(eina_clist_count(&string_list) != 0); +} +END_TEST + +void +eina_test_clist(TCase *tc) +{ + tcase_add_test(tc, eina_clist_basic); +}