4 * Author: Jeffrey Stedfast <fejj@novell.com>
6 * (C) 2011 Novell, Inc.
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 /* Any segment <= this threshold will be sorted using insertion
32 * sort. OpenBSD seems to use a value of 7 so we'll go with that for
34 #define MAX_THRESHOLD 7
36 #define STACK_SIZE (8 * sizeof (size_t))
38 typedef struct _QSortStack
{
43 #define QSORT_PUSH(sp, a, c) ((sp)->array = (char*)(a), (sp)->count = (c), (sp)++)
44 #define QSORT_POP(sp, a, c) ((sp)--, (a) = (sp)->array, (c) = (sp)->count)
46 #define SWAPTYPE(TYPE, a, b) { \
47 gssize __n = size / sizeof (TYPE); \
48 TYPE *__a = (TYPE *) (a); \
49 TYPE *__b = (TYPE *) (b); \
56 } while (--__n > 0); \
59 #define SWAPBYTE(a, b) SWAPTYPE(char, (a), (b))
60 #define SWAPLONG(a, b) SWAPTYPE(long, (a), (b))
61 #define SWAP(a, b) if (swaplong) SWAPLONG((a), (b)) else SWAPBYTE((a), (b))
63 /* check if we can swap by longs rather than bytes by making sure that
64 * memory is properly aligned and that the element size is a multiple
66 #define SWAP_INIT() swaplong = (((char *) base) - ((char *) 0)) % sizeof (long) == 0 && (size % sizeof (long)) == 0
69 g_qsort_with_data (gpointer base
, size_t nmemb
, size_t size
, GCompareDataFunc compare
, gpointer user_data
)
71 QSortStack stack
[STACK_SIZE
], *sp
;
82 /* initialize our stack */
84 QSORT_PUSH (sp
, base
, nmemb
);
87 QSORT_POP (sp
, lo
, n
);
89 hi
= lo
+ (n
- 1) * size
;
91 if (n
< MAX_THRESHOLD
) {
92 /* switch to insertion sort */
93 for (i
= lo
+ size
; i
<= hi
; i
+= size
)
94 for (k
= i
; k
> lo
&& compare (k
- size
, k
, user_data
) > 0; k
-= size
)
100 /* calculate the middle element */
101 mid
= lo
+ (n
/ 2) * size
;
103 /* once we re-order the lo, mid, and hi elements to be in
104 * ascending order, we'll use mid as our pivot. */
105 if (compare (mid
, lo
, user_data
) < 0) {
109 if (compare (hi
, mid
, user_data
) < 0) {
111 if (compare (mid
, lo
, user_data
) < 0) {
116 /* since we've already guaranteed that lo <= mid and mid <= hi,
117 * we can skip comparing them again */
122 /* find the first element with a value > pivot value */
123 while (i
< k
&& compare (i
, mid
, user_data
) <= 0)
126 /* find the last element with a value <= pivot value */
127 while (k
>= i
&& compare (mid
, k
, user_data
) < 0)
135 /* make sure we keep track of our pivot element */
138 } else if (mid
== k
) {
147 /* swap the pivot with the last element in the first partition */
151 /* calculate segment sizes */
152 n2
= (hi
- k
) / size
;
153 n1
= (k
- lo
) / size
;
155 /* push our partitions onto the stack, largest first
156 * (to make sure we don't run out of stack space) */
158 if (n2
> 1) QSORT_PUSH (sp
, k
+ size
, n2
);
159 if (n1
> 1) QSORT_PUSH (sp
, lo
, n1
);
161 if (n1
> 1) QSORT_PUSH (sp
, lo
, n1
);
162 if (n2
> 1) QSORT_PUSH (sp
, k
+ size
, n2
);
164 } while (sp
> stack
);