add skiplist_set.[hc] and skiplist bug fixed

pull/37/head
yuqing 2018-05-29 18:10:50 +08:00
parent 6d9d71005a
commit fd894d810b
14 changed files with 877 additions and 123 deletions

View File

@ -1,11 +1,12 @@
Version 1.38 2018-05-21
Version 1.38 2018-05-29
* connection_pool.c: set err_no to 0 when success
* shared_func.h: add functions float2buff / buff2float, double2buff / buff2double
* logger.h: add function log_get_level_caption
* add files: common_blocked_queue.[hc]
* add files: multi_socket_client.[hc]
* ioevent.[hc]: remove care_events in FreeBSD or MacOS
* add skiplist_set.[hc] and skiplist bug fixed
Version 1.37 2018-02-24
* ini_file_reader.c function annotations LOCAL_IP_GET support index, such as:

View File

@ -14,7 +14,7 @@ FAST_SHARED_OBJS = hash.lo chain.lo shared_func.lo ini_file_reader.lo \
fast_buffer.lo multi_skiplist.lo flat_skiplist.lo \
system_info.lo fast_blocked_queue.lo id_generator.lo \
char_converter.lo char_convert_loader.lo common_blocked_queue.lo \
multi_socket_client.lo
multi_socket_client.lo skiplist_set.lo
FAST_STATIC_OBJS = hash.o chain.o shared_func.o ini_file_reader.o \
logger.o sockopt.o base64.o sched_thread.o \
@ -25,7 +25,7 @@ FAST_STATIC_OBJS = hash.o chain.o shared_func.o ini_file_reader.o \
fast_buffer.o multi_skiplist.o flat_skiplist.o \
system_info.o fast_blocked_queue.o id_generator.o \
char_converter.o char_convert_loader.o common_blocked_queue.o \
multi_socket_client.o
multi_socket_client.o skiplist_set.o
HEADER_FILES = common_define.h hash.h chain.h logger.h base64.h \
shared_func.h pthread_func.h ini_file_reader.h _os_define.h \
@ -37,7 +37,7 @@ HEADER_FILES = common_define.h hash.h chain.h logger.h base64.h \
skiplist_common.h system_info.h fast_blocked_queue.h \
php7_ext_wrapper.h id_generator.h char_converter.h \
char_convert_loader.h common_blocked_queue.h \
multi_socket_client.h
multi_socket_client.h skiplist_set.h
ALL_OBJS = $(FAST_STATIC_OBJS) $(FAST_SHARED_OBJS)

View File

@ -42,6 +42,15 @@ int flat_skiplist_init_ex(FlatSkiplist *sl, const int level_count,
return E2BIG;
}
bytes = sizeof(FlatSkiplistNode *) * level_count;
sl->tmp_previous = (FlatSkiplistNode **)malloc(bytes);
if (sl->tmp_previous == NULL) {
logError("file: "__FILE__", line: %d, "
"malloc %d bytes fail, errno: %d, error info: %s",
__LINE__, bytes, errno, STRERROR(errno));
return errno != 0 ? errno : ENOMEM;
}
bytes = sizeof(struct fast_mblock_man) * level_count;
sl->mblocks = (struct fast_mblock_man *)malloc(bytes);
if (sl->mblocks == NULL) {
@ -145,13 +154,13 @@ int flat_skiplist_insert(FlatSkiplist *sl, void *data)
int level_index;
FlatSkiplistNode *node;
FlatSkiplistNode *previous;
FlatSkiplistNode *current = NULL;
level_index = flat_skiplist_get_level_index(sl);
node = (FlatSkiplistNode *)fast_mblock_alloc_object(sl->mblocks + level_index);
if (node == NULL) {
return ENOMEM;
}
node->data = data;
previous = sl->top;
for (i=sl->top_level_index; i>level_index; i--) {
@ -169,16 +178,20 @@ int flat_skiplist_insert(FlatSkiplist *sl, void *data)
previous = previous->links[i];
}
current = previous->links[i];
previous->links[i] = node;
node->links[i] = current;
sl->tmp_previous[i] = previous;
i--;
}
//set previous links of level 0
node->prev = previous;
current->prev = node;
node->data = data;
previous->links[0]->prev = node;
//thread safe for one write with many read model
for (i=0; i<=level_index; i++) {
node->links[i] = sl->tmp_previous[i]->links[i];
sl->tmp_previous[i]->links[i] = node;
}
return 0;
}
@ -188,9 +201,7 @@ static FlatSkiplistNode *flat_skiplist_get_previous(FlatSkiplist *sl, void *data
int i;
int cmp;
FlatSkiplistNode *previous;
FlatSkiplistNode *found;
found = NULL;
previous = sl->top;
for (i=sl->top_level_index; i>=0; i--) {
while (previous->links[i] != sl->tail) {
@ -199,17 +210,15 @@ static FlatSkiplistNode *flat_skiplist_get_previous(FlatSkiplist *sl, void *data
break;
}
else if (cmp == 0) {
found = previous;
*level_index = i;
goto DONE;
return previous;
}
previous = previous->links[i];
}
}
DONE:
return found;
return NULL;
}
int flat_skiplist_delete(FlatSkiplist *sl, void *data)
@ -226,13 +235,11 @@ int flat_skiplist_delete(FlatSkiplist *sl, void *data)
deleted = previous->links[level_index];
for (i=level_index; i>=0; i--) {
while (previous->links[i] != sl->tail && sl->compare_func(data,
previous->links[i]->data) < 0)
{
while (previous->links[i] != sl->tail && previous->links[i] != deleted) {
previous = previous->links[i];
}
assert(sl->compare_func(data, previous->links[i]->data) == 0);
assert(previous->links[i] == deleted);
previous->links[i] = previous->links[i]->links[i];
}
@ -277,13 +284,17 @@ int flat_skiplist_find_all(FlatSkiplist *sl, void *data, FlatSkiplistIterator *i
return ENOENT;
}
last = previous->links[0]->links[0];
previous = previous->links[level_index];
last = previous->links[0];
while (last != sl->tail && sl->compare_func(data, last->data) == 0) {
last = last->links[0];
}
do {
previous = previous->prev;
} while (previous != sl->top && sl->compare_func(data, previous->data) == 0);
iterator->top = previous;
iterator->current = last->prev;
return 0;
}

