add mergesort() for linked lists

This adds a generic bottom-up mergesort implementation for singly linked lists. It was inspired by Simon Tatham's webpage on the topic[1], but not so much by his implementation -- for no good reason, really, just a case of NIH. [1] http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed Apr 1, 2012 at 00:10 UTC 0db71e0fa94c1857f98890928098e8f4c8ac6f26
5 files changed +138
.gitignore
+1
@@ -180,6 +180,7 @@
180 /test-index-version
181 /test-line-buffer
182 /test-match-trees
183 +/test-mergesort
184 /test-mktemp
185 /test-obj-pool
186 /test-parse-options
Makefile
+3
@@ -465,6 +465,7 @@ TEST_PROGRAMS_NEED_X += test-genrandom
465 TEST_PROGRAMS_NEED_X += test-index-version
466 TEST_PROGRAMS_NEED_X += test-line-buffer
467 TEST_PROGRAMS_NEED_X += test-match-trees
468 +TEST_PROGRAMS_NEED_X += test-mergesort
469 TEST_PROGRAMS_NEED_X += test-mktemp
470 TEST_PROGRAMS_NEED_X += test-obj-pool
471 TEST_PROGRAMS_NEED_X += test-parse-options
@@ -578,6 +579,7 @@ LIB_H += log-tree.h
579 LIB_H += mailmap.h
580 LIB_H += merge-file.h
581 LIB_H += merge-recursive.h
582 +LIB_H += mergesort.h
583 LIB_H += notes.h
584 LIB_H += notes-cache.h
585 LIB_H += notes-merge.h
@@ -681,6 +683,7 @@ LIB_OBJS += mailmap.o
683 LIB_OBJS += match-trees.o
684 LIB_OBJS += merge-file.o
685 LIB_OBJS += merge-recursive.o
686 +LIB_OBJS += mergesort.o
687 LIB_OBJS += name-hash.o
688 LIB_OBJS += notes.o
689 LIB_OBJS += notes-cache.o
mergesort.c new
+73
@@ -0,0 +1,73 @@
1 +#include "cache.h"
2 +#include "mergesort.h"
3 +
4 +struct mergesort_sublist {
5 + void *ptr;
6 + unsigned long len;
7 +};
8 +
9 +static void *get_nth_next(void *list, unsigned long n,
10 + void *(*get_next_fn)(const void *))
11 +{
12 + while (n-- && list)
13 + list = get_next_fn(list);
14 + return list;
15 +}
16 +
17 +static void *pop_item(struct mergesort_sublist *l,
18 + void *(*get_next_fn)(const void *))
19 +{
20 + void *p = l->ptr;
21 + l->ptr = get_next_fn(l->ptr);
22 + l->len = l->ptr ? (l->len - 1) : 0;
23 + return p;
24 +}
25 +
26 +void *mergesort(void *list,
27 + void *(*get_next_fn)(const void *),
28 + void (*set_next_fn)(void *, void *),
29 + int (*compare_fn)(const void *, const void *))
30 +{
31 + unsigned long l;
32 +
33 + if (!list)
34 + return NULL;
35 + for (l = 1; ; l *= 2) {
36 + void *curr;
37 + struct mergesort_sublist p, q;
38 +
39 + p.ptr = list;
40 + q.ptr = get_nth_next(p.ptr, l, get_next_fn);
41 + if (!q.ptr)
42 + break;
43 + p.len = q.len = l;
44 +
45 + if (compare_fn(p.ptr, q.ptr) > 0)
46 + list = curr = pop_item(&q, get_next_fn);
47 + else
48 + list = curr = pop_item(&p, get_next_fn);
49 +
50 + while (p.ptr) {
51 + while (p.len || q.len) {
52 + void *prev = curr;
53 +
54 + if (!p.len)
55 + curr = pop_item(&q, get_next_fn);
56 + else if (!q.len)
57 + curr = pop_item(&p, get_next_fn);
58 + else if (compare_fn(p.ptr, q.ptr) > 0)
59 + curr = pop_item(&q, get_next_fn);
60 + else
61 + curr = pop_item(&p, get_next_fn);
62 + set_next_fn(prev, curr);
63 + }
64 + p.ptr = q.ptr;
65 + p.len = l;
66 + q.ptr = get_nth_next(p.ptr, l, get_next_fn);
67 + q.len = q.ptr ? l : 0;
68 +
69 + }
70 + set_next_fn(curr, NULL);
71 + }
72 + return list;
73 +}
mergesort.h new
+9
@@ -0,0 +1,9 @@
1 +#ifndef MERGESORT_H
2 +#define MERGESORT_H
3 +
4 +void *mergesort(void *list,
5 + void *(*get_next_fn)(const void *),
6 + void (*set_next_fn)(void *, void *),
7 + int (*compare_fn)(const void *, const void *));
8 +
9 +#endif
test-mergesort.c new
+52
@@ -0,0 +1,52 @@
1 +#include "cache.h"
2 +#include "mergesort.h"
3 +
4 +struct line {
5 + char *text;
6 + struct line *next;
7 +};
8 +
9 +static void *get_next(const void *a)
10 +{
11 + return ((const struct line *)a)->next;
12 +}
13 +
14 +static void set_next(void *a, void *b)
15 +{
16 + ((struct line *)a)->next = b;
17 +}
18 +
19 +static int compare_strings(const void *a, const void *b)
20 +{
21 + const struct line *x = a, *y = b;
22 + return strcmp(x->text, y->text);
23 +}
24 +
25 +int main(int argc, const char **argv)
26 +{
27 + struct line *line, *p = NULL, *lines = NULL;
28 + struct strbuf sb = STRBUF_INIT;
29 +
30 + for (;;) {
31 + if (strbuf_getwholeline(&sb, stdin, '\n'))
32 + break;
33 + line = xmalloc(sizeof(struct line));
34 + line->text = strbuf_detach(&sb, NULL);
35 + if (p) {
36 + line->next = p->next;
37 + p->next = line;
38 + } else {
39 + line->next = NULL;
40 + lines = line;
41 + }
42 + p = line;
43 + }
44 +
45 + lines = mergesort(lines, get_next, set_next, compare_strings);
46 +
47 + while (lines) {
48 + printf("%s", lines->text);
49 + lines = lines->next;
50 + }
51 + return 0;
52 +}