skiplist support stable sort
parent
159d69983b
commit
a6ef605c6e
2
HISTORY
2
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
|
||||
|
|
|
|||
|
|
@ -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<level_count; i++) {
|
||||
alloc_elements_once = min_alloc_elements_once;
|
||||
if (alloc_elements_once <= 0) {
|
||||
alloc_elements_once = SKIPLIST_DEFAULT_MIN_ALLOC_ELEMENTS_ONCE;
|
||||
}
|
||||
else if (alloc_elements_once > 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; i<level_count; i++) {
|
||||
sl->top->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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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; i<RECORDS; i++) {
|
||||
records[i].line = i + 1;
|
||||
records[i].key = i + 1;
|
||||
}
|
||||
|
||||
for (i=0; i<RECORDS/4; i++) {
|
||||
index1 = (RECORDS - 1) * (int64_t)rand() / (int64_t)RAND_MAX;
|
||||
index2 = RECORDS - 1 - index1;
|
||||
if (index1 != index2) {
|
||||
records[index1].key = records[index2].key;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<RECORDS; i++) {
|
||||
if ((result=skiplist_insert(&sl, records + i)) != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<RECORDS; i++) {
|
||||
value = skiplist_find(&sl, records + i);
|
||||
assert(value != NULL && ((Record *)value)->key == 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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue