Bugfix eina_list_search_sorted_near_list() add sorted_insert.

eina_list_search_sorted_near_list() was broken and barfed at my face
during development of eina_list_sorted_insert(), so I rewrote it
following more traditional approach, also adding special cases for
head/tail remembering that random access in lists is not as fast as
array. I also simplified that code.

eina_list_sorted_insert() should be fast, O(log2 n) insert, with
special cases to insert already sorted arrays forwards or backwards,
however I believe that it's better to simply append/prepend in those
cases (if known).



SVN revision: 41625
This commit is contained in:
Gustavo Sverzut Barbieri 2009-08-06 22:31:45 +00:00
parent f86aa350c6
commit 944be7e745
3 changed files with 157 additions and 42 deletions

View File

@ -84,6 +84,7 @@ EAPI Eina_List *eina_list_append_relative (Eina_List *list, const void *data, co
EAPI Eina_List *eina_list_append_relative_list (Eina_List *list, const void *data, Eina_List *relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
EAPI Eina_List *eina_list_prepend_relative (Eina_List *list, const void *data, const void *relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
EAPI Eina_List *eina_list_prepend_relative_list (Eina_List *list, const void *data, Eina_List *relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
EAPI Eina_List *eina_list_sorted_insert(Eina_List *list, Eina_Compare_Cb func, const void *data) EINA_ARG_NONNULL(2, 3) EINA_WARN_UNUSED_RESULT;
EAPI Eina_List *eina_list_remove (Eina_List *list, const void *data) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
EAPI Eina_List *eina_list_remove_list (Eina_List *list, Eina_List *remove_list) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
EAPI Eina_List *eina_list_promote_list (Eina_List *list, Eina_List *move_list) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;

View File

@ -862,6 +862,37 @@ eina_list_prepend_relative_list(Eina_List *list, const void *data, Eina_List *re
return new_l;
}
/**
* @brief Insert a new node into a sorted list.
*
* @param list The given linked list, @b must be sorted.
* @param data The data to insert sorted.
* @return A list pointer.
*
* This function inserts values into a linked list assuming it was
* sorted and the result will be sorted. If @p list is @c NULLL, a new
* list 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)) average/worst case performance as it uses
* eina_list_search_sorted_near_list() and thus is bounded to that.
*/
EAPI Eina_List *
eina_list_sorted_insert(Eina_List *list, Eina_Compare_Cb func, const void *data)
{
Eina_List *lnear;
int cmp;
if (!list) return eina_list_append(NULL, data);
lnear = eina_list_search_sorted_near_list(list, func, data, &cmp);
if (cmp < 0)
return eina_list_append_relative_list(list, data, lnear);
else
return eina_list_prepend_relative_list(list, data, lnear);
}
/**
* @brief Remove the first instance of the specified data from the given list.
*
@ -1709,9 +1740,8 @@ EAPI Eina_List *
eina_list_search_sorted_near_list(const Eina_List *list, Eina_Compare_Cb func, const void *data, int *result_cmp)
{
const Eina_List *ct;
void *d;
unsigned int inf, sup, cur, tmp;
int part;
unsigned int inf, sup, cur;
int cmp;
if (!list)
{
@ -1719,38 +1749,61 @@ eina_list_search_sorted_near_list(const Eina_List *list, Eina_Compare_Cb func, c
return NULL;
}
inf = 0;
sup = list->accounting->count;
cur = sup >> 1;
for (tmp = 0, ct = list; tmp != cur; tmp++, ct = ct->next);
d = ct->data;
while ((part = func(d, data)))
if (list->accounting->count == 1)
{
if (inf == sup
|| (part < 0 && inf == cur)
|| (part > 0 && sup == cur))
goto end;
if (result_cmp) *result_cmp = func(list->data, data);
return (Eina_List *)list;
}
tmp = (sup + inf) >> 1;
if (part < 0)
inf = tmp;
else
sup = tmp;
/* Faster to move directly from where we are to the new position than using eina_list_nth_list. */
if (tmp < cur)
for (; cur != tmp; cur--, ct = ct->prev)
;
else
for (; cur != tmp; cur++, ct = ct->next)
;
d = ct->data;
/* list walk is expensive, do quick check: tail */
ct = list->accounting->last;
cmp = func(ct->data, data);
if (cmp <= 0)
goto end;
/* list walk is expensive, do quick check: head */
ct = list;
cmp = func(ct->data, data);
if (cmp >= 0)
goto end;
/* inclusive bounds */
inf = 1;
sup = list->accounting->count - 2;
cur = 1;
ct = list->next;
/* no loop, just compare if comparison value is important to caller */
if (inf > sup)
{
if (result_cmp) cmp = func(ct->data, data);
goto end;
}
while (inf <= sup)
{
unsigned 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->data, data);
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;
}
end:
if (result_cmp) *result_cmp = part;
return (Eina_List*) ct;
if (result_cmp) *result_cmp = cmp;
return (Eina_List *)ct;
}
/**
@ -1784,12 +1837,10 @@ EAPI Eina_List *
eina_list_search_sorted_list(const Eina_List *list, Eina_Compare_Cb func, const void *data)
{
Eina_List *lnear;
void *d;
int cmp;
int cmp;
lnear = eina_list_search_sorted_near_list(list, func, data, &cmp);
if (!lnear) return NULL;
d = eina_list_data_get(lnear);
if (cmp == 0)
return lnear;
return NULL;

View File

@ -25,6 +25,27 @@
#include "eina_list.h"
#include "eina_suite.h"
static Eina_Bool eina_list_sorted_check(const Eina_List *list)
{
const Eina_List *n;
void *d;
int last = *(int *)list->data;
EINA_LIST_FOREACH(list->next, n, d)
{
int current = *(int *)d;
if (last > current)
{
fprintf(stderr, "list is not sorted: last=%d, current=%d\n",
last, current);
return 0;
}
last = current;
}
return 1;
}
static int eina_int_cmp(const void *a, const void *b)
{
const int *ia = a;
@ -196,8 +217,6 @@ START_TEST(eina_test_merge)
Eina_List *l4;
Eina_List *l5;
int data[] = { 6, 9, 42, 1, 7, 9, 81, 1664, 1337, 3, 21, 10, 0, 5, 2008 };
int *prev;
int *current;
int i;
eina_list_init();
@ -269,13 +288,56 @@ START_TEST(eina_test_merge)
fail_if(l1 == NULL);
fail_if(eina_list_count(l1) != 15);
prev = eina_list_data_get(l1);
for (i = 1; i < eina_list_count(l1); ++i)
{
current = eina_list_nth(l1, i);
fail_if (*prev > *current);
prev = current;
}
fail_if(!eina_list_sorted_check(l1));
eina_list_shutdown();
}
END_TEST
START_TEST(eina_test_sorted_insert)
{
const int data[] = {6, 9, 42, 1, 7, 9, 81, 1664, 1337, 3, 21, 10, 0, 5, 2008};
const int data2[] = {5, 0, 3, 2, 1, 0, 1, 2, 3, 4, 5};
int i, count;
Eina_List *l1, *l2, *itr;
void *d;
eina_list_init();
count = sizeof(data)/sizeof(data[0]);
l1 = NULL;
for (i = 0; i < count; i++)
l1 = eina_list_sorted_insert(l1, eina_int_cmp, data + i);
fail_if(l1 == NULL);
fail_if(!eina_list_sorted_check(l1));
l2 = NULL;
EINA_LIST_FOREACH(l1, itr, d)
l2 = eina_list_sorted_insert(l2, eina_int_cmp, d);
fail_if(l2 == NULL);
fail_if(!eina_list_sorted_check(l2));
eina_list_free(l2);
l2 = NULL;
EINA_LIST_REVERSE_FOREACH(l1, itr, d)
l2 = eina_list_sorted_insert(l2, eina_int_cmp, d);
fail_if(l2 == NULL);
fail_if(!eina_list_sorted_check(l2));
eina_list_free(l2);
eina_list_free(l1);
count = sizeof(data2)/sizeof(data2[0]);
l1 = NULL;
for (i = 0; i < count; i++)
l1 = eina_list_sorted_insert(l1, eina_int_cmp, data2 + i);
fail_if(l1 == NULL);
fail_if(!eina_list_sorted_check(l1));
eina_list_free(l1);
eina_list_shutdown();
}
@ -286,4 +348,5 @@ eina_test_list(TCase *tc)
{
tcase_add_test(tc, eina_test_simple);
tcase_add_test(tc, eina_test_merge);
tcase_add_test(tc, eina_test_sorted_insert);
}