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_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 (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_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 (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_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;
|
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;
|
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.
|
* @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)
|
eina_list_search_sorted_near_list(const Eina_List *list, Eina_Compare_Cb func, const void *data, int *result_cmp)
|
||||||
{
|
{
|
||||||
const Eina_List *ct;
|
const Eina_List *ct;
|
||||||
void *d;
|
unsigned int inf, sup, cur;
|
||||||
unsigned int inf, sup, cur, tmp;
|
int cmp;
|
||||||
int part;
|
|
||||||
|
|
||||||
if (!list)
|
if (!list)
|
||||||
{
|
{
|
||||||
|
@ -1719,38 +1749,61 @@ eina_list_search_sorted_near_list(const Eina_List *list, Eina_Compare_Cb func, c
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
inf = 0;
|
if (list->accounting->count == 1)
|
||||||
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 (inf == sup
|
if (result_cmp) *result_cmp = func(list->data, data);
|
||||||
|| (part < 0 && inf == cur)
|
return (Eina_List *)list;
|
||||||
|| (part > 0 && sup == cur))
|
}
|
||||||
|
|
||||||
|
/* list walk is expensive, do quick check: tail */
|
||||||
|
ct = list->accounting->last;
|
||||||
|
cmp = func(ct->data, data);
|
||||||
|
if (cmp <= 0)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
tmp = (sup + inf) >> 1;
|
/* list walk is expensive, do quick check: head */
|
||||||
if (part < 0)
|
ct = list;
|
||||||
inf = tmp;
|
cmp = func(ct->data, data);
|
||||||
else
|
if (cmp >= 0)
|
||||||
sup = tmp;
|
goto end;
|
||||||
/* Faster to move directly from where we are to the new position than using eina_list_nth_list. */
|
|
||||||
if (tmp < cur)
|
/* inclusive bounds */
|
||||||
for (; cur != tmp; cur--, ct = ct->prev)
|
inf = 1;
|
||||||
;
|
sup = list->accounting->count - 2;
|
||||||
else
|
cur = 1;
|
||||||
for (; cur != tmp; cur++, ct = ct->next)
|
ct = list->next;
|
||||||
;
|
|
||||||
d = ct->data;
|
/* 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:
|
end:
|
||||||
if (result_cmp) *result_cmp = part;
|
if (result_cmp) *result_cmp = cmp;
|
||||||
return (Eina_List*) ct;
|
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_search_sorted_list(const Eina_List *list, Eina_Compare_Cb func, const void *data)
|
||||||
{
|
{
|
||||||
Eina_List *lnear;
|
Eina_List *lnear;
|
||||||
void *d;
|
|
||||||
int cmp;
|
int cmp;
|
||||||
|
|
||||||
lnear = eina_list_search_sorted_near_list(list, func, data, &cmp);
|
lnear = eina_list_search_sorted_near_list(list, func, data, &cmp);
|
||||||
if (!lnear) return NULL;
|
if (!lnear) return NULL;
|
||||||
d = eina_list_data_get(lnear);
|
|
||||||
if (cmp == 0)
|
if (cmp == 0)
|
||||||
return lnear;
|
return lnear;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -25,6 +25,27 @@
|
||||||
#include "eina_list.h"
|
#include "eina_list.h"
|
||||||
#include "eina_suite.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)
|
static int eina_int_cmp(const void *a, const void *b)
|
||||||
{
|
{
|
||||||
const int *ia = a;
|
const int *ia = a;
|
||||||
|
@ -196,8 +217,6 @@ START_TEST(eina_test_merge)
|
||||||
Eina_List *l4;
|
Eina_List *l4;
|
||||||
Eina_List *l5;
|
Eina_List *l5;
|
||||||
int data[] = { 6, 9, 42, 1, 7, 9, 81, 1664, 1337, 3, 21, 10, 0, 5, 2008 };
|
int data[] = { 6, 9, 42, 1, 7, 9, 81, 1664, 1337, 3, 21, 10, 0, 5, 2008 };
|
||||||
int *prev;
|
|
||||||
int *current;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
eina_list_init();
|
eina_list_init();
|
||||||
|
@ -269,13 +288,56 @@ START_TEST(eina_test_merge)
|
||||||
fail_if(l1 == NULL);
|
fail_if(l1 == NULL);
|
||||||
fail_if(eina_list_count(l1) != 15);
|
fail_if(eina_list_count(l1) != 15);
|
||||||
|
|
||||||
prev = eina_list_data_get(l1);
|
fail_if(!eina_list_sorted_check(l1));
|
||||||
for (i = 1; i < eina_list_count(l1); ++i)
|
|
||||||
{
|
eina_list_shutdown();
|
||||||
current = eina_list_nth(l1, i);
|
}
|
||||||
fail_if (*prev > *current);
|
END_TEST
|
||||||
prev = current;
|
|
||||||
}
|
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();
|
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_simple);
|
||||||
tcase_add_test(tc, eina_test_merge);
|
tcase_add_test(tc, eina_test_merge);
|
||||||
|
tcase_add_test(tc, eina_test_sorted_insert);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue