PatchworkOS
Loading...
Searching...
No Matches
qsort.c
Go to the documentation of this file.
1#include <stdint.h>
2#include <stdlib.h>
3
4/* This implementation is taken from Paul Edward's PDPCLIB.
5
6 Original code is credited to Raymond Gardner, Englewood CO.
7 Minor mods are credited to Paul Edwards.
8 Some reformatting and simplification done by Martin Baute.
9 All code is still Public Domain.
10*/
11
12static _INLINE void memswp(char* i, char* j, size_t size)
13{
14 char tmp;
15 do
16 {
17 tmp = *i;
18 *i++ = *j;
19 *j++ = tmp;
20 } while (--size);
21}
22
23/* For small sets, insertion sort is faster than quicksort.
24 T is the threshold below which insertion sort will be used.
25 Must be 3 or larger.
26*/
27#define T 7
28
29/* Macros for handling the QSort stack */
30#define PREPARE_STACK \
31 char* stack[STACKSIZE]; \
32 char** stackptr = stack
33#define PUSH(base, limit) \
34 stackptr[0] = base; \
35 stackptr[1] = limit; \
36 stackptr += 2
37#define POP(base, limit) \
38 stackptr -= 2; \
39 base = stackptr[0]; \
40 limit = stackptr[1]
41/* TODO: Stack usage is log2( nmemb ) (minus what T shaves off the worst case).
42 Worst-case nmemb is platform dependent and should probably be
43 configured through _PDCLIB_config.h.
44*/
45#define STACKSIZE 64
46
47void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*))
48{
49 char* i;
50 char* j;
51 uint64_t thresh = T * size;
52 char* base_ = (char*)base;
53 char* limit = base_ + nmemb * size;
55
56 for (;;)
57 {
58 if ((size_t)(limit - base_) > thresh) /* QSort for more than T elements. */
59 {
60 /* We work from second to last - first will be pivot element. */
61 i = base_ + size;
62 j = limit - size;
63 /* We swap first with middle element, then sort that with second
64 and last element so that eventually first element is the median
65 of the three - avoiding pathological pivots.
66 TODO: Instead of middle element, chose one randomly.
67 */
68 memswp(((((size_t)(limit - base_)) / size) / 2) * size + base_, base_, size);
69
70 if (compar(i, j) > 0)
71 {
72 memswp(i, j, size);
73 }
74
75 if (compar(base_, j) > 0)
76 {
77 memswp(base_, j, size);
78 }
79
80 if (compar(i, base_) > 0)
81 {
82 memswp(i, base_, size);
83 }
84
85 /* Now we have the median for pivot element, entering main Quicksort. */
86 for (;;)
87 {
88 do
89 {
90 /* move i right until *i >= pivot */
91 i += size;
92 } while (compar(i, base_) < 0);
93
94 do
95 {
96 /* move j left until *j <= pivot */
97 j -= size;
98 } while (compar(j, base_) > 0);
99
100 if (i > j)
101 {
102 /* break loop if pointers crossed */
103 break;
104 }
105
106 /* else swap elements, keep scanning */
107 memswp(i, j, size);
108 }
109
110 /* move pivot into correct place */
111 memswp(base_, j, size);
112
113 /* larger subfile base / limit to stack, sort smaller */
114 if (j - base_ > limit - i)
115 {
116 /* left is larger */
117 PUSH(base_, j);
118 base_ = i;
119 }
120 else
121 {
122 /* right is larger */
123 PUSH(i, limit);
124 limit = j;
125 }
126 }
127 else /* insertion sort for less than T elements */
128 {
129 for (j = base_, i = j + size; i < limit; j = i, i += size)
130 {
131 for (; compar(j, j + size) > 0; j -= size)
132 {
133 memswp(j, j + size, size);
134
135 if (j == base_)
136 {
137 break;
138 }
139 }
140 }
141
142 if (stackptr != stack) /* if any entries on stack */
143 {
144 POP(base_, limit);
145 }
146 else /* else stack empty, done */
147 {
148 break;
149 }
150 }
151 }
152}
#define _INLINE
Definition config.h:34
static pmm_stack_t stack
Definition pmm.c:36
#define T
Definition qsort.c:27
void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *))
Definition qsort.c:47
#define POP(base, limit)
Definition qsort.c:37
#define PUSH(base, limit)
Definition qsort.c:33
static _INLINE void memswp(char *i, char *j, size_t size)
Definition qsort.c:12
#define PREPARE_STACK
Definition qsort.c:30
__UINT64_TYPE__ uint64_t
Definition stdint.h:17