skiplist support stable sort
parent
159d69983b
commit
a6ef605c6e
2
HISTORY
2
HISTORY
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
Version 1.24 2015-12-25
|
Version 1.24 2015-12-25
|
||||||
* php extension compiled on PHP 7
|
* php extension compiled on PHP 7
|
||||||
* add skiplist
|
* add skiplist which support stable sort
|
||||||
* make.sh: use sed to replace perl
|
* make.sh: use sed to replace perl
|
||||||
|
|
||||||
Version 1.23 2015-11-16
|
Version 1.23 2015-11-16
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,12 @@
|
||||||
#include "skiplist.h"
|
#include "skiplist.h"
|
||||||
|
|
||||||
int skiplist_init_ex(Skiplist *sl, const int level_count,
|
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 bytes;
|
||||||
int element_size;
|
int element_size;
|
||||||
int i;
|
int i;
|
||||||
|
int alloc_elements_once;
|
||||||
int result;
|
int result;
|
||||||
struct fast_mblock_man *top_mblock;
|
struct fast_mblock_man *top_mblock;
|
||||||
|
|
||||||
|
|
@ -33,7 +34,7 @@ int skiplist_init_ex(Skiplist *sl, const int level_count,
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level_count > 32) {
|
if (level_count > 20) {
|
||||||
logError("file: "__FILE__", line: %d, "
|
logError("file: "__FILE__", line: %d, "
|
||||||
"level count: %d is too large",
|
"level count: %d is too large",
|
||||||
__LINE__, level_count);
|
__LINE__, level_count);
|
||||||
|
|
@ -50,13 +51,24 @@ int skiplist_init_ex(Skiplist *sl, const int level_count,
|
||||||
}
|
}
|
||||||
memset(sl->mblocks, 0, bytes);
|
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);
|
element_size = sizeof(SkiplistNode) + sizeof(SkiplistNode *) * (i + 1);
|
||||||
if ((result=fast_mblock_init_ex(sl->mblocks + i,
|
if ((result=fast_mblock_init_ex(sl->mblocks + i,
|
||||||
element_size, alloc_elements_once, NULL, false)) != 0)
|
element_size, alloc_elements_once, NULL, false)) != 0)
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
if (alloc_elements_once < 1024 * 1024) {
|
||||||
|
alloc_elements_once *= 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sl->top_level_index = level_count - 1;
|
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);
|
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->level_count = level_count;
|
||||||
sl->compare_func = compare_func;
|
sl->compare_func = compare_func;
|
||||||
|
|
||||||
|
|
@ -109,7 +132,7 @@ int skiplist_insert(Skiplist *sl, void *data)
|
||||||
int level_index;
|
int level_index;
|
||||||
SkiplistNode *node;
|
SkiplistNode *node;
|
||||||
SkiplistNode *previous;
|
SkiplistNode *previous;
|
||||||
SkiplistNode *current;
|
SkiplistNode *current = NULL;
|
||||||
|
|
||||||
level_index = skiplist_get_level_index(sl);
|
level_index = skiplist_get_level_index(sl);
|
||||||
node = (SkiplistNode *)fast_mblock_alloc_object(sl->mblocks + level_index);
|
node = (SkiplistNode *)fast_mblock_alloc_object(sl->mblocks + level_index);
|
||||||
|
|
@ -119,16 +142,16 @@ int skiplist_insert(Skiplist *sl, void *data)
|
||||||
|
|
||||||
previous = sl->top;
|
previous = sl->top;
|
||||||
for (i=sl->top_level_index; i>level_index; i--) {
|
for (i=sl->top_level_index; i>level_index; i--) {
|
||||||
while (previous->links[i] != NULL && sl->compare_func(data,
|
while (previous->links[i] != sl->tail && sl->compare_func(data,
|
||||||
previous->links[i]->data) > 0)
|
previous->links[i]->data) < 0)
|
||||||
{
|
{
|
||||||
previous = previous->links[i];
|
previous = previous->links[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (i >= 0) {
|
while (i >= 0) {
|
||||||
while (previous->links[i] != NULL && sl->compare_func(data,
|
while (previous->links[i] != sl->tail && sl->compare_func(data,
|
||||||
previous->links[i]->data) > 0)
|
previous->links[i]->data) < 0)
|
||||||
{
|
{
|
||||||
previous = previous->links[i];
|
previous = previous->links[i];
|
||||||
}
|
}
|
||||||
|
|
@ -140,6 +163,8 @@ int skiplist_insert(Skiplist *sl, void *data)
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node->prev = previous;
|
||||||
|
current->prev = node;
|
||||||
node->data = data;
|
node->data = data;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -155,9 +180,9 @@ static SkiplistNode *skiplist_get_previous(Skiplist *sl, void *data,
|
||||||
found = NULL;
|
found = NULL;
|
||||||
previous = sl->top;
|
previous = sl->top;
|
||||||
for (i=sl->top_level_index; i>=0; i--) {
|
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);
|
cmp = sl->compare_func(data, previous->links[i]->data);
|
||||||
if (cmp < 0) {
|
if (cmp > 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (cmp == 0) {
|
else if (cmp == 0) {
|
||||||
|
|
@ -188,8 +213,8 @@ int skiplist_delete(Skiplist *sl, void *data)
|
||||||
|
|
||||||
deleted = previous->links[level_index];
|
deleted = previous->links[level_index];
|
||||||
for (i=level_index; i>=0; i--) {
|
for (i=level_index; i>=0; i--) {
|
||||||
while (previous->links[i] != NULL && sl->compare_func(data,
|
while (previous->links[i] != sl->tail && sl->compare_func(data,
|
||||||
previous->links[i]->data) > 0)
|
previous->links[i]->data) < 0)
|
||||||
{
|
{
|
||||||
previous = previous->links[i];
|
previous = previous->links[i];
|
||||||
}
|
}
|
||||||
|
|
@ -198,6 +223,8 @@ int skiplist_delete(Skiplist *sl, void *data)
|
||||||
previous->links[i] = previous->links[i]->links[i];
|
previous->links[i] = previous->links[i]->links[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleted->links[0]->prev = previous;
|
||||||
|
|
||||||
fast_mblock_free_object(sl->mblocks + level_index, deleted);
|
fast_mblock_free_object(sl->mblocks + level_index, deleted);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
//skiplist.h
|
//skiplist.h, support stable sort :)
|
||||||
#ifndef _SKIPLIST_H
|
#ifndef _SKIPLIST_H
|
||||||
#define _SKIPLIST_H
|
#define _SKIPLIST_H
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ typedef int (*skiplist_compare_func)(const void *p1, const void *p2);
|
||||||
typedef struct skiplist_node
|
typedef struct skiplist_node
|
||||||
{
|
{
|
||||||
void *data;
|
void *data;
|
||||||
// struct skiplist_node *prev; //for stable sort
|
struct skiplist_node *prev; //for stable sort
|
||||||
struct skiplist_node *links[0];
|
struct skiplist_node *links[0];
|
||||||
} SkiplistNode;
|
} SkiplistNode;
|
||||||
|
|
||||||
|
|
@ -32,9 +32,11 @@ typedef struct skiplist
|
||||||
skiplist_compare_func compare_func;
|
skiplist_compare_func compare_func;
|
||||||
struct fast_mblock_man *mblocks; //node allocators
|
struct fast_mblock_man *mblocks; //node allocators
|
||||||
SkiplistNode *top; //the top node
|
SkiplistNode *top; //the top node
|
||||||
|
SkiplistNode *tail; //the tail node for interator
|
||||||
} Skiplist;
|
} Skiplist;
|
||||||
|
|
||||||
typedef struct skiplist_iterator {
|
typedef struct skiplist_iterator {
|
||||||
|
Skiplist *sl;
|
||||||
SkiplistNode *current;
|
SkiplistNode *current;
|
||||||
} SkiplistIterator;
|
} SkiplistIterator;
|
||||||
|
|
||||||
|
|
@ -42,11 +44,14 @@ typedef struct skiplist_iterator {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define SKIPLIST_DEFAULT_MIN_ALLOC_ELEMENTS_ONCE 128
|
||||||
|
|
||||||
#define skiplist_init(sl, level_count, compare_func) \
|
#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,
|
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);
|
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)
|
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)
|
static inline void *skiplist_next(SkiplistIterator *iterator)
|
||||||
{
|
{
|
||||||
void *data;
|
void *data;
|
||||||
|
|
||||||
if (iterator->current == NULL) {
|
if (iterator->current == iterator->sl->top) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = iterator->current->data;
|
data = iterator->current->data;
|
||||||
iterator->current = iterator->current->links[0];
|
iterator->current = iterator->current->prev;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,72 @@ static void test_delete()
|
||||||
assert(i==0);
|
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 main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -117,6 +183,7 @@ int main(int argc, char *argv[])
|
||||||
numbers[index2] = tmp;
|
numbers[index2] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fast_mblock_manager_init();
|
||||||
result = skiplist_init_ex(&sl, 12, compare_func, 128);
|
result = skiplist_init_ex(&sl, 12, compare_func, 128);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -125,6 +192,8 @@ int main(int argc, char *argv[])
|
||||||
test_insert();
|
test_insert();
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
|
fast_mblock_manager_stat_print(false);
|
||||||
|
|
||||||
test_delete();
|
test_delete();
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
|
|
@ -135,6 +204,8 @@ int main(int argc, char *argv[])
|
||||||
printf("\n");
|
printf("\n");
|
||||||
skiplist_destroy(&sl);
|
skiplist_destroy(&sl);
|
||||||
|
|
||||||
|
test_stable_sort();
|
||||||
|
|
||||||
printf("pass OK\n");
|
printf("pass OK\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue