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