| #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; | |
| } |