blob: b9bda66c1f5e877f99b76ac643cbdcc39fe92ea8 [file] [log] [blame]
Vojtech Fried3e031b72012-08-24 16:52:44 +08001/*
2 * taken from https://github.com/swenson/sort
3 * Kept as is for the moment to be able to apply upstream patches for that
4 * code, currently used only to speed up XPath node sorting, see xpath.c
5 */
6
7/*
8 * All code in this header, unless otherwise specified, is hereby licensed under the MIT Public License:
9
10Copyright (c) 2010 Christopher Swenson
11
12Permission 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:
13
14The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15
16THE 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.
17*/
18
19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22#include <stdint.h>
23
24#ifndef MAX
25#define MAX(x,y) (((x) > (y) ? (x) : (y)))
26#endif
27#ifndef MIN
28#define MIN(x,y) (((x) < (y) ? (x) : (y)))
29#endif
30
31int compute_minrun(uint64_t);
32
33#ifndef CLZ
34#ifdef __GNUC__
35#define CLZ __builtin_clzll
36#else
37
38int clzll(uint64_t);
39
40/* adapted from Hacker's Delight */
41int clzll(uint64_t x) /* {{{ */
42{
43 int n;
44
45 if (x == 0) return(64);
46 n = 0;
47 if (x <= 0x00000000FFFFFFFFL) {n = n + 32; x = x << 32;}
48 if (x <= 0x0000FFFFFFFFFFFFL) {n = n + 16; x = x << 16;}
49 if (x <= 0x00FFFFFFFFFFFFFFL) {n = n + 8; x = x << 8;}
50 if (x <= 0x0FFFFFFFFFFFFFFFL) {n = n + 4; x = x << 4;}
51 if (x <= 0x3FFFFFFFFFFFFFFFL) {n = n + 2; x = x << 2;}
52 if (x <= 0x7FFFFFFFFFFFFFFFL) {n = n + 1;}
53 return n;
54}
55/* }}} */
56
57#define CLZ clzll
58#endif
59#endif
60
61int compute_minrun(const uint64_t size) /* {{{ */
62{
63 const int top_bit = 64 - CLZ(size);
64 const int shift = MAX(top_bit, 6) - 6;
65 const int minrun = size >> shift;
66 const uint64_t mask = (1ULL << shift) - 1;
67 if (mask & size) return minrun + 1;
68 return minrun;
69}
70/* }}} */
71
72#ifndef SORT_NAME
73#error "Must declare SORT_NAME"
74#endif
75
76#ifndef SORT_TYPE
77#error "Must declare SORT_TYPE"
78#endif
79
80#ifndef SORT_CMP
81#define SORT_CMP(x, y) ((x) < (y) ? -1 : ((x) == (y) ? 0 : 1))
82#endif
83
84
85#define SORT_SWAP(x,y) {SORT_TYPE __SORT_SWAP_t = (x); (x) = (y); (y) = __SORT_SWAP_t;}
86
87#define SORT_CONCAT(x, y) x ## _ ## y
88#define SORT_MAKE_STR1(x, y) SORT_CONCAT(x,y)
89#define SORT_MAKE_STR(x) SORT_MAKE_STR1(SORT_NAME,x)
90
91#define BINARY_INSERTION_FIND SORT_MAKE_STR(binary_insertion_find)
92#define BINARY_INSERTION_SORT_START SORT_MAKE_STR(binary_insertion_sort_start)
93#define BINARY_INSERTION_SORT SORT_MAKE_STR(binary_insertion_sort)
94#define REVERSE_ELEMENTS SORT_MAKE_STR(reverse_elements)
95#define COUNT_RUN SORT_MAKE_STR(count_run)
96#define CHECK_INVARIANT SORT_MAKE_STR(check_invariant)
97#define TIM_SORT SORT_MAKE_STR(tim_sort)
98#define TIM_SORT_RESIZE SORT_MAKE_STR(tim_sort_resize)
99#define TIM_SORT_MERGE SORT_MAKE_STR(tim_sort_merge)
100#define TIM_SORT_COLLAPSE SORT_MAKE_STR(tim_sort_collapse)
101
102#define TIM_SORT_RUN_T SORT_MAKE_STR(tim_sort_run_t)
103#define TEMP_STORAGE_T SORT_MAKE_STR(temp_storage_t)
104
105typedef struct {
106 int64_t start;
107 int64_t length;
108} TIM_SORT_RUN_T;
109
110void BINARY_INSERTION_SORT(SORT_TYPE *dst, const size_t size);
111void TIM_SORT(SORT_TYPE *dst, const size_t size);
112
113/* Function used to do a binary search for binary insertion sort */
114static int64_t BINARY_INSERTION_FIND(SORT_TYPE *dst, const SORT_TYPE x, const size_t size)
115{
116 int64_t l, c, r;
117 SORT_TYPE lx;
118 SORT_TYPE cx;
119 l = 0;
120 r = size - 1;
121 c = r >> 1;
122 lx = dst[l];
123
124 /* check for beginning conditions */
125 if (SORT_CMP(x, lx) < 0)
126 return 0;
127 else if (SORT_CMP(x, lx) == 0)
128 {
129 int64_t i = 1;
130 while (SORT_CMP(x, dst[i]) == 0) i++;
131 return i;
132 }
133
134 cx = dst[c];
135 while (1)
136 {
137 const int val = SORT_CMP(x, cx);
138 if (val < 0)
139 {
140 if (c - l <= 1) return c;
141 r = c;
142 }
143 else if (val > 0)
144 {
145 if (r - c <= 1) return c + 1;
146 l = c;
147 lx = cx;
148 }
149 else
150 {
151 do
152 {
153 cx = dst[++c];
154 } while (SORT_CMP(x, cx) == 0);
155 return c;
156 }
157 c = l + ((r - l) >> 1);
158 cx = dst[c];
159 }
160}
161
162/* Binary insertion sort, but knowing that the first "start" entries are sorted. Used in timsort. */
163static void BINARY_INSERTION_SORT_START(SORT_TYPE *dst, const size_t start, const size_t size)
164{
165 int64_t i;
166 for (i = start; i < size; i++)
167 {
168 int64_t j;
169 SORT_TYPE x;
170 int64_t location;
171 /* If this entry is already correct, just move along */
172 if (SORT_CMP(dst[i - 1], dst[i]) <= 0) continue;
173
174 /* Else we need to find the right place, shift everything over, and squeeze in */
175 x = dst[i];
176 location = BINARY_INSERTION_FIND(dst, x, i);
177 for (j = i - 1; j >= location; j--)
178 {
179 dst[j + 1] = dst[j];
180 }
181 dst[location] = x;
182 }
183}
184
185/* Binary insertion sort */
186void BINARY_INSERTION_SORT(SORT_TYPE *dst, const size_t size)
187{
188 BINARY_INSERTION_SORT_START(dst, 1, size);
189}
190
191/* timsort implementation, based on timsort.txt */
192
193static void REVERSE_ELEMENTS(SORT_TYPE *dst, int64_t start, int64_t end)
194{
195 while (1)
196 {
197 if (start >= end) return;
198 SORT_SWAP(dst[start], dst[end]);
199 start++;
200 end--;
201 }
202}
203
204static int64_t COUNT_RUN(SORT_TYPE *dst, const int64_t start, const size_t size)
205{
206 int64_t curr;
207 if (size - start == 1) return 1;
208 if (start >= size - 2)
209 {
210 if (SORT_CMP(dst[size - 2], dst[size - 1]) > 0)
211 SORT_SWAP(dst[size - 2], dst[size - 1]);
212 return 2;
213 }
214
215 curr = start + 2;
216
217 if (SORT_CMP(dst[start], dst[start + 1]) <= 0)
218 {
219 /* increasing run */
220 while (1)
221 {
222 if (curr == size - 1) break;
223 if (SORT_CMP(dst[curr - 1], dst[curr]) > 0) break;
224 curr++;
225 }
226 return curr - start;
227 }
228 else
229 {
230 /* decreasing run */
231 while (1)
232 {
233 if (curr == size - 1) break;
234 if (SORT_CMP(dst[curr - 1], dst[curr]) <= 0) break;
235 curr++;
236 }
237 /* reverse in-place */
238 REVERSE_ELEMENTS(dst, start, curr - 1);
239 return curr - start;
240 }
241}
242
243#define PUSH_NEXT() do {\
244len = COUNT_RUN(dst, curr, size);\
245run = minrun;\
246if (run < minrun) run = minrun;\
247if (run > size - curr) run = size - curr;\
248if (run > len)\
249{\
250 BINARY_INSERTION_SORT_START(&dst[curr], len, run);\
251 len = run;\
252}\
253{\
254run_stack[stack_curr].start = curr;\
255run_stack[stack_curr].length = len;\
256stack_curr++;\
257}\
258curr += len;\
259if (curr == size)\
260{\
261 /* finish up */ \
262 while (stack_curr > 1) \
263 { \
264 TIM_SORT_MERGE(dst, run_stack, stack_curr, store); \
265 run_stack[stack_curr - 2].length += run_stack[stack_curr - 1].length; \
266 stack_curr--; \
267 } \
268 if (store->storage != NULL)\
269 {\
270 free(store->storage);\
271 store->storage = NULL;\
272 }\
273 return;\
274}\
275}\
276while (0)
277
278static int CHECK_INVARIANT(TIM_SORT_RUN_T *stack, const int stack_curr)
279{
280 int64_t A, B, C;
281 if (stack_curr < 2) return 1;
282 if (stack_curr == 2)
283 {
284 const int64_t A1 = stack[stack_curr - 2].length;
285 const int64_t B1 = stack[stack_curr - 1].length;
286 if (A1 <= B1) return 0;
287 return 1;
288 }
289 A = stack[stack_curr - 3].length;
290 B = stack[stack_curr - 2].length;
291 C = stack[stack_curr - 1].length;
292 if ((A <= B + C) || (B <= C)) return 0;
293 return 1;
294}
295
296typedef struct {
297 size_t alloc;
298 SORT_TYPE *storage;
299} TEMP_STORAGE_T;
300
301
302static void TIM_SORT_RESIZE(TEMP_STORAGE_T *store, const size_t new_size)
303{
304 if (store->alloc < new_size)
305 {
306 SORT_TYPE *tempstore = (SORT_TYPE *)realloc(store->storage, new_size * sizeof(SORT_TYPE));
307 if (tempstore == NULL)
308 {
309 fprintf(stderr, "Error allocating temporary storage for tim sort: need %lu bytes", sizeof(SORT_TYPE) * new_size);
310 exit(1);
311 }
312 store->storage = tempstore;
313 store->alloc = new_size;
314 }
315}
316
317static void TIM_SORT_MERGE(SORT_TYPE *dst, const TIM_SORT_RUN_T *stack, const int stack_curr, TEMP_STORAGE_T *store)
318{
319 const int64_t A = stack[stack_curr - 2].length;
320 const int64_t B = stack[stack_curr - 1].length;
321 const int64_t curr = stack[stack_curr - 2].start;
322 SORT_TYPE *storage;
323 int64_t i, j, k;
324
325 TIM_SORT_RESIZE(store, MIN(A, B));
326 storage = store->storage;
327
328 /* left merge */
329 if (A < B)
330 {
331 memcpy(storage, &dst[curr], A * sizeof(SORT_TYPE));
332 i = 0;
333 j = curr + A;
334
335 for (k = curr; k < curr + A + B; k++)
336 {
337 if ((i < A) && (j < curr + A + B))
338 {
339 if (SORT_CMP(storage[i], dst[j]) <= 0)
340 dst[k] = storage[i++];
341 else
342 dst[k] = dst[j++];
343 }
344 else if (i < A)
345 {
346 dst[k] = storage[i++];
347 }
348 else
349 dst[k] = dst[j++];
350 }
351 }
352 /* right merge */
353 else
354 {
355 memcpy(storage, &dst[curr + A], B * sizeof(SORT_TYPE));
356 i = B - 1;
357 j = curr + A - 1;
358
359 for (k = curr + A + B - 1; k >= curr; k--)
360 {
361 if ((i >= 0) && (j >= curr))
362 {
363 if (SORT_CMP(dst[j], storage[i]) > 0)
364 dst[k] = dst[j--];
365 else
366 dst[k] = storage[i--];
367 }
368 else if (i >= 0)
369 dst[k] = storage[i--];
370 else
371 dst[k] = dst[j--];
372 }
373 }
374}
375
376static int TIM_SORT_COLLAPSE(SORT_TYPE *dst, TIM_SORT_RUN_T *stack, int stack_curr, TEMP_STORAGE_T *store, const size_t size)
377{
378 while (1)
379 {
380 int64_t A, B, C;
381 /* if the stack only has one thing on it, we are done with the collapse */
382 if (stack_curr <= 1) break;
383 /* if this is the last merge, just do it */
384 if ((stack_curr == 2) && (stack[0].length + stack[1].length == size))
385 {
386 TIM_SORT_MERGE(dst, stack, stack_curr, store);
387 stack[0].length += stack[1].length;
388 stack_curr--;
389 break;
390 }
391 /* check if the invariant is off for a stack of 2 elements */
392 else if ((stack_curr == 2) && (stack[0].length <= stack[1].length))
393 {
394 TIM_SORT_MERGE(dst, stack, stack_curr, store);
395 stack[0].length += stack[1].length;
396 stack_curr--;
397 break;
398 }
399 else if (stack_curr == 2)
400 break;
401
402 A = stack[stack_curr - 3].length;
403 B = stack[stack_curr - 2].length;
404 C = stack[stack_curr - 1].length;
405
406 /* check first invariant */
407 if (A <= B + C)
408 {
409 if (A < C)
410 {
411 TIM_SORT_MERGE(dst, stack, stack_curr - 1, store);
412 stack[stack_curr - 3].length += stack[stack_curr - 2].length;
413 stack[stack_curr - 2] = stack[stack_curr - 1];
414 stack_curr--;
415 }
416 else
417 {
418 TIM_SORT_MERGE(dst, stack, stack_curr, store);
419 stack[stack_curr - 2].length += stack[stack_curr - 1].length;
420 stack_curr--;
421 }
422 }
423 /* check second invariant */
424 else if (B <= C)
425 {
426 TIM_SORT_MERGE(dst, stack, stack_curr, store);
427 stack[stack_curr - 2].length += stack[stack_curr - 1].length;
428 stack_curr--;
429 }
430 else
431 break;
432 }
433 return stack_curr;
434}
435
436void TIM_SORT(SORT_TYPE *dst, const size_t size)
437{
438 int minrun;
439 TEMP_STORAGE_T _store, *store;
440 TIM_SORT_RUN_T run_stack[128];
441 int stack_curr = 0;
442 int64_t len, run;
443 int64_t curr = 0;
444
445 if (size < 64)
446 {
447 BINARY_INSERTION_SORT(dst, size);
448 return;
449 }
450
451 /* compute the minimum run length */
452 minrun = compute_minrun(size);
453
454 /* temporary storage for merges */
455 store = &_store;
456 store->alloc = 0;
457 store->storage = NULL;
458
459 PUSH_NEXT();
460 PUSH_NEXT();
461 PUSH_NEXT();
462
463 while (1)
464 {
465 if (!CHECK_INVARIANT(run_stack, stack_curr))
466 {
467 stack_curr = TIM_SORT_COLLAPSE(dst, run_stack, stack_curr, store, size);
468 continue;
469 }
470 PUSH_NEXT();
471 }
472}
473
474#undef SORT_CONCAT
475#undef SORT_MAKE_STR1
476#undef SORT_MAKE_STR
477#undef SORT_NAME
478#undef SORT_TYPE
479#undef SORT_CMP
480#undef TEMP_STORAGE_T
481#undef TIM_SORT_RUN_T
482#undef PUSH_NEXT
483#undef SORT_SWAP
484#undef SORT_CONCAT
485#undef SORT_MAKE_STR1
486#undef SORT_MAKE_STR
487#undef BINARY_INSERTION_FIND
488#undef BINARY_INSERTION_SORT_START
489#undef BINARY_INSERTION_SORT
490#undef REVERSE_ELEMENTS
491#undef COUNT_RUN
492#undef TIM_SORT
493#undef TIM_SORT_RESIZE
494#undef TIM_SORT_COLLAPSE
495#undef TIM_SORT_RUN_T
496#undef TEMP_STORAGE_T