|
| 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 *llist_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 | +} |
0 commit comments