View File

@ -33,6 +33,7 @@ typedef struct flat_skiplist
struct fast_mblock_man *mblocks; //node allocators
FlatSkiplistNode *top; //the top node
FlatSkiplistNode *tail; //the tail node for interator
FlatSkiplistNode **tmp_previous; //thread safe for insert
} FlatSkiplist;
typedef struct flat_skiplist_iterator {
@ -79,6 +80,33 @@ static inline void *flat_skiplist_next(FlatSkiplistIterator *iterator)
return data;
}
static inline bool flat_skiplist_empty(FlatSkiplist *sl)
{
return sl->top->links[0] == sl->tail;
}
typedef const char * (*flat_skiplist_tostring_func)(void *data, char *buff, const int size);
static inline void flat_skiplist_print(FlatSkiplist *sl, flat_skiplist_tostring_func tostring_func)
{
int i;
FlatSkiplistNode *current;
char buff[1024];
printf("###################\n");
for (i=sl->top_level_index; i>=0; i--) {
printf("level %d: ", i);
current = sl->top->links[i];
while (current != sl->tail) {
printf("%s ", tostring_func(current->data, buff, sizeof(buff)));
current = current->links[i];
}
printf("\n");
}
printf("###################\n");
printf("\n");
}
#ifdef __cplusplus
}
#endif

View File

@ -43,6 +43,15 @@ int multi_skiplist_init_ex(MultiSkiplist *sl, const int level_count,
return E2BIG;
}
bytes = sizeof(MultiSkiplistNode *) * level_count;
sl->tmp_previous = (MultiSkiplistNode **)malloc(bytes);
if (sl->tmp_previous == NULL) {
logError("file: "__FILE__", line: %d, "
"malloc %d bytes fail, errno: %d, error info: %s",
__LINE__, bytes, errno, STRERROR(errno));
return errno != 0 ? errno : ENOMEM;
}
bytes = sizeof(struct fast_mblock_man) * level_count;
sl->mblocks = (struct fast_mblock_man *)malloc(bytes);
if (sl->mblocks == NULL) {
@ -149,9 +158,7 @@ static MultiSkiplistNode *multi_skiplist_get_previous(MultiSkiplist *sl, void *d
int i;
int cmp;
MultiSkiplistNode *previous;
MultiSkiplistNode *found;
found = NULL;
previous = sl->top;
for (i=sl->top_level_index; i>=0; i--) {
while (previous->links[i] != sl->tail) {
@ -160,17 +167,15 @@ static MultiSkiplistNode *multi_skiplist_get_previous(MultiSkiplist *sl, void *d
break;
}
else if (cmp == 0) {
found = previous;
*level_index = i;
goto DONE;
return previous;
}
previous = previous->links[i];
}
}
DONE:
return found;
return NULL;
}
static inline void multi_skiplist_free_data_node(MultiSkiplist *sl,
@ -202,7 +207,6 @@ int multi_skiplist_insert(MultiSkiplist *sl, void *data)
MultiSkiplistData *dataNode;
MultiSkiplistNode *node;
MultiSkiplistNode *previous;
MultiSkiplistNode *current = NULL;
dataNode = (MultiSkiplistData *)fast_mblock_alloc_object(&sl->data_mblock);
if (dataNode == NULL) {
@ -242,15 +246,19 @@ int multi_skiplist_insert(MultiSkiplist *sl, void *data)
previous = previous->links[i];
}
current = previous->links[i];
previous->links[i] = node;
node->links[i] = current;
sl->tmp_previous[i] = previous;
i--;
}
node->head = dataNode;
node->tail = dataNode;
//thread safe for one write with many read model
for (i=0; i<=level_index; i++) {
node->links[i] = sl->tmp_previous[i]->links[i];
sl->tmp_previous[i]->links[i] = node;
}
return 0;
}
@ -283,12 +291,13 @@ int multi_skiplist_do_delete(MultiSkiplist *sl, void *data,
}
for (i=level_index; i>=0; i--) {
while (previous->links[i] != sl->tail && sl->compare_func(data,
previous->links[i]->head->data) > 0)
while (previous->links[i] != sl->tail &&
previous->links[i] != deleted)
{
previous = previous->links[i];
}
assert(previous->links[i] == deleted);
previous->links[i] = previous->links[i]->links[i];
}
@ -339,9 +348,9 @@ int multi_skiplist_find_all(MultiSkiplist *sl, void *data,
return ENOENT;
}
else {
iterator->current.node = previous;
iterator->tail = previous->links[0]->links[0];
iterator->current.node = previous->links[level_index];
iterator->tail = iterator->current.node->links[0];
iterator->current.data = iterator->current.node->head;
return 0;
}
}

