]> git.ozlabs.org Git - ccan/blob - ccan/asort/asort.c
doc: english fixup.
[ccan] / ccan / asort / asort.c
1 #include <ccan/asort/asort.h>
2 #include <stdlib.h>
3
4 #if !HAVE_QSORT_R_PRIVATE_LAST
5
6 /* Steal glibc's code. */
7
8 /* Copyright (C) 1991,1992,1996,1997,1999,2004 Free Software Foundation, Inc.
9    This file is part of the GNU C Library.
10    Written by Douglas C. Schmidt (schmidt@ics.uci.edu).
11
12    The GNU C Library is free software; you can redistribute it and/or
13    modify it under the terms of the GNU Lesser General Public
14    License as published by the Free Software Foundation; either
15    version 2.1 of the License, or (at your option) any later version.
16
17    The GNU C Library is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20    Lesser General Public License for more details.
21
22    You should have received a copy of the GNU Lesser General Public
23    License along with the GNU C Library; if not, write to the Free
24    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25    02111-1307 USA.  */
26
27 /* If you consider tuning this algorithm, you should consult first:
28    Engineering a sort function; Jon Bentley and M. Douglas McIlroy;
29    Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993.  */
30
31 #include <limits.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 /* Byte-wise swap two items of size SIZE. */
36 #define SWAP(a, b, size)                                                      \
37   do                                                                          \
38     {                                                                         \
39       register size_t __size = (size);                                        \
40       register char *__a = (a), *__b = (b);                                   \
41       do                                                                      \
42         {                                                                     \
43           char __tmp = *__a;                                                  \
44           *__a++ = *__b;                                                      \
45           *__b++ = __tmp;                                                     \
46         } while (--__size > 0);                                               \
47     } while (0)
48
49 /* Discontinue quicksort algorithm when partition gets below this size.
50    This particular magic number was chosen to work best on a Sun 4/260. */
51 #define MAX_THRESH 4
52
53 /* Stack node declarations used to store unfulfilled partition obligations. */
54 typedef struct
55   {
56     char *lo;
57     char *hi;
58   } stack_node;
59
60 /* The next 4 #defines implement a very fast in-line stack abstraction. */
61 /* The stack needs log (total_elements) entries (we could even subtract
62    log(MAX_THRESH)).  Since total_elements has type size_t, we get as
63    upper bound for log (total_elements):
64    bits per byte (CHAR_BIT) * sizeof(size_t).  */
65 #define STACK_SIZE      (CHAR_BIT * sizeof(size_t))
66 #define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
67 #define POP(low, high)  ((void) (--top, (low = top->lo), (high = top->hi)))
68 #define STACK_NOT_EMPTY (stack < top)
69
70
71 /* Order size using quicksort.  This implementation incorporates
72    four optimizations discussed in Sedgewick:
73
74    1. Non-recursive, using an explicit stack of pointer that store the
75       next array partition to sort.  To save time, this maximum amount
76       of space required to store an array of SIZE_MAX is allocated on the
77       stack.  Assuming a 32-bit (64 bit) integer for size_t, this needs
78       only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes).
79       Pretty cheap, actually.
80
81    2. Chose the pivot element using a median-of-three decision tree.
82       This reduces the probability of selecting a bad pivot value and
83       eliminates certain extraneous comparisons.
84
85    3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
86       insertion sort to order the MAX_THRESH items within each partition.
87       This is a big win, since insertion sort is faster for small, mostly
88       sorted array segments.
89
90    4. The larger of the two sub-partitions is always pushed onto the
91       stack first, with the algorithm then concentrating on the
92       smaller partition.  This *guarantees* no more than log (total_elems)
93       stack size is needed (actually O(1) in this case)!  */
94
95 void
96 _asort (void *const pbase, size_t total_elems, size_t size,
97         int(*cmp)(const void *, const void *, void *arg),
98         void *arg)
99 {
100   register char *base_ptr = (char *) pbase;
101
102   const size_t max_thresh = MAX_THRESH * size;
103
104   if (total_elems == 0)
105     /* Avoid lossage with unsigned arithmetic below.  */
106     return;
107
108   if (total_elems > MAX_THRESH)
109     {
110       char *lo = base_ptr;
111       char *hi = &lo[size * (total_elems - 1)];
112       stack_node stack[STACK_SIZE];
113       stack_node *top = stack;
114
115       PUSH (NULL, NULL);
116
117       while (STACK_NOT_EMPTY)
118         {
119           char *left_ptr;
120           char *right_ptr;
121
122           /* Select median value from among LO, MID, and HI. Rearrange
123              LO and HI so the three values are sorted. This lowers the
124              probability of picking a pathological pivot value and
125              skips a comparison for both the LEFT_PTR and RIGHT_PTR in
126              the while loops. */
127
128           char *mid = lo + size * ((hi - lo) / size >> 1);
129
130           if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
131             SWAP (mid, lo, size);
132           if ((*cmp) ((void *) hi, (void *) mid, arg) < 0)
133             SWAP (mid, hi, size);
134           else
135             goto jump_over;
136           if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
137             SWAP (mid, lo, size);
138         jump_over:;
139
140           left_ptr  = lo + size;
141           right_ptr = hi - size;
142
143           /* Here's the famous ``collapse the walls'' section of quicksort.
144              Gotta like those tight inner loops!  They are the main reason
145              that this algorithm runs much faster than others. */
146           do
147             {
148               while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
149                 left_ptr += size;
150
151               while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
152                 right_ptr -= size;
153
154               if (left_ptr < right_ptr)
155                 {
156                   SWAP (left_ptr, right_ptr, size);
157                   if (mid == left_ptr)
158                     mid = right_ptr;
159                   else if (mid == right_ptr)
160                     mid = left_ptr;
161                   left_ptr += size;
162                   right_ptr -= size;
163                 }
164               else if (left_ptr == right_ptr)
165                 {
166                   left_ptr += size;
167                   right_ptr -= size;
168                   break;
169                 }
170             }
171           while (left_ptr <= right_ptr);
172
173           /* Set up pointers for next iteration.  First determine whether
174              left and right partitions are below the threshold size.  If so,
175              ignore one or both.  Otherwise, push the larger partition's
176              bounds on the stack and continue sorting the smaller one. */
177
178           if ((size_t) (right_ptr - lo) <= max_thresh)
179             {
180               if ((size_t) (hi - left_ptr) <= max_thresh)
181                 /* Ignore both small partitions. */
182                 POP (lo, hi);
183               else
184                 /* Ignore small left partition. */
185                 lo = left_ptr;
186             }
187           else if ((size_t) (hi - left_ptr) <= max_thresh)
188             /* Ignore small right partition. */
189             hi = right_ptr;
190           else if ((right_ptr - lo) > (hi - left_ptr))
191             {
192               /* Push larger left partition indices. */
193               PUSH (lo, right_ptr);
194               lo = left_ptr;
195             }
196           else
197             {
198               /* Push larger right partition indices. */
199               PUSH (left_ptr, hi);
200               hi = right_ptr;
201             }
202         }
203     }
204
205   /* Once the BASE_PTR array is partially sorted by quicksort the rest
206      is completely sorted using insertion sort, since this is efficient
207      for partitions below MAX_THRESH size. BASE_PTR points to the beginning
208      of the array to sort, and END_PTR points at the very last element in
209      the array (*not* one beyond it!). */
210
211 #define min(x, y) ((x) < (y) ? (x) : (y))
212
213   {
214     char *const end_ptr = &base_ptr[size * (total_elems - 1)];
215     char *tmp_ptr = base_ptr;
216     char *thresh = min(end_ptr, base_ptr + max_thresh);
217     register char *run_ptr;
218
219     /* Find smallest element in first threshold and place it at the
220        array's beginning.  This is the smallest array element,
221        and the operation speeds up insertion sort's inner loop. */
222
223     for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
224       if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0)
225         tmp_ptr = run_ptr;
226
227     if (tmp_ptr != base_ptr)
228       SWAP (tmp_ptr, base_ptr, size);
229
230     /* Insertion sort, running from left-hand-side up to right-hand-side.  */
231
232     run_ptr = base_ptr + size;
233     while ((run_ptr += size) <= end_ptr)
234       {
235         tmp_ptr = run_ptr - size;
236         while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0)
237           tmp_ptr -= size;
238
239         tmp_ptr += size;
240         if (tmp_ptr != run_ptr)
241           {
242             char *trav;
243
244             trav = run_ptr + size;
245             while (--trav >= run_ptr)
246               {
247                 char c = *trav;
248                 char *hi, *lo;
249
250                 for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
251                   *hi = *lo;
252                 *hi = c;
253               }
254           }
255       }
256   }
257 }
258
259 #endif /* !HAVE_QSORT_R_PRIVATE_LAST */