forked from enlightenment/efl
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:
parent
f86aa350c6
commit
944be7e745
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue