| #include "cache.h" |
| #include "mergesort.h" |
| |
| struct mergesort_sublist { |
| void *ptr; |
| unsigned long len; |
| }; |
| |
| static void *get_nth_next(void *list, unsigned long n, |
| void *(*get_next_fn)(const void *)) |
| { |
| while (n-- && list) |
| list = get_next_fn(list); |
| return list; |
| } |
| |
| static void *pop_item(struct mergesort_sublist *l, |
| void *(*get_next_fn)(const void *)) |
| { |
| void *p = l->ptr; |
| l->ptr = get_next_fn(l->ptr); |
| l->len = l->ptr ? (l->len - 1) : 0; |
| return p; |
| } |
| |
| void *llist_mergesort(void *list, |
| void *(*get_next_fn)(const void *), |
| void (*set_next_fn)(void *, void *), |
| int (*compare_fn)(const void *, const void *)) |
| { |
| unsigned long l; |
| |
| if (!list) |
| return NULL; |
| for (l = 1; ; l *= 2) { |
| void *curr; |
| struct mergesort_sublist p, q; |
| |
| p.ptr = list; |
| q.ptr = get_nth_next(p.ptr, l, get_next_fn); |
| if (!q.ptr) |
| break; |
| p.len = q.len = l; |
| |
| if (compare_fn(p.ptr, q.ptr) > 0) |
| list = curr = pop_item(&q, get_next_fn); |
| else |
| list = curr = pop_item(&p, get_next_fn); |
| |
| while (p.ptr) { |
| while (p.len || q.len) { |
| void *prev = curr; |
| |
| if (!p.len) |
| curr = pop_item(&q, get_next_fn); |
| else if (!q.len) |
| curr = pop_item(&p, get_next_fn); |
| else if (compare_fn(p.ptr, q.ptr) > 0) |
| curr = pop_item(&q, get_next_fn); |
| else |
| curr = pop_item(&p, get_next_fn); |
| set_next_fn(prev, curr); |
| } |
| p.ptr = q.ptr; |
| p.len = l; |
| q.ptr = get_nth_next(p.ptr, l, get_next_fn); |
| q.len = q.ptr ? l : 0; |
| |
| } |
| set_next_fn(curr, NULL); |
| } |
| return list; |
| } |