From fd894d810b2382f45990e6f0c34c95debf16b7d0 Mon Sep 17 00:00:00 2001 From: yuqing Date: Tue, 29 May 2018 18:10:50 +0800 Subject: [PATCH] add skiplist_set.[hc] and skiplist bug fixed --- HISTORY | 3 +- src/Makefile.in | 6 +- src/flat_skiplist.c | 49 +++--- src/flat_skiplist.h | 28 ++++ src/multi_skiplist.c | 41 ++++-- src/multi_skiplist.h | 37 ++++- src/multi_socket_client.c | 7 +- src/multi_socket_client.h | 10 +- src/skiplist.h | 171 +++++++++++++++------ src/skiplist_set.c | 270 ++++++++++++++++++++++++++++++++++ src/skiplist_set.h | 90 ++++++++++++ src/tests/Makefile | 5 +- src/tests/test_skiplist.c | 117 ++++++++++++--- src/tests/test_skiplist_set.c | 166 +++++++++++++++++++++ 14 files changed, 877 insertions(+), 123 deletions(-) create mode 100644 src/skiplist_set.c create mode 100644 src/skiplist_set.h create mode 100644 src/tests/test_skiplist_set.c diff --git a/HISTORY b/HISTORY index fb0c2a7..a49c79b 100644 --- a/HISTORY +++ b/HISTORY @@ -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: diff --git a/src/Makefile.in b/src/Makefile.in index 091ec29..a778d86 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -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) diff --git a/src/flat_skiplist.c b/src/flat_skiplist.c index 8fbb8d3..9227512 100644 --- a/src/flat_skiplist.c +++ b/src/flat_skiplist.c @@ -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; } - diff --git a/src/flat_skiplist.h b/src/flat_skiplist.h index 86e3a64..bdf6b96 100644 --- a/src/flat_skiplist.h +++ b/src/flat_skiplist.h @@ -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 diff --git a/src/multi_skiplist.c b/src/multi_skiplist.c index 0003427..8f0a4e2 100644 --- a/src/multi_skiplist.c +++ b/src/multi_skiplist.c @@ -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; } } - diff --git a/src/multi_skiplist.h b/src/multi_skiplist.h index 0414fcc..2849b76 100644 --- a/src/multi_skiplist.h +++ b/src/multi_skiplist.h @@ -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.data = NULL; + 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 diff --git a/src/multi_socket_client.c b/src/multi_socket_client.c index d234ba5..8add649 100644 --- a/src/multi_socket_client.c +++ b/src/multi_socket_client.c @@ -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; ientry_count; i++) { diff --git a/src/multi_socket_client.h b/src/multi_socket_client.h index 550dbf4..3762f35 100644 --- a/src/multi_socket_client.h +++ b/src/multi_socket_client.h @@ -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 { diff --git a/src/skiplist.h b/src/skiplist.h index 15e3bbd..91e8686 100644 --- a/src/skiplist.h +++ b/src/skiplist.h @@ -17,24 +17,28 @@ #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 { int type; union { - FlatSkiplist flat; + FlatSkiplist flat; MultiSkiplist multi; + SkiplistSet set; } u; } Skiplist; typedef struct skiplist_iterator { int type; union { - FlatSkiplistIterator flat; + 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) { - return flat_skiplist_init_ex(&sl->u.flat, level_count, - compare_func, free_func, min_alloc_elements_once); - } - else { - return multi_skiplist_init_ex(&sl->u.multi, level_count, - compare_func, free_func, min_alloc_elements_once); + switch (type) { + case SKIPLIST_TYPE_FLAT: + return flat_skiplist_init_ex(&sl->u.flat, level_count, + compare_func, free_func, min_alloc_elements_once); + 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) { - flat_skiplist_destroy(&sl->u.flat); - } - else { - multi_skiplist_destroy(&sl->u.multi); + switch (sl->type) { + case SKIPLIST_TYPE_FLAT: + flat_skiplist_destroy(&sl->u.flat); + 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) { - return flat_skiplist_insert(&sl->u.flat, data); - } - else { - return multi_skiplist_insert(&sl->u.multi, data); + switch (sl->type) { + case SKIPLIST_TYPE_FLAT: + return flat_skiplist_insert(&sl->u.flat, data); + 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) { - return flat_skiplist_delete(&sl->u.flat, data); - } - else { - return multi_skiplist_delete(&sl->u.multi, data); + switch (sl->type) { + case SKIPLIST_TYPE_FLAT: + return flat_skiplist_delete(&sl->u.flat, data); + 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) { - return flat_skiplist_delete_all(&sl->u.flat, data, delete_count); - } - else { - return multi_skiplist_delete_all(&sl->u.multi, data, delete_count); + int result; + switch (sl->type) { + case SKIPLIST_TYPE_FLAT: + return flat_skiplist_delete_all(&sl->u.flat, data, delete_count); + 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) { - return flat_skiplist_find(&sl->u.flat, data); - } - else { - return multi_skiplist_find(&sl->u.multi, data); + switch (sl->type) { + case SKIPLIST_TYPE_FLAT: + return flat_skiplist_find(&sl->u.flat, data); + 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) { - return flat_skiplist_find_all(&sl->u.flat, data, &iterator->u.flat); - } - else { - return multi_skiplist_find_all(&sl->u.multi, data, &iterator->u.multi); + switch (sl->type) { + case SKIPLIST_TYPE_FLAT: + return flat_skiplist_find_all(&sl->u.flat, data, &iterator->u.flat); + 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) { - return flat_skiplist_iterator(&sl->u.flat, &iterator->u.flat); - } - else { - return multi_skiplist_iterator(&sl->u.multi, &iterator->u.multi); + switch (sl->type) { + case SKIPLIST_TYPE_FLAT: + return flat_skiplist_iterator(&sl->u.flat, &iterator->u.flat); + 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) { - return flat_skiplist_next(&iterator->u.flat); + switch (iterator->type) { + case SKIPLIST_TYPE_FLAT: + return flat_skiplist_next(&iterator->u.flat); + 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; } - else { - return multi_skiplist_next(&iterator->u.multi); +} + +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"; } } diff --git a/src/skiplist_set.c b/src/skiplist_set.c new file mode 100644 index 0000000..12444f2 --- /dev/null +++ b/src/skiplist_set.c @@ -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 +#include +#include +#include +#include +#include +#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; itop->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; ilevel_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; itop_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; +} diff --git a/src/skiplist_set.h b/src/skiplist_set.h new file mode 100644 index 0000000..32e3005 --- /dev/null +++ b/src/skiplist_set.h @@ -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 +#include +#include +#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 + diff --git a/src/tests/Makefile b/src/tests/Makefile index e0324f3..e2e76a4 100644 --- a/src/tests/Makefile +++ b/src/tests/Makefile @@ -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: diff --git a/src/tests/test_skiplist.c b/src/tests/test_skiplist.c index 48942a7..41d1026 100644 --- a/src/tests/test_skiplist.c +++ b/src/tests/test_skiplist.c @@ -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,16 +160,42 @@ static int test_stable_sort() records[i].key = i + 1; } - for (i=0; i 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; ikey == target.key); } - i = 0; - while ((value=skiplist_next(&iterator)) != NULL) { - i++; - record = (Record *)value; - printf("%d => #%d\n", record->key, record->line); + else { + if (skiplist_find_all(&sl, &target, &iterator) == 0) { + printf("found key: %d\n", target.key); + } + i = 0; + while ((value=skiplist_next(&iterator)) != NULL) { + i++; + record = (Record *)value; + printf("%d => #%d\n", record->key, record->line); + } + printf("found record count: %d\n", i); } - 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 0) || - (result != 0 && 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"); diff --git a/src/tests/test_skiplist_set.c b/src/tests/test_skiplist_set.c new file mode 100644 index 0000000..6f31da9 --- /dev/null +++ b/src/tests/test_skiplist_set.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#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