[runtime] Move eglib into mono/eglib so it becomes a convenience library similar...
[mono.git] / mono / eglib / gqsort.c
diff --git a/mono/eglib/gqsort.c b/mono/eglib/gqsort.c
new file mode 100644 (file)
index 0000000..59bd453
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * QuickSort
+ *
+ * Author: Jeffrey Stedfast <fejj@novell.com>
+ *
+ * (C) 2011 Novell, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <glib.h>
+
+/* Any segment <= this threshold will be sorted using insertion
+ * sort. OpenBSD seems to use a value of 7 so we'll go with that for
+ * now... */
+#define MAX_THRESHOLD 7
+
+#define STACK_SIZE (8 * sizeof (size_t))
+
+typedef struct _QSortStack {
+       char *array;
+       size_t count;
+} QSortStack;
+
+#define QSORT_PUSH(sp, a, c) (sp->array = a, sp->count = c, sp++)
+#define QSORT_POP(sp, a, c) (sp--, a = sp->array, c = sp->count)
+
+#define SWAPTYPE(TYPE, a, b) {              \
+       long __n = size / sizeof (TYPE);    \
+       register TYPE *__a = (TYPE *) (a);  \
+       register TYPE *__b = (TYPE *) (b);  \
+       register TYPE t;                    \
+                                           \
+       do {                                \
+               t = *__a;                   \
+               *__a++ = *__b;              \
+               *__b++ = t;                 \
+       } while (--__n > 0);                \
+}
+
+#define SWAPBYTE(a, b) SWAPTYPE(char, (a), (b))
+#define SWAPLONG(a, b) SWAPTYPE(long, (a), (b))
+#define SWAP(a, b) if (swaplong) SWAPLONG((a), (b)) else SWAPBYTE((a), (b))
+
+/* check if we can swap by longs rather than bytes by making sure that
+ * memory is properly aligned and that the element size is a multiple
+ * of sizeof (long) */
+#define SWAP_INIT() swaplong = (((char *) base) - ((char *) 0)) % sizeof (long) == 0 && (size % sizeof (long)) == 0
+
+void
+g_qsort_with_data (gpointer base, size_t nmemb, size_t size, GCompareDataFunc compare, gpointer user_data)
+{
+       QSortStack stack[STACK_SIZE], *sp;
+       register char *i, *k, *mid;
+       size_t n, n1, n2;
+       char *lo, *hi;
+       int swaplong;
+       
+       if (nmemb <= 1)
+               return;
+       
+       SWAP_INIT ();
+       
+       /* initialize our stack */
+       sp = stack;
+       QSORT_PUSH (sp, base, nmemb);
+       
+       do {
+               QSORT_POP (sp, lo, n);
+               
+               hi = lo + (n - 1) * size;
+               
+               if (n < MAX_THRESHOLD) {
+                       /* switch to insertion sort */
+                       for (i = lo + size; i <= hi; i += size)
+                               for (k = i; k > lo && compare (k - size, k, user_data) > 0; k -= size)
+                                       SWAP (k - size, k);
+                       
+                       continue;
+               }
+               
+               /* calculate the middle element */
+               mid = lo + (n / 2) * size;
+               
+               /* once we re-order the lo, mid, and hi elements to be in
+                * ascending order, we'll use mid as our pivot. */
+               if (compare (mid, lo, user_data) < 0) {
+                       SWAP (mid, lo);
+               }
+               
+               if (compare (hi, mid, user_data) < 0) {
+                       SWAP (mid, hi);
+                       if (compare (mid, lo, user_data) < 0) {
+                               SWAP (mid, lo);
+                       }
+               }
+               
+               /* since we've already guaranteed that lo <= mid and mid <= hi,
+                * we can skip comparing them again */
+               i = lo + size;
+               k = hi - size;
+               
+               do {
+                       /* find the first element with a value > pivot value */
+                       while (i < k && compare (i, mid, user_data) <= 0)
+                               i += size;
+                       
+                       /* find the last element with a value <= pivot value */
+                       while (k >= i && compare (mid, k, user_data) < 0)
+                               k -= size;
+                       
+                       if (k <= i)
+                               break;
+                       
+                       SWAP (i, k);
+                       
+                       /* make sure we keep track of our pivot element */
+                       if (mid == i) {
+                               mid = k;
+                       } else if (mid == k) {
+                               mid = i;
+                       }
+                       
+                       i += size;
+                       k -= size;
+               } while (1);
+               
+               if (k != mid) {
+                       /* swap the pivot with the last element in the first partition */
+                       SWAP (mid, k);
+               }
+               
+               /* calculate segment sizes */
+               n2 = (hi - k) / size;
+               n1 = (k - lo) / size;
+               
+               /* push our partitions onto the stack, largest first
+                * (to make sure we don't run out of stack space) */
+               if (n2 > n1) {
+                       if (n2 > 1) QSORT_PUSH (sp, k + size, n2);
+                       if (n1 > 1) QSORT_PUSH (sp, lo, n1);
+               } else {
+                       if (n1 > 1) QSORT_PUSH (sp, lo, n1);
+                       if (n2 > 1) QSORT_PUSH (sp, k + size, n2);
+               }
+       } while (sp > stack);
+}