parent
30e6030197
commit
cde3148eca
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue