]> git.proxmox.com Git - libgit2.git/blame - src/tsort.c
unix/posix.h: remove redundant include
[libgit2.git] / src / tsort.c
CommitLineData
bdcc4611 1#include <common.h>
417a581d 2
de18f276
VM
3/**
4 * An array-of-pointers implementation of Python's Timsort
5 * Based on code by Christopher Swenson under the MIT license
6 *
7 * Copyright (c) 2010 Christopher Swenson
8 * Copyright (c) 2011 Vicent Marti
9 */
10
11#ifndef MAX
12# define MAX(x,y) (((x) > (y) ? (x) : (y)))
13#endif
14
15#ifndef MIN
16# define MIN(x,y) (((x) < (y) ? (x) : (y)))
17#endif
18
de18f276
VM
19typedef int (*cmp_ptr_t)(const void *, const void *);
20
21static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
22{
23 int l, c, r;
d4cb0ee8 24 void *lx, *cx;
de18f276
VM
25
26 l = 0;
27 r = size - 1;
28 c = r >> 1;
29 lx = dst[l];
30
31 /* check for beginning conditions */
32 if (cmp(x, lx) < 0)
33 return 0;
34
35 else if (cmp(x, lx) == 0) {
36 int i = 1;
37 while (cmp(x, dst[i]) == 0)
38 i++;
39 return i;
40 }
41
de18f276
VM
42 /* guaranteed not to be >= rx */
43 cx = dst[c];
44 while (1) {
45 const int val = cmp(x, cx);
46 if (val < 0) {
47 if (c - l <= 1) return c;
48 r = c;
de18f276
VM
49 } else if (val > 0) {
50 if (r - c <= 1) return c + 1;
51 l = c;
52 lx = cx;
53 } else {
54 do {
55 cx = dst[++c];
56 } while (cmp(x, cx) == 0);
57 return c;
58 }
59 c = l + ((r - l) >> 1);
60 cx = dst[c];
61 }
62}
63
64/* Binary insertion sort, but knowing that the first "start" entries are sorted. Used in timsort. */
65static void bisort(void **dst, size_t start, size_t size, cmp_ptr_t cmp)
66{
67 size_t i;
bdcc4611 68 void *x;
69 int location;
de18f276
VM
70
71 for (i = start; i < size; i++) {
72 int j;
73 /* If this entry is already correct, just move along */
74 if (cmp(dst[i - 1], dst[i]) <= 0)
75 continue;
76
77 /* Else we need to find the right place, shift everything over, and squeeze in */
bdcc4611 78 x = dst[i];
79 location = binsearch(dst, x, i, cmp);
de18f276
VM
80 for (j = i - 1; j >= location; j--) {
81 dst[j + 1] = dst[j];
82 }
83 dst[location] = x;
84 }
85}
86
87
88/* timsort implementation, based on timsort.txt */
89struct tsort_run {
90 ssize_t start;
91 ssize_t length;
92};
93
94struct tsort_store {
95 size_t alloc;
96 cmp_ptr_t cmp;
97 void **storage;
98};
99
100static void reverse_elements(void **dst, int start, int end)
101{
102 while (start < end) {
103 void *tmp = dst[start];
104 dst[start] = dst[end];
105 dst[end] = tmp;
106
107 start++;
108 end--;
109 }
110}
111
112static int count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
113{
114 ssize_t curr = start + 2;
115
116 if (size - start == 1)
117 return 1;
118
119 if (start >= size - 2) {
120 if (store->cmp(dst[size - 2], dst[size - 1]) > 0) {
121 void *tmp = dst[size - 1];
122 dst[size - 1] = dst[size - 2];
123 dst[size - 2] = tmp;
124 }
125
126 return 2;
127 }
128
129 if (store->cmp(dst[start], dst[start + 1]) <= 0) {
130 while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) <= 0)
131 curr++;
132
133 return curr - start;
134 } else {
135 while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) > 0)
136 curr++;
137
138 /* reverse in-place */
139 reverse_elements(dst, start, curr - 1);
140 return curr - start;
141 }
142}
143
144static int compute_minrun(size_t n)
145{
146 int r = 0;
147 while (n >= 64) {
148 r |= n & 1;
149 n >>= 1;
150 }
151 return n + r;
152}
153
154static int check_invariant(struct tsort_run *stack, int stack_curr)
155{
156 if (stack_curr < 2)
157 return 1;
158
159 else if (stack_curr == 2) {
160 const int A = stack[stack_curr - 2].length;
161 const int B = stack[stack_curr - 1].length;
162 return (A > B);
163 } else {
164 const int A = stack[stack_curr - 3].length;
165 const int B = stack[stack_curr - 2].length;
166 const int C = stack[stack_curr - 1].length;
167 return !((A <= B + C) || (B <= C));
168 }
169}
170
171static int resize(struct tsort_store *store, size_t new_size)
172{
173 if (store->alloc < new_size) {
174 void **tempstore = realloc(store->storage, new_size * sizeof(void *));
175
176 /**
177 * Do not propagate on OOM; this will abort the sort and
178 * leave the array unsorted, but no error code will be
179 * raised
180 */
181 if (tempstore == NULL)
182 return -1;
183
184 store->storage = tempstore;
185 store->alloc = new_size;
186 }
187
188 return 0;
189}
190
191static void merge(void **dst, const struct tsort_run *stack, int stack_curr, struct tsort_store *store)
192{
193 const ssize_t A = stack[stack_curr - 2].length;
194 const ssize_t B = stack[stack_curr - 1].length;
195 const ssize_t curr = stack[stack_curr - 2].start;
196
197 void **storage;
198 ssize_t i, j, k;
199
200 if (resize(store, MIN(A, B)) < 0)
201 return;
202
203 storage = store->storage;
204
205 /* left merge */
206 if (A < B) {
207 memcpy(storage, &dst[curr], A * sizeof(void *));
208 i = 0;
209 j = curr + A;
210
211 for (k = curr; k < curr + A + B; k++) {
212 if ((i < A) && (j < curr + A + B)) {
213 if (store->cmp(storage[i], dst[j]) <= 0)
214 dst[k] = storage[i++];
215 else
216 dst[k] = dst[j++];
217 } else if (i < A) {
218 dst[k] = storage[i++];
219 } else
220 dst[k] = dst[j++];
221 }
222 } else {
223 memcpy(storage, &dst[curr + A], B * sizeof(void *));
224 i = B - 1;
225 j = curr + A - 1;
226
227 for (k = curr + A + B - 1; k >= curr; k--) {
228 if ((i >= 0) && (j >= curr)) {
229 if (store->cmp(dst[j], storage[i]) > 0)
230 dst[k] = dst[j--];
231 else
232 dst[k] = storage[i--];
233 } else if (i >= 0)
234 dst[k] = storage[i--];
235 else
236 dst[k] = dst[j--];
237 }
238 }
239}
240
241static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store, ssize_t size)
242{
243 ssize_t A, B, C;
244
245 while (1) {
246 /* if the stack only has one thing on it, we are done with the collapse */
247 if (stack_curr <= 1)
248 break;
249
250 /* if this is the last merge, just do it */
251 if ((stack_curr == 2) && (stack[0].length + stack[1].length == size)) {
252 merge(dst, stack, stack_curr, store);
253 stack[0].length += stack[1].length;
254 stack_curr--;
255 break;
256 }
257
258 /* check if the invariant is off for a stack of 2 elements */
259 else if ((stack_curr == 2) && (stack[0].length <= stack[1].length)) {
260 merge(dst, stack, stack_curr, store);
261 stack[0].length += stack[1].length;
262 stack_curr--;
263 break;
264 }
265 else if (stack_curr == 2)
266 break;
267
268 A = stack[stack_curr - 3].length;
269 B = stack[stack_curr - 2].length;
270 C = stack[stack_curr - 1].length;
271
272 /* check first invariant */
273 if (A <= B + C) {
274 if (A < C) {
275 merge(dst, stack, stack_curr - 1, store);
276 stack[stack_curr - 3].length += stack[stack_curr - 2].length;
277 stack[stack_curr - 2] = stack[stack_curr - 1];
278 stack_curr--;
279 } else {
280 merge(dst, stack, stack_curr, store);
281 stack[stack_curr - 2].length += stack[stack_curr - 1].length;
282 stack_curr--;
283 }
284 } else if (B <= C) {
285 merge(dst, stack, stack_curr, store);
286 stack[stack_curr - 2].length += stack[stack_curr - 1].length;
287 stack_curr--;
288 } else
289 break;
290 }
291
292 return stack_curr;
293}
294
295#define PUSH_NEXT() do {\
296 len = count_run(dst, curr, size, store);\
297 run = minrun;\
298 if (run < minrun) run = minrun;\
299 if (run > (ssize_t)size - curr) run = size - curr;\
300 if (run > len) {\
301 bisort(&dst[curr], len, run, cmp);\
302 len = run;\
303 }\
bdcc4611 304 run_stack[stack_curr].start = curr;\
305 run_stack[stack_curr++].length = len;\
de18f276
VM
306 curr += len;\
307 if (curr == (ssize_t)size) {\
308 /* finish up */ \
309 while (stack_curr > 1) { \
310 merge(dst, run_stack, stack_curr, store); \
311 run_stack[stack_curr - 2].length += run_stack[stack_curr - 1].length; \
312 stack_curr--; \
313 } \
314 if (store->storage != NULL) {\
315 free(store->storage);\
316 store->storage = NULL;\
317 }\
318 return;\
319 }\
320}\
321while (0)
322
de18f276
VM
323void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
324{
325 struct tsort_store _store, *store = &_store;
326 struct tsort_run run_stack[128];
327
328 ssize_t stack_curr = 0;
329 ssize_t len, run;
330 ssize_t curr = 0;
331 ssize_t minrun;
332
333 if (size < 64) {
334 bisort(dst, 1, size, cmp);
335 return;
336 }
337
338 /* compute the minimum run length */
339 minrun = compute_minrun(size);
340
341 /* temporary storage for merges */
342 store->alloc = 0;
343 store->storage = NULL;
344 store->cmp = cmp;
345
346 PUSH_NEXT();
347 PUSH_NEXT();
348 PUSH_NEXT();
349
350 while (1) {
351 if (!check_invariant(run_stack, stack_curr)) {
352 stack_curr = collapse(dst, run_stack, stack_curr, store, size);
353 continue;
354 }
355
356 PUSH_NEXT();
357 }
358}