PageRenderTime 43ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/src/tsort.c

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