eina: add eina_inlist_sorted_insert.

SVN revision: 59654
This commit is contained in:
Cedric BAIL 2011-05-24 15:17:56 +00:00
parent 30e6030197
commit cde3148eca
4 changed files with 232 additions and 0 deletions

View File

@ -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.

View File

@ -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.
*

View File

@ -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)
{

View File

@ -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);
}