eina: add eina_inlist_sorted_insert.
authorcedric <cedric@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Tue, 24 May 2011 15:17:56 +0000 (15:17 +0000)
committercedric <cedric@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Tue, 24 May 2011 15:17:56 +0000 (15:17 +0000)
git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/eina@59654 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

ChangeLog
src/include/eina_inlist.h
src/lib/eina_inlist.c
src/tests/eina_test_inlist.c

index 5354a68..79cbe7a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
index ed67785..1b5a767 100644 (file)
@@ -362,6 +362,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.
  *
  * @param list The list handle to sort.
index 90a0580..bf30de9 100644 (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)
 {
index 6d8b210..5c7166c 100644 (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);
 }