View File

@ -40,6 +40,7 @@ typedef struct multi_skiplist
struct fast_mblock_man *mblocks; //node allocators
MultiSkiplistNode *top; //the top node
MultiSkiplistNode *tail; //the tail node for terminate
MultiSkiplistNode **tmp_previous; //thread safe for insert
} MultiSkiplist;
typedef struct multi_skiplist_iterator {
@ -76,8 +77,13 @@ static inline void multi_skiplist_iterator(MultiSkiplist *sl,
MultiSkiplistIterator *iterator)
{
iterator->tail = sl->tail;
iterator->current.node = sl->top;
iterator->current.node = sl->top->links[0];
if (iterator->current.node != sl->tail) {
iterator->current.data = iterator->current.node->head;
}
else {
iterator->current.data = NULL;
}
}
static inline void *multi_skiplist_next(MultiSkiplistIterator *iterator)
@ -100,6 +106,33 @@ static inline void *multi_skiplist_next(MultiSkiplistIterator *iterator)
return data;
}
static inline bool multi_skiplist_empty(MultiSkiplist *sl)
{
return sl->top->links[0] == sl->tail;
}
typedef const char * (*multi_skiplist_tostring_func)(void *data, char *buff, const int size);
static inline void multi_skiplist_print(MultiSkiplist *sl, multi_skiplist_tostring_func tostring_func)
{
int i;
MultiSkiplistNode *current;
char buff[1024];
printf("###################\n");
for (i=sl->top_level_index; i>=0; i--) {
printf("level %d: ", i);
current = sl->top->links[i];
while (current != sl->tail) {
printf("%s ", tostring_func(current->head->data, buff, sizeof(buff)));
current = current->links[i];
}
printf("\n");
}
printf("###################\n");
printf("\n");
}
#ifdef __cplusplus
}
#endif

View File

@ -96,14 +96,11 @@ static int fast_multi_sock_client_do_send(FastMultiSockClient *client,
int bytes;
int result;
logInfo("file: "__FILE__", line: %d, "
"send remain: %d", __LINE__, entry->remain);
result = 0;
while (entry->remain > 0) {
bytes = write(entry->conn->sock, entry->send_buffer->data +
(entry->send_buffer->length - entry->remain), entry->remain);
logInfo("sock: %d, write bytes: %d, remain: %d", entry->conn->sock, bytes, entry->remain);
if (bytes < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
break;
@ -217,8 +214,6 @@ static int fast_multi_sock_client_do_recv(FastMultiSockClient *client,
int bytes;
int result;
logInfo("file: "__FILE__", line: %d, "
"recv remain: %d", __LINE__, entry->remain);
result = 0;
while (entry->remain > 0) {
bytes = read(entry->conn->sock, entry->recv_buffer.data +
@ -322,9 +317,11 @@ static int fast_multi_sock_client_deal_io(FastMultiSockClient *client)
}
}
/*
logInfo("file: "__FILE__", line: %d, pulling_count: %d, "
"success_count: %d\n", __LINE__,
client->pulling_count, client->success_count);
*/
if (client->pulling_count > 0) {
int i;
for (i=0; i<client->entry_count; i++) {

View File

@ -31,17 +31,19 @@ struct fast_multi_sock_entry;
//return the body length
typedef int (*fast_multi_sock_client_get_body_length_func)(const FastBuffer *recv_buffer);
//IO deal fucntion
typedef int (*fast_multi_sock_client_io_func)(struct fast_multi_sock_client *client,
struct fast_multi_sock_entry *entry);
typedef struct fast_multi_sock_entry {
ConnectionInfo *conn;
ConnectionInfo *conn; //the socket must be non-block socket
FastBuffer *send_buffer; //send buffer for internal use
fast_multi_sock_client_io_func io_callback; //for internal use
FastBuffer recv_buffer; //recv buffer
int remain; //remain bytes
int error_no; //0 for success
bool done;
int error_no; //0 for success, != 0 fail
int remain; //remain bytes, for internal use
bool done; //for internal use
} FastMultiSockEntry;
typedef struct fast_multi_sock_client {

View File

@ -17,9 +17,11 @@
#include "skiplist_common.h"
#include "flat_skiplist.h"
#include "multi_skiplist.h"
#include "skiplist_set.h"
#define SKIPLIST_TYPE_FLAT 0
#define SKIPLIST_TYPE_MULTI 1
#define SKIPLIST_TYPE_SET 2
typedef struct skiplist
{
@ -27,6 +29,7 @@ typedef struct skiplist
union {
FlatSkiplist flat;
MultiSkiplist multi;
SkiplistSet set;
} u;
} Skiplist;
@ -35,6 +38,7 @@ typedef struct skiplist_iterator {
union {
FlatSkiplistIterator flat;
MultiSkiplistIterator multi;
SkiplistSetIterator set;
} u;
} SkiplistIterator;
@ -51,95 +55,166 @@ static inline int skiplist_init_ex(Skiplist *sl, const int level_count,
const int min_alloc_elements_once, const int type)
{
sl->type = type;
if (type == SKIPLIST_TYPE_FLAT) {
switch (type) {
case SKIPLIST_TYPE_FLAT:
return flat_skiplist_init_ex(&sl->u.flat, level_count,
compare_func, free_func, min_alloc_elements_once);
}
else {
case SKIPLIST_TYPE_MULTI:
return multi_skiplist_init_ex(&sl->u.multi, level_count,
compare_func, free_func, min_alloc_elements_once);
case SKIPLIST_TYPE_SET:
return skiplist_set_init_ex(&sl->u.set, level_count,
compare_func, free_func, min_alloc_elements_once);
default:
return EINVAL;
}
}
static inline void skiplist_destroy(Skiplist *sl)
{
if (sl->type == SKIPLIST_TYPE_FLAT) {
switch (sl->type) {
case SKIPLIST_TYPE_FLAT:
flat_skiplist_destroy(&sl->u.flat);
}
else {
break;
case SKIPLIST_TYPE_MULTI:
multi_skiplist_destroy(&sl->u.multi);
break;
case SKIPLIST_TYPE_SET:
skiplist_set_destroy(&sl->u.set);
break;
default:
break;
}
}
static inline int skiplist_insert(Skiplist *sl, void *data)
{
if (sl->type == SKIPLIST_TYPE_FLAT) {
switch (sl->type) {
case SKIPLIST_TYPE_FLAT:
return flat_skiplist_insert(&sl->u.flat, data);
}
else {
case SKIPLIST_TYPE_MULTI:
return multi_skiplist_insert(&sl->u.multi, data);
case SKIPLIST_TYPE_SET:
return skiplist_set_insert(&sl->u.set, data);
default:
return EINVAL;
}
}
static inline int skiplist_delete(Skiplist *sl, void *data)
{
if (sl->type == SKIPLIST_TYPE_FLAT) {
switch (sl->type) {
case SKIPLIST_TYPE_FLAT:
return flat_skiplist_delete(&sl->u.flat, data);
}
else {
case SKIPLIST_TYPE_MULTI:
return multi_skiplist_delete(&sl->u.multi, data);
case SKIPLIST_TYPE_SET:
return skiplist_set_delete(&sl->u.set, data);
default:
return EINVAL;
}
}
static inline int skiplist_delete_all(Skiplist *sl, void *data, int *delete_count)
{
if (sl->type == SKIPLIST_TYPE_FLAT) {
int result;
switch (sl->type) {
case SKIPLIST_TYPE_FLAT:
return flat_skiplist_delete_all(&sl->u.flat, data, delete_count);
}
else {
case SKIPLIST_TYPE_MULTI:
return multi_skiplist_delete_all(&sl->u.multi, data, delete_count);
case SKIPLIST_TYPE_SET:
result = skiplist_set_delete(&sl->u.set, data);
*delete_count = (result == 0) ? 1 : 0;
return result;
default:
return EINVAL;
}
}
static inline void *skiplist_find(Skiplist *sl, void *data)
{
if (sl->type == SKIPLIST_TYPE_FLAT) {
switch (sl->type) {
case SKIPLIST_TYPE_FLAT:
return flat_skiplist_find(&sl->u.flat, data);
}
else {
case SKIPLIST_TYPE_MULTI:
return multi_skiplist_find(&sl->u.multi, data);
case SKIPLIST_TYPE_SET:
return skiplist_set_find(&sl->u.set, data);
default:
return NULL;
}
}
static inline int skiplist_find_all(Skiplist *sl, void *data, SkiplistIterator *iterator)
{
iterator->type = sl->type;
if (sl->type == SKIPLIST_TYPE_FLAT) {
switch (sl->type) {
case SKIPLIST_TYPE_FLAT:
return flat_skiplist_find_all(&sl->u.flat, data, &iterator->u.flat);
}
else {
case SKIPLIST_TYPE_MULTI:
return multi_skiplist_find_all(&sl->u.multi, data, &iterator->u.multi);
case SKIPLIST_TYPE_SET:
return EOPNOTSUPP;
default:
return EINVAL;
}
}
static inline void skiplist_iterator(Skiplist *sl, SkiplistIterator *iterator)
{
iterator->type = sl->type;
if (sl->type == SKIPLIST_TYPE_FLAT) {
switch (sl->type) {
case SKIPLIST_TYPE_FLAT:
return flat_skiplist_iterator(&sl->u.flat, &iterator->u.flat);
}
else {
case SKIPLIST_TYPE_MULTI:
return multi_skiplist_iterator(&sl->u.multi, &iterator->u.multi);
case SKIPLIST_TYPE_SET:
return skiplist_set_iterator(&sl->u.set, &iterator->u.set);
default:
return EINVAL;
}
}
static inline void *skiplist_next(SkiplistIterator *iterator)
{
if (iterator->type == SKIPLIST_TYPE_FLAT) {
switch (iterator->type) {
case SKIPLIST_TYPE_FLAT:
return flat_skiplist_next(&iterator->u.flat);
}
else {
case SKIPLIST_TYPE_MULTI:
return multi_skiplist_next(&iterator->u.multi);
case SKIPLIST_TYPE_SET:
return skiplist_set_next(&iterator->u.set);
default:
return NULL;
}
}
static inline bool skiplist_empty(Skiplist *sl)
{
switch (sl->type) {
case SKIPLIST_TYPE_FLAT:
return flat_skiplist_empty(&sl->u.flat);
case SKIPLIST_TYPE_MULTI:
return multi_skiplist_empty(&sl->u.multi);
case SKIPLIST_TYPE_SET:
return skiplist_set_empty(&sl->u.set);
default:
return false;
}
}
static inline const char* skiplist_get_type_caption(Skiplist *sl)
{
switch (sl->type) {
case SKIPLIST_TYPE_FLAT:
return "flat";
case SKIPLIST_TYPE_MULTI:
return "multi";
case SKIPLIST_TYPE_SET:
return "set";
default:
return "unknown";
}
}

270
src/skiplist_set.c Normal file
View File

@ -0,0 +1,270 @@
/**
* Copyright (C) 2015 Happy Fish / YuQing
*
* libfastcommon may be copied only under the terms of the GNU General
* Public License V3, which may be found in the FastDFS source kit.
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
**/
//skiplist_set.c
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include "logger.h"
#include "skiplist_set.h"
int skiplist_set_init_ex(SkiplistSet *sl, const int level_count,
skiplist_compare_func compare_func, skiplist_free_func free_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;
if (level_count <= 0) {
logError("file: "__FILE__", line: %d, "
"invalid level count: %d",
__LINE__, level_count);
return EINVAL;
}
if (level_count > 30) {
logError("file: "__FILE__", line: %d, "
"level count: %d is too large",
__LINE__, level_count);
return E2BIG;
}
bytes = sizeof(SkiplistSetNode *) * level_count;
sl->tmp_previous = (SkiplistSetNode **)malloc(bytes);
if (sl->tmp_previous == NULL) {
logError("file: "__FILE__", line: %d, "
"malloc %d bytes fail, errno: %d, error info: %s",
__LINE__, bytes, errno, STRERROR(errno));
return errno != 0 ? errno : ENOMEM;
}
bytes = sizeof(struct fast_mblock_man) * level_count;
sl->mblocks = (struct fast_mblock_man *)malloc(bytes);
if (sl->mblocks == NULL) {
logError("file: "__FILE__", line: %d, "
"malloc %d bytes fail, errno: %d, error info: %s",
__LINE__, bytes, errno, STRERROR(errno));
return errno != 0 ? errno : ENOMEM;
}
memset(sl->mblocks, 0, bytes);
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(SkiplistSetNode) + sizeof(SkiplistSetNode *) * (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;
top_mblock = sl->mblocks + sl->top_level_index;
sl->top = (SkiplistSetNode *)fast_mblock_alloc_object(top_mblock);
if (sl->top == NULL) {
return ENOMEM;
}
memset(sl->top, 0, top_mblock->info.element_size);
sl->tail = (SkiplistSetNode *)fast_mblock_alloc_object(sl->mblocks + 0);
if (sl->tail == NULL) {
return ENOMEM;
}
memset(sl->tail, 0, sl->mblocks[0].info.element_size);
for (i=0; i<level_count; i++) {
sl->top->links[i] = sl->tail;
}
sl->level_count = level_count;
sl->compare_func = compare_func;
sl->free_func = free_func;
srand(time(NULL));
return 0;
}
void skiplist_set_destroy(SkiplistSet *sl)
{
int i;
SkiplistSetNode *node;
SkiplistSetNode *deleted;
if (sl->mblocks == NULL) {
return;
}
if (sl->free_func != NULL) {
node = sl->top->links[0];
while (node != sl->tail) {
deleted = node;
node = node->links[0];
sl->free_func(deleted->data);
}
}
for (i=0; i<sl->level_count; i++) {
fast_mblock_destroy(sl->mblocks + i);
}
free(sl->mblocks);
sl->mblocks = NULL;
}
static inline int skiplist_set_get_level_index(SkiplistSet *sl)
{
int i;
for (i=0; i<sl->top_level_index; i++) {
if (rand() < RAND_MAX / 2) {
break;
}
}
return i;
}
int skiplist_set_insert(SkiplistSet *sl, void *data)
{
int i;
int level_index;
int cmp;
SkiplistSetNode *node;
SkiplistSetNode *previous;
level_index = skiplist_set_get_level_index(sl);
previous = sl->top;
for (i=sl->top_level_index; i>level_index; i--) {
while (previous->links[i] != sl->tail) {
cmp = sl->compare_func(data, previous->links[i]->data);
if (cmp < 0) {
break;
}
else if (cmp == 0) {
return EEXIST;
}
previous = previous->links[i];
}
}
while (i >= 0) {
while (previous->links[i] != sl->tail) {
cmp = sl->compare_func(data, previous->links[i]->data);
if (cmp < 0) {
break;
}
else if (cmp == 0) {
return EEXIST;
}
previous = previous->links[i];
}
sl->tmp_previous[i] = previous;
i--;
}
node = (SkiplistSetNode *)fast_mblock_alloc_object(sl->mblocks + level_index);
if (node == NULL) {
return ENOMEM;
}
node->data = data;
//thread safe for one write with many read model
for (i=0; i<=level_index; i++) {
node->links[i] = sl->tmp_previous[i]->links[i];
sl->tmp_previous[i]->links[i] = node;
}
return 0;
}
static SkiplistSetNode *skiplist_set_get_previous(SkiplistSet *sl, void *data,
int *level_index)
{
int i;
int cmp;
SkiplistSetNode *previous;
previous = sl->top;
for (i=sl->top_level_index; i>=0; i--) {
while (previous->links[i] != sl->tail) {
cmp = sl->compare_func(data, previous->links[i]->data);
if (cmp < 0) {
break;
}
else if (cmp == 0) {
*level_index = i;
return previous;
}
previous = previous->links[i];
}
}
return NULL;
}
int skiplist_set_delete(SkiplistSet *sl, void *data)
{
int i;
int level_index;
SkiplistSetNode *previous;
SkiplistSetNode *deleted;
previous = skiplist_set_get_previous(sl, data, &level_index);
if (previous == NULL) {
return ENOENT;
}
deleted = previous->links[level_index];
for (i=level_index; i>=0; i--) {
while (previous->links[i] != sl->tail &&
previous->links[i] != deleted)
{
previous = previous->links[i];
}
assert(previous->links[i] == deleted);
previous->links[i] = previous->links[i]->links[i];
}
if (sl->free_func != NULL) {
sl->free_func(deleted->data);
}
fast_mblock_free_object(sl->mblocks + level_index, deleted);
return 0;
}
void *skiplist_set_find(SkiplistSet *sl, void *data)
{
int level_index;
SkiplistSetNode *previous;
previous = skiplist_set_get_previous(sl, data, &level_index);
return (previous != NULL) ? previous->links[level_index]->data : NULL;
}

90
src/skiplist_set.h Normal file
View File

@ -0,0 +1,90 @@
/**
* Copyright (C) 2015 Happy Fish / YuQing
*
* libfastcommon may be copied only under the terms of the GNU General
* Public License V3, which may be found in the FastDFS source kit.
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
**/
//skiplist_set.h, support stable sort :)
#ifndef _SKIPLIST_SET_H
#define _SKIPLIST_SET_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common_define.h"
#include "skiplist_common.h"
#include "fast_mblock.h"
typedef struct skiplist_set_node
{
void *data;
struct skiplist_set_node *links[0];
} SkiplistSetNode;
typedef struct skiplist_set
{
int level_count;
int top_level_index;
skiplist_compare_func compare_func;
skiplist_free_func free_func;
struct fast_mblock_man *mblocks; //node allocators
SkiplistSetNode *top; //the top node
SkiplistSetNode *tail; //the tail node for interator
SkiplistSetNode **tmp_previous; //thread safe for insert
} SkiplistSet;
typedef struct skiplist_set_iterator {
SkiplistSetNode *tail;
SkiplistSetNode *current;
} SkiplistSetIterator;
#ifdef __cplusplus
extern "C" {
#endif
#define skiplist_set_init(sl, level_count, compare_func, free_func) \
skiplist_set_init_ex(sl, level_count, compare_func, free_func, \
SKIPLIST_DEFAULT_MIN_ALLOC_ELEMENTS_ONCE)
int skiplist_set_init_ex(SkiplistSet *sl, const int level_count,
skiplist_compare_func compare_func, skiplist_free_func free_func,
const int min_alloc_elements_once);
void skiplist_set_destroy(SkiplistSet *sl);
int skiplist_set_insert(SkiplistSet *sl, void *data);
int skiplist_set_delete(SkiplistSet *sl, void *data);
void *skiplist_set_find(SkiplistSet *sl, void *data);
static inline void skiplist_set_iterator(SkiplistSet *sl, SkiplistSetIterator *iterator)
{
iterator->tail = sl->tail;
iterator->current = sl->top->links[0];
}
static inline void *skiplist_set_next(SkiplistSetIterator *iterator)
{
void *data;
if (iterator->current == iterator->tail) {
return NULL;
}
data = iterator->current->data;
iterator->current = iterator->current->links[0];
return data;
}
static inline bool skiplist_set_empty(SkiplistSet *sl)
{
return sl->top->links[0] == sl->tail;
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,11 +1,12 @@
.SUFFIXES: .c .o
COMPILE = $(CC) -g -O1 -Wall -D_FILE_OFFSET_BITS=64 -g -DDEBUG_FLAG
COMPILE = $(CC) -g -O3 -Wall -D_FILE_OFFSET_BITS=64 -g -DDEBUG_FLAG
INC_PATH = -I/usr/local/include
LIB_PATH = -lfastcommon -lpthread
ALL_PRGS = test_allocator test_skiplist test_multi_skiplist test_mblock test_blocked_queue \
test_id_generator test_ini_parser test_char_convert test_char_convert_loader test_logger
test_id_generator test_ini_parser test_char_convert test_char_convert_loader \
test_logger test_skiplist_set
all: $(ALL_PRGS)
.c:

View File

@ -118,6 +118,14 @@ static int compare_record(const void *p1, const void *p2)
return ((Record *)p1)->key - ((Record *)p2)->key;
}
/*
static const char * skiplist_tostring(void *data, char *buff, const int size)
{
snprintf(buff, size, "%d(%04x)", ((Record *)data)->key, (int)(((long)data) & 0xFFFFL));
return buff;
}
*/
static int test_stable_sort()
{
#define RECORDS 32
@ -127,13 +135,19 @@ static int test_stable_sort()
int index2;
int delete_count;
int total_delete_count;
int occur_count;
int max_occur_count;
int previous_key;
int max_occur_key;
Skiplist sl;
SkiplistIterator iterator;
Record records[RECORDS];
Record tmp_records[RECORDS];
Record *record;
Record target;
void *value;
printf("test_stable_sort ...\n");
instance_count = 0;
result = skiplist_init_ex(&sl, 12, compare_record,
free_test_func, 128, skiplist_type);
@ -146,6 +160,7 @@ static int test_stable_sort()
records[i].key = i + 1;
}
if (skiplist_type != SKIPLIST_TYPE_SET) {
for (i=0; i<RECORDS/4; i++) {
index1 = (RECORDS - 1) * (int64_t)rand() / (int64_t)RAND_MAX;
index2 = RECORDS - 1 - index1;
@ -153,9 +168,34 @@ static int test_stable_sort()
records[index1].key = records[index2].key;
}
}
}
memcpy(tmp_records, records, sizeof(tmp_records));
qsort(tmp_records, RECORDS, sizeof(Record), compare_record);
max_occur_count = 0;
max_occur_key = 0;
i = 0;
while (i < RECORDS) {
occur_count = 1;
previous_key = tmp_records[i].key;
i++;
while (i < RECORDS && tmp_records[i].key == previous_key) {
i++;
occur_count++;
}
if (occur_count > max_occur_count) {
max_occur_key = previous_key;
max_occur_count = occur_count;
}
}
printf("max_occur_key: %d, max_occur_count: %d\n\n", max_occur_key, max_occur_count);
for (i=0; i<RECORDS; i++) {
if ((result=skiplist_insert(&sl, records + i)) != 0) {
fprintf(stderr, "skiplist_insert insert fail, "
"errno: %d, error info: %s\n",
result, STRERROR(result));
return result;
}
instance_count++;
@ -176,8 +216,13 @@ static int test_stable_sort()
}
assert(i==RECORDS);
target.key = 10;
target.key = max_occur_key;
target.line = 0;
if (skiplist_type == SKIPLIST_TYPE_SET) {
record = (Record *)skiplist_find(&sl, &target);
assert(record != NULL && record->key == target.key);
}
else {
if (skiplist_find_all(&sl, &target, &iterator) == 0) {
printf("found key: %d\n", target.key);
}
@ -188,20 +233,44 @@ static int test_stable_sort()
printf("%d => #%d\n", record->key, record->line);
}
printf("found record count: %d\n", i);
}
/*
if (skiplist_type == SKIPLIST_TYPE_FLAT) {
flat_skiplist_print(&sl.u.flat, skiplist_tostring);
}
else if (skiplist_type == SKIPLIST_TYPE_MULTI) {
multi_skiplist_print(&sl.u.multi, skiplist_tostring);
}
*/
total_delete_count = 0;
for (i=0; i<RECORDS; i++) {
if ((result=skiplist_delete_all(&sl, records + i,
&delete_count)) == 0)
do {
delete_count = 0;
if ((result=skiplist_delete(&sl, records + i)) == 0)
{
delete_count = 1;
total_delete_count += delete_count;
}
assert((result == 0 && delete_count > 0) ||
(result != 0 && delete_count == 0));
} while (result == 0);
}
assert(total_delete_count == RECORDS);
assert(instance_count == 0);
/*
if (skiplist_type == SKIPLIST_TYPE_FLAT) {
flat_skiplist_print(&sl.u.flat, skiplist_tostring);
}
else if (skiplist_type == SKIPLIST_TYPE_MULTI) {
multi_skiplist_print(&sl.u.multi, skiplist_tostring);
}
*/
i = 0;
skiplist_iterator(&sl, &iterator);
while ((value=skiplist_next(&iterator)) != NULL) {
@ -230,9 +299,10 @@ int main(int argc, char *argv[])
if (strcasecmp(argv[1], "multi") == 0 || strcmp(argv[1], "1") == 0) {
skiplist_type = SKIPLIST_TYPE_MULTI;
}
else if (strcasecmp(argv[1], "set") == 0 || strcmp(argv[1], "2") == 0) {
skiplist_type = SKIPLIST_TYPE_SET;
}
}
printf("skiplist type: %s\n",
skiplist_type == SKIPLIST_TYPE_FLAT ? "flat" : "multi");
numbers = (int *)malloc(sizeof(int) * COUNT);
srand(time(NULL));
@ -257,6 +327,7 @@ int main(int argc, char *argv[])
if (result != 0) {
return result;
}
printf("skiplist type: %s\n", skiplist_get_type_caption(&sl));
test_insert();
printf("\n");

View File

@ -0,0 +1,166 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <assert.h>
#include <inttypes.h>
#include <sys/time.h>
#include "fastcommon/skiplist_set.h"
#include "fastcommon/logger.h"
#include "fastcommon/shared_func.h"
#define COUNT 1000000
#define LEVEL_COUNT 16
#define MIN_ALLOC_ONCE 32
#define LAST_INDEX (COUNT - 1)
static int *numbers;
static SkiplistSet sl;
static SkiplistSetIterator iterator;
static int instance_count = 0;
static void free_test_func(void *ptr)
{
instance_count--;
}
static int compare_func(const void *p1, const void *p2)
{
return *((int *)p1) - *((int *)p2);
}
static int test_insert()
{
int i;
int result;
int64_t start_time;
int64_t end_time;
void *value;
instance_count = 0;
start_time = get_current_time_ms();
for (i=0; i<COUNT; i++) {
if ((result=skiplist_set_insert(&sl, numbers + i)) != 0) {
return result;
}
instance_count++;
}
assert(instance_count == COUNT);
end_time = get_current_time_ms();
printf("insert time used: %"PRId64" ms\n", end_time - start_time);
start_time = get_current_time_ms();
for (i=0; i<COUNT; i++) {
value = skiplist_set_find(&sl, numbers + i);
assert(value != NULL && *((int *)value) == numbers[i]);
}
end_time = get_current_time_ms();
printf("find time used: %"PRId64" ms\n", end_time - start_time);
start_time = get_current_time_ms();
i = 0;
skiplist_set_iterator(&sl, &iterator);
while ((value=skiplist_set_next(&iterator)) != NULL) {
i++;
if (i != *((int *)value)) {
fprintf(stderr, "i: %d != value: %d\n", i, *((int *)value));
break;
}
}
assert(i==COUNT);
end_time = get_current_time_ms();
printf("iterator time used: %"PRId64" ms\n", end_time - start_time);
return 0;
}
static void test_delete()
{
int i;
int64_t start_time;
int64_t end_time;
void *value;
start_time = get_current_time_ms();
for (i=0; i<COUNT; i++) {
assert(skiplist_set_delete(&sl, numbers + i) == 0);
}
assert(instance_count == 0);
end_time = get_current_time_ms();
printf("delete time used: %"PRId64" ms\n", end_time - start_time);
start_time = get_current_time_ms();
for (i=0; i<COUNT; i++) {
value = skiplist_set_find(&sl, numbers + i);
assert(value == NULL);
}
end_time = get_current_time_ms();
printf("find after delete time used: %"PRId64" ms\n", end_time - start_time);
i = 0;
skiplist_set_iterator(&sl, &iterator);
while ((value=skiplist_set_next(&iterator)) != NULL) {
i++;
}
assert(i==0);
}
int main(int argc, char *argv[])
{
int i;
int tmp;
int index1;
int index2;
int result;
log_init();
numbers = (int *)malloc(sizeof(int) * COUNT);
srand(time(NULL));
for (i=0; i<COUNT; i++) {
numbers[i] = i + 1;
}
for (i=0; i<COUNT; i++) {
index1 = LAST_INDEX * (int64_t)rand() / (int64_t)RAND_MAX;
index2 = LAST_INDEX * (int64_t)rand() / (int64_t)RAND_MAX;
if (index1 == index2) {
continue;
}
tmp = numbers[index1];
numbers[index1] = numbers[index2];
numbers[index2] = tmp;
}
fast_mblock_manager_init();
result = skiplist_set_init_ex(&sl, LEVEL_COUNT, compare_func,
free_test_func, MIN_ALLOC_ONCE);
if (result != 0) {
return result;
}
test_insert();
printf("\n");
fast_mblock_manager_stat_print(false);
test_delete();
printf("\n");
test_insert();
printf("\n");
/*
test_delete();
printf("\n");
*/
skiplist_set_destroy(&sl);
assert(instance_count == 0);
printf("pass OK\n");
return 0;
}