diff --git a/legacy/eina/ChangeLog b/legacy/eina/ChangeLog index 5354a68e06..79cbe7a823 100644 --- a/legacy/eina/ChangeLog +++ b/legacy/eina/ChangeLog @@ -89,3 +89,7 @@ 2011-05-24 Vincent Torri * Implement eina_sched_prio_drop() on Windows + +2011-05-24 Cedric Bail + + * Add eina_inlist_sorted_insert. diff --git a/legacy/eina/src/include/eina_inlist.h b/legacy/eina/src/include/eina_inlist.h index ed67785617..1b5a76720a 100644 --- a/legacy/eina/src/include/eina_inlist.h +++ b/legacy/eina/src/include/eina_inlist.h @@ -361,6 +361,29 @@ EAPI Eina_Iterator *eina_inlist_iterator_new(const Eina_Inlist *in_list) EINA_MA */ EAPI Eina_Accessor *eina_inlist_accessor_new(const Eina_Inlist *in_list) EINA_MALLOC EINA_WARN_UNUSED_RESULT; +/** + * @brief Insert a new node into a sorted list. + * + * @param list The given linked list, @b must be sorted. + * @param item list node to insert, must not be NULL. + * @param func The function called for the sort. + * @return A list pointer. + * + * This function inserts item into a linked list assuming it was + * sorted and the result will be sorted. If @p list is @c NULLL, item + * is returned. On success, a new list pointer that should be + * used in place of the one given to this function is + * returned. Otherwise, the old pointer is returned. See eina_error_get(). + * + * @note O(log2(n)) comparisons (calls to @p func) average/worst case + * performance as it uses eina_list_search_sorted_near_list() and thus + * is bounded to that. As said in eina_list_search_sorted_near_list(), + * lists do not have O(1) access time, so walking to the correct node + * can be costly, consider worst case to be almost O(n) pointer + * dereference (list walk). + */ +EAPI Eina_Inlist *eina_inlist_sorted_insert(Eina_Inlist *list, Eina_Inlist *item, Eina_Compare_Cb func) EINA_ARG_NONNULL(2, 3) EINA_WARN_UNUSED_RESULT; + /** * @brief Sort a list according to the ordering func will return. * diff --git a/legacy/eina/src/lib/eina_inlist.c b/legacy/eina/src/lib/eina_inlist.c index 90a0580072..bf30de972d 100644 --- a/legacy/eina/src/lib/eina_inlist.c +++ b/legacy/eina/src/lib/eina_inlist.c @@ -422,6 +422,141 @@ eina_inlist_count(const Eina_Inlist *list) return i; } +#define EINA_INLIST_JUMP_SIZE 256 + +EAPI Eina_Inlist * +eina_inlist_sorted_insert(Eina_Inlist *list, + Eina_Inlist *item, + Eina_Compare_Cb func) +{ + Eina_Inlist *ct = NULL; + Eina_Inlist *jump_table[EINA_INLIST_JUMP_SIZE]; + int cmp = 0; + int inf, sup; + int cur = 0; + int count = 0; + unsigned short jump_limit = 0; + int jump_div = 1; + int jump_count = 1; + + if (!list) return eina_inlist_append(NULL, item); + + if (!list->next) + { + cmp = func(list, item); + + if (cmp < 0) + return eina_inlist_append(list, item); + return eina_inlist_prepend(list, item); + } + + /* + * prepare a jump table to avoid doing unecessary rewalk + * of the inlist as much as possible. + */ + for (ct = list->next; ct; ct = ct->next, jump_count++, count++) + { + if (jump_count == jump_div) + { + if (jump_limit == EINA_INLIST_JUMP_SIZE) + { + unsigned short i, j; + + /* compress the jump table */ + jump_div *= 2; + jump_limit /= 2; + + for (i = 2, j = 1; + i < EINA_INLIST_JUMP_SIZE; + i += 2, j++) + jump_table[j] = jump_table[i]; + } + + jump_table[jump_limit] = ct; + jump_limit++; + jump_count = 0; + } + } + + /* + * now do a dychotomic search directly inside the jump_table. + */ + inf = 0; + sup = jump_limit - 1; + cur = 0; + ct = jump_table[cur]; + + while (inf <= sup) + { + cur = inf + ((sup - inf) >> 1); + ct = jump_table[cur]; + + cmp = func(ct, item); + if (cmp == 0) + break ; + else if (cmp < 0) + inf = cur + 1; + else if (cmp > 0) + { + if (cur > 0) + sup = cur - 1; + else + break; + } + else + break; + } + + /* If at the beginning of the table and cmp < 0, + * insert just after the head */ + if (cur == 0 && cmp < 0) + return eina_inlist_append_relative(list, item, list->next); + + /* If at the end of the table and cmp >= 0, + * just append the item to the list */ + if (cmp >= 0 && ct == list->last) + return eina_inlist_append(list, item); + + /* + * Now do a dychotomic search between two entries inside the jump_table + */ + cur *= jump_div; + inf = cur; + sup = inf + jump_div; + + if (sup > count - 1) sup = count - 1; + + while (inf <= sup) + { + int tmp = cur; + + cur = inf + ((sup - inf) >> 1); + if (tmp < cur) + for (; tmp != cur; tmp++, ct = ct->next); + else if (tmp > cur) + for (; tmp != cur; tmp--, ct = ct->prev); + + cmp = func(ct, item); + if (cmp == 0) + break ; + else if (cmp < 0) + inf = cur + 1; + else if (cmp > 0) + { + if (cur > 0) + sup = cur - 1; + else + break; + } + else + break; + } + + if (cmp < 0) + return eina_inlist_append_relative(list, item, ct); + return eina_inlist_prepend_relative(list, item, ct); +} + EAPI Eina_Inlist * eina_inlist_sort(Eina_Inlist *head, Eina_Compare_Cb func) { diff --git a/legacy/eina/src/tests/eina_test_inlist.c b/legacy/eina/src/tests/eina_test_inlist.c index 6d8b210285..5c7166cf95 100644 --- a/legacy/eina/src/tests/eina_test_inlist.c +++ b/legacy/eina/src/tests/eina_test_inlist.c @@ -135,8 +135,78 @@ START_TEST(eina_inlist_simple) } END_TEST +typedef struct _Eina_Test_Inlist_Sorted Eina_Test_Inlist_Sorted; +struct _Eina_Test_Inlist_Sorted +{ + EINA_INLIST; + + int value; +}; + +static int +_eina_test_inlist_cmp(const void *d1, const void *d2) +{ + const Eina_Test_Inlist_Sorted *t1 = d1; + const Eina_Test_Inlist_Sorted *t2 = d2; + + return t1->value - t2->value; +} + +static void +_eina_test_inlist_check(const Eina_Inlist *list) +{ + const Eina_Test_Inlist_Sorted *t; + int last_value = 0; + + EINA_INLIST_FOREACH(list, t) + { + fail_if(t->value < last_value); + last_value = t->value; + } +} + +START_TEST(eina_inlist_sorted) +{ + Eina_Inlist *list = NULL; + Eina_Inlist *sorted = NULL; + int i; + + srand(time(NULL)); + + for (i = 0; i < 5000; ++i) + { + Eina_Test_Inlist_Sorted *tmp; + + tmp = malloc(sizeof (Eina_Test_Inlist_Sorted)); + if (!tmp) continue ; + + tmp->value = rand(); + + list = eina_inlist_prepend(list, EINA_INLIST_GET(tmp)); + } + + list = eina_inlist_sort(list, _eina_test_inlist_cmp); + + _eina_test_inlist_check(list); + + i = 0; + while (list) + { + Eina_Inlist *tmp = list; + + list = eina_inlist_remove(list, list); + + sorted = eina_inlist_sorted_insert(sorted, tmp, _eina_test_inlist_cmp); + _eina_test_inlist_check(sorted); + } + + _eina_test_inlist_check(sorted); +} +END_TEST + void eina_test_inlist(TCase *tc) { tcase_add_test(tc, eina_inlist_simple); + tcase_add_test(tc, eina_inlist_sorted); }