diff --git a/HISTORY b/HISTORY index e9a3877..b74e877 100644 --- a/HISTORY +++ b/HISTORY @@ -1,7 +1,7 @@ Version 1.24 2015-12-25 * php extension compiled on PHP 7 - * add skiplist + * add skiplist which support stable sort * make.sh: use sed to replace perl Version 1.23 2015-11-16 diff --git a/src/skiplist.c b/src/skiplist.c index debc650..f77ff71 100644 --- a/src/skiplist.c +++ b/src/skiplist.c @@ -18,11 +18,12 @@ #include "skiplist.h" int skiplist_init_ex(Skiplist *sl, const int level_count, - skiplist_compare_func compare_func, const int alloc_elements_once) + skiplist_compare_func compare_func, const int min_alloc_elements_once) { int bytes; int element_size; int i; + int alloc_elements_once; int result; struct fast_mblock_man *top_mblock; @@ -33,7 +34,7 @@ int skiplist_init_ex(Skiplist *sl, const int level_count, return EINVAL; } - if (level_count > 32) { + if (level_count > 20) { logError("file: "__FILE__", line: %d, " "level count: %d is too large", __LINE__, level_count); @@ -50,13 +51,24 @@ int skiplist_init_ex(Skiplist *sl, const int level_count, } memset(sl->mblocks, 0, bytes); - for (i=0; i 1024) { + alloc_elements_once = 1024; + } + + for (i=level_count-1; i>=0; i--) { element_size = sizeof(SkiplistNode) + sizeof(SkiplistNode *) * (i + 1); if ((result=fast_mblock_init_ex(sl->mblocks + i, element_size, alloc_elements_once, NULL, false)) != 0) { return result; } + if (alloc_elements_once < 1024 * 1024) { + alloc_elements_once *= 2; + } } sl->top_level_index = level_count - 1; @@ -67,6 +79,17 @@ int skiplist_init_ex(Skiplist *sl, const int level_count, } memset(sl->top, 0, top_mblock->info.element_size); + sl->tail = (SkiplistNode *)fast_mblock_alloc_object(sl->mblocks + 0); + if (sl->tail == NULL) { + return ENOMEM; + } + memset(sl->tail, 0, sl->mblocks[0].info.element_size); + + sl->tail->prev = sl->top; + for (i=0; itop->links[i] = sl->tail; + } + sl->level_count = level_count; sl->compare_func = compare_func; @@ -109,7 +132,7 @@ int skiplist_insert(Skiplist *sl, void *data) int level_index; SkiplistNode *node; SkiplistNode *previous; - SkiplistNode *current; + SkiplistNode *current = NULL; level_index = skiplist_get_level_index(sl); node = (SkiplistNode *)fast_mblock_alloc_object(sl->mblocks + level_index); @@ -119,16 +142,16 @@ int skiplist_insert(Skiplist *sl, void *data) previous = sl->top; for (i=sl->top_level_index; i>level_index; i--) { - while (previous->links[i] != NULL && sl->compare_func(data, - previous->links[i]->data) > 0) + while (previous->links[i] != sl->tail && sl->compare_func(data, + previous->links[i]->data) < 0) { previous = previous->links[i]; } } while (i >= 0) { - while (previous->links[i] != NULL && sl->compare_func(data, - previous->links[i]->data) > 0) + while (previous->links[i] != sl->tail && sl->compare_func(data, + previous->links[i]->data) < 0) { previous = previous->links[i]; } @@ -140,6 +163,8 @@ int skiplist_insert(Skiplist *sl, void *data) i--; } + node->prev = previous; + current->prev = node; node->data = data; return 0; } @@ -155,9 +180,9 @@ static SkiplistNode *skiplist_get_previous(Skiplist *sl, void *data, found = NULL; previous = sl->top; for (i=sl->top_level_index; i>=0; i--) { - while (previous->links[i] != NULL) { + while (previous->links[i] != sl->tail) { cmp = sl->compare_func(data, previous->links[i]->data); - if (cmp < 0) { + if (cmp > 0) { break; } else if (cmp == 0) { @@ -188,8 +213,8 @@ int skiplist_delete(Skiplist *sl, void *data) deleted = previous->links[level_index]; for (i=level_index; i>=0; i--) { - while (previous->links[i] != NULL && sl->compare_func(data, - previous->links[i]->data) > 0) + while (previous->links[i] != sl->tail && sl->compare_func(data, + previous->links[i]->data) < 0) { previous = previous->links[i]; } @@ -198,6 +223,8 @@ int skiplist_delete(Skiplist *sl, void *data) previous->links[i] = previous->links[i]->links[i]; } + deleted->links[0]->prev = previous; + fast_mblock_free_object(sl->mblocks + level_index, deleted); return 0; } diff --git a/src/skiplist.h b/src/skiplist.h index 5566818..95849ee 100644 --- a/src/skiplist.h +++ b/src/skiplist.h @@ -6,7 +6,7 @@ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail. **/ -//skiplist.h +//skiplist.h, support stable sort :) #ifndef _SKIPLIST_H #define _SKIPLIST_H @@ -21,7 +21,7 @@ typedef int (*skiplist_compare_func)(const void *p1, const void *p2); typedef struct skiplist_node { void *data; -// struct skiplist_node *prev; //for stable sort + struct skiplist_node *prev; //for stable sort struct skiplist_node *links[0]; } SkiplistNode; @@ -32,9 +32,11 @@ typedef struct skiplist skiplist_compare_func compare_func; struct fast_mblock_man *mblocks; //node allocators SkiplistNode *top; //the top node + SkiplistNode *tail; //the tail node for interator } Skiplist; typedef struct skiplist_iterator { + Skiplist *sl; SkiplistNode *current; } SkiplistIterator; @@ -42,11 +44,14 @@ typedef struct skiplist_iterator { extern "C" { #endif +#define SKIPLIST_DEFAULT_MIN_ALLOC_ELEMENTS_ONCE 128 + #define skiplist_init(sl, level_count, compare_func) \ - skiplist_init_ex(sl, level_count, compare_func, 1024) + skiplist_init_ex(sl, level_count, compare_func, \ + SKIPLIST_DEFAULT_MIN_ALLOC_ELEMENTS_ONCE) int skiplist_init_ex(Skiplist *sl, const int level_count, - skiplist_compare_func compare_func, const int alloc_elements_once); + skiplist_compare_func compare_func, const int min_alloc_elements_once); void skiplist_destroy(Skiplist *sl); @@ -56,19 +61,20 @@ void *skiplist_find(Skiplist *sl, void *data); static inline void skiplist_iterator(Skiplist *sl, SkiplistIterator *iterator) { - iterator->current = sl->top->links[0]; + iterator->sl = sl; + iterator->current = sl->tail->prev; } static inline void *skiplist_next(SkiplistIterator *iterator) { void *data; - if (iterator->current == NULL) { + if (iterator->current == iterator->sl->top) { return NULL; } data = iterator->current->data; - iterator->current = iterator->current->links[0]; + iterator->current = iterator->current->prev; return data; } diff --git a/src/tests/test_skiplist.c b/src/tests/test_skiplist.c index 4a9c59d..f4fb9dd 100644 --- a/src/tests/test_skiplist.c +++ b/src/tests/test_skiplist.c @@ -91,6 +91,72 @@ static void test_delete() assert(i==0); } +typedef struct record +{ + int line; + int key; +} Record; + +static int compare_record(const void *p1, const void *p2) +{ + return ((Record *)p1)->key - ((Record *)p2)->key; +} + +static int test_stable_sort() +{ +#define RECORDS 20 + int i; + int result; + int index1; + int index2; + Skiplist sl; + SkiplistIterator iterator; + Record records[RECORDS]; + Record *record; + void *value; + + result = skiplist_init_ex(&sl, 16, compare_record, 128); + if (result != 0) { + return result; + } + + for (i=0; ikey == records[i].key); + } + + i = 0; + skiplist_iterator(&sl, &iterator); + while ((value=skiplist_next(&iterator)) != NULL) { + i++; + record = (Record *)value; + printf("%d => #%d\n", record->key, record->line); + } + assert(i==RECORDS); + + skiplist_destroy(&sl); + return 0; +} + int main(int argc, char *argv[]) { int i; @@ -117,6 +183,7 @@ int main(int argc, char *argv[]) numbers[index2] = tmp; } + fast_mblock_manager_init(); result = skiplist_init_ex(&sl, 12, compare_func, 128); if (result != 0) { return result; @@ -125,6 +192,8 @@ int main(int argc, char *argv[]) test_insert(); printf("\n"); + fast_mblock_manager_stat_print(false); + test_delete(); printf("\n"); @@ -135,6 +204,8 @@ int main(int argc, char *argv[]) printf("\n"); skiplist_destroy(&sl); + test_stable_sort(); + printf("pass OK\n"); return 0; }