diff options
-rw-r--r-- | libdimension/dimension/list.h | 70 | ||||
-rw-r--r-- | libdimension/list.c | 57 |
2 files changed, 109 insertions, 18 deletions
diff --git a/libdimension/dimension/list.h b/libdimension/dimension/list.h index 5c7d700..8143e8b 100644 --- a/libdimension/dimension/list.h +++ b/libdimension/dimension/list.h @@ -25,6 +25,7 @@ #ifndef DIMENSION_LIST_H #define DIMENSION_LIST_H +#include <stdbool.h> #include <stdlib.h> #include <string.h> @@ -128,25 +129,52 @@ dmnsn_list_set(dmnsn_list_iterator *i, const void *obj) memcpy(i->ptr, obj, i->obj_size); } -/* Insert an item before `i' */ +/* Swap two iterators */ DMNSN_INLINE void -dmnsn_list_insert(dmnsn_list *list, dmnsn_list_iterator *i, const void *obj) +dmnsn_list_swap(dmnsn_list_iterator *a, dmnsn_list_iterator *b) +{ + dmnsn_list_iterator temp = *a; + *a = *b; + *b = temp; +} + +/* Insert `j' before `i' */ +DMNSN_INLINE void +dmnsn_list_iterator_insert(dmnsn_list *list, + dmnsn_list_iterator *i, dmnsn_list_iterator *j) { - dmnsn_list_iterator *j = dmnsn_new_list_iterator(obj, list->obj_size); - dmnsn_assert(i, "NULL list iterator."); if (list->first == i) list->first = j; + + if (i) { + j->prev = i->prev; + i->prev = j; + } else { + if (list->last) { + j->prev = list->last; + j->prev->next = j; + } + list->last = j; + } + j->next = i; - j->prev = i->prev; - i->prev = j; ++list->length; } -/* Remove the specified item */ +/* Insert an item before `i' (NULL means at the end) */ DMNSN_INLINE void -dmnsn_list_remove(dmnsn_list *list, dmnsn_list_iterator *i) +dmnsn_list_insert(dmnsn_list *list, dmnsn_list_iterator *i, const void *obj) +{ + dmnsn_list_iterator *j = dmnsn_new_list_iterator(obj, list->obj_size); + dmnsn_list_iterator_insert(list, i, j); +} + +/* Remove the given iterator */ +DMNSN_INLINE void +dmnsn_list_iterator_remove(dmnsn_list *list, dmnsn_list_iterator *i) { dmnsn_assert(i, "NULL list iterator."); + dmnsn_assert(list->first, "List is empty."); if (list->first == i) list->first = i->next; if (list->last == i) @@ -155,23 +183,22 @@ dmnsn_list_remove(dmnsn_list *list, dmnsn_list_iterator *i) i->prev->next = i->next; if (i->next) i->next->prev = i->prev; - dmnsn_delete_list_iterator(i); --list->length; } +/* Remove the specified item */ +DMNSN_INLINE void +dmnsn_list_remove(dmnsn_list *list, dmnsn_list_iterator *i) +{ + dmnsn_list_iterator_remove(list, i); + dmnsn_delete_list_iterator(i); +} + /* Push obj to the end of the list */ DMNSN_INLINE void dmnsn_list_push(dmnsn_list *list, const void *obj) { - dmnsn_list_iterator *i = dmnsn_new_list_iterator(obj, list->obj_size); - if (!list->first) { - list->first = i; - } else { - i->prev = list->last; - i->prev->next = i; - } - list->last = i; - ++list->length; + dmnsn_list_insert(list, NULL, obj); } /* Pop obj from the end of the list */ @@ -183,4 +210,11 @@ dmnsn_list_pop(dmnsn_list *list, void *obj) dmnsn_list_remove(list, list->last); } +/* Splits a list in half, and returns the second half */ +dmnsn_list *dmnsn_list_split(dmnsn_list *list); +/* Sort a list */ +typedef bool dmnsn_comparator_fn(dmnsn_list_iterator *l, + dmnsn_list_iterator *r); +void dmnsn_list_sort(dmnsn_list *list, dmnsn_comparator_fn *comparator); + #endif /* DIMENSION_LIST_H */ diff --git a/libdimension/list.c b/libdimension/list.c index 0303513..f6a1f5d 100644 --- a/libdimension/list.c +++ b/libdimension/list.c @@ -43,3 +43,60 @@ dmnsn_delete_list(dmnsn_list *list) free(list); } } + +dmnsn_list * +dmnsn_list_split(dmnsn_list *list) +{ + dmnsn_list *half = dmnsn_new_list(list->obj_size); + + /* Find the halfway point */ + size_t i, max = dmnsn_list_size(list)/2; + dmnsn_list_iterator *ii; + for (i = 0, ii = dmnsn_list_last(list); + i < max && ii; + ++i, ii = dmnsn_list_prev(ii)); + + if (ii && ii->next) { + half->first = ii->next; + half->last = list->last; + list->last = ii; + half->first->prev = NULL; + list->last->next = NULL; + } + + list->length -= max; + half->length = max; + + return half; +} + +void +dmnsn_list_sort(dmnsn_list *list, dmnsn_comparator_fn *comparator) +{ + /* Recursive merge sort */ + if (dmnsn_list_size(list) < 2) { + return; + } else if (dmnsn_list_size(list) == 2) { + if ((*comparator)(list->last, list->first)) { + dmnsn_list_swap(list->first, list->last); + } + } else { + dmnsn_list *half = dmnsn_list_split(list); + dmnsn_list_sort(list, comparator); + dmnsn_list_sort(half, comparator); + + dmnsn_list_iterator *i = list->first, *j = half->first; + while (i || j) { + if (!i || (*comparator)(j, i)) { + dmnsn_list_iterator *temp = dmnsn_list_next(j); + dmnsn_list_iterator_remove(half, j); + dmnsn_list_iterator_insert(list, i, j); + j = temp; + } else { + i = dmnsn_list_next(i); + } + } + + dmnsn_delete_list(half); + } +} |