commit 57e0a96f18be94a4af2e382420200d8ec67829f7 Author: Darko Date: Fri Mar 20 20:32:11 2015 +0100 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b755a9b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o +*.so +sl + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8b7718c --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2015, Darko Poljak +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of mergesort nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..87a4206 --- /dev/null +++ b/README.rst @@ -0,0 +1,4 @@ +skiplist +========= + +Academic example of skip list implementation in C. diff --git a/main.c b/main.c new file mode 100644 index 0000000..15cfde7 --- /dev/null +++ b/main.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include "skiplist.h" +#include "x.h" + +#define MAXBUF 80 +#define KEYCNT 1000 + +int dodebug = 1; + +int main(int argc, char **argv) +{ + slist sl; + int key; + sl_node *x; + int res; + int i; + int keys[KEYCNT]; + int failed = 0; + int cntnew; + int cntsame; +/* + char foo[MAXBUF]; + + sl_init(&sl); + sl_print(&sl); + + printf("addition\n"); + while (fgets(foo, MAXBUF - 1, stdin) != NULL) { + if (foo[0] == '\n') { + break; + } + key = atoi(foo); + sl_add(&sl, key, NULL); + sl_print(&sl); + } + + printf("search\n"); + while (fgets(foo, MAXBUF - 1, stdin) != NULL) { + if (foo[0] == '\n') { + break; + } + key = atoi(foo); + x = sl_find(&sl, key); + if (x) { + printf("found: %d\n", x->key); + } else { + printf("not found\n"); + } + } + + printf("remove\n"); + while (fgets(foo, MAXBUF - 1, stdin) != NULL) { + if (foo[0] == '\n') { + break; + } + key = atoi(foo); + res = sl_remove(&sl, key); + sl_print(&sl); + if (res) { + printf("removed\n"); + } else { + printf("not found\n"); + } + } + + sl_free(&sl); +*/ + + sl_init(&sl); + dodebug = 0; + failed = 0; + cntnew = cntsame = 0; + for (i = 0; i < KEYCNT; ++i) { + key = random() % KEYCNT; + keys[i] = key; + res = sl_add(&sl, key, NULL); + switch(res) { + case SL_ADD_NEW: + ++cntnew; + break; + case SL_ADD_SAME: + ++cntsame; + break; + default: + failed = 1; + break; + } + } + printf("added %d new keys, %d same keys\n", cntnew, cntsame); + dodebug = 1; + sl_print(&sl); + dodebug = 0; + cntnew = cntsame = 0; + for (i = 0; i < KEYCNT; ++i) { + key = keys[i]; + x = sl_find(&sl, key); + if (!x) { + failed = 1; + break; + } + } + if (failed) { + fatal("find failed, report bug\n"); + } + cntnew = cntsame = 0; + for (i = 0; i < KEYCNT; ++i) { + key = keys[i]; + res = sl_remove(&sl, key); + switch (res) { + case 0: + ++cntsame; + break; + case 1: + ++cntnew; + break; + default: + break; + } + if (sl_find(&sl, key)) { + failed = 1; + break; + } + } + if (failed) { + fatal("remove failed, report bug\n"); + } + printf("removed %d new keys, %d same keys\n", cntnew, cntsame); + sl_free(&sl); + + return 0; +} + diff --git a/skiplist.c b/skiplist.c new file mode 100644 index 0000000..8296e7d --- /dev/null +++ b/skiplist.c @@ -0,0 +1,292 @@ +#include +#include "skiplist.h" +#include "x.h" + +sl_node *_sl_create_node(int key, void *data) +{ + sl_node *x = xmalloc(sizeof(sl_node)); + + x->key = key; + x->data = data; + x->next = x->prev = x->below = x->above = NULL; + + return x; +} + +void sl_init(slist *sl) +{ + sl_node *x = _sl_create_node(KEY_INF_MIN, NULL); + sl_node *y = _sl_create_node(KEY_INF_MAX, NULL); + + x->next = y; + y->prev = x; + + sl->topleft = x; + sl->bottomleft = x; + sl->topright = y; + sl->bottomright = y; + + sl->maxlevel = 0; +} + +void _sl_add_level(slist *sl) +{ + sl_node *x = _sl_create_node(KEY_INF_MIN, NULL); + sl_node *y = _sl_create_node(KEY_INF_MAX, NULL); + + x->next = y; + y->prev = x; + + x->below = sl->topleft; + y->below = sl->topright; + sl->topleft->above = x; + sl->topright->above = y; + sl->topleft = x; + sl->topright = y; + + ++sl->maxlevel; +} + +void sl_free(slist *sl) +{ + sl_node *foo; + sl_node *x; + sl_node *y; + + for (foo = sl->bottomleft; foo != NULL;) { + x = foo; + foo = foo->next; + while (x != NULL) { + y = x; + x = x->above; + free(y->data); + free(y); + } + } +} + +void _print_key(int key) +{ + if (key == KEY_INF_MIN) { + debug("-INF -> "); + } else if (key == KEY_INF_MAX) { + debug("+INF -> "); + } else { + debug("%d -> ", key); + } +} + +void _sl_println() +{ + int i; + + for (i = 0; i < 79; ++i) { + debug("-"); + } + debug("\n"); +} + +void sl_print(const slist *sl) +{ + sl_node *foo; + sl_node *x; + int level = sl->maxlevel; + + _sl_println(); + for (foo = sl->topleft; foo != NULL; foo = foo->below, --level) { + debug("level %d: ", level); + for (x = foo; x != NULL; x = x->next) { + _print_key(x->key); + } + debug("\n"); + } + _sl_println(); + debug("from below to above:\n"); + for (foo = sl->bottomleft; foo != NULL; foo = foo->next) { + for (x = foo; x != NULL; x = x->above) { + _print_key(x->key); + } + debug("\n"); + } + _sl_println(); +} + +#define HEAD 1 +#define TAIL 0 + +int _toss_coin(int maxlevel) { + int i; + int x; + + i = 0; + while(1) { + x = random() & 0x1; + if (x == TAIL) { + break; + } + ++i; + /* inc level only by one at one iter */ + if (i == maxlevel + 1) { + break; + } + }; + + return i; +} + +/* + * Clean top levels with only INFINITY nodes until only one such + * level exists. + */ +void _sl_clean_top(slist *sl) +{ + sl_node *foo; + + for (foo = sl->topleft; foo->below != NULL; foo = foo->below) { + if (foo->next->key == KEY_INF_MAX + && foo->below->next->key == KEY_INF_MAX) { + sl->topleft = foo->below; + sl->topright = foo->below->next; + sl->topleft->above = NULL; + sl->topright->above = NULL; + --sl->maxlevel; + free(foo->data); + free(foo->next); + free(foo); + debug("cleaned one level\n"); + } + } +} + +int sl_add(slist *sl, int key, void *data) +{ + sl_node *foo; + sl_node *x; + int i; + int k; + int level; + sl_node **update; + int res; + + if (key == KEY_INF_MIN || key == KEY_INF_MAX) { + return SL_ADD_INV; + } + i = _toss_coin(sl->maxlevel); + debug("toss coin result: %d\n", i); + if (i >= sl->maxlevel) { + /* add one more level */ + _sl_add_level(sl); + debug("added level\n"); + } + update = (sl_node **) xmalloc(sizeof(sl_node *) * (sl->maxlevel + 1)); + bzero(update, sizeof(sl_node *) * (sl->maxlevel + 1)); + level = sl->maxlevel; + + foo = sl->topleft; + while (1) { + while (key >= foo->next->key) { + foo = foo->next; + } + if (foo->below == NULL) { + break; + } + update[level--] = foo; + foo = foo->below; + } + update[level] = foo; + + debug("search finished at key: %d\n", foo->key); + debug("update list keys:\n"); + for (k = 0; k <= sl->maxlevel; ++k) { + if (update[k] != 0) { + debug("%d\n", update[k]->key); + } + } + + debug("adding\n"); + if (foo->key == key) { + res = SL_ADD_SAME; + debug("key exists\n"); + do { + foo->data = data; + foo = foo->above; + } while (foo != NULL); + } else { + res = SL_ADD_NEW; + debug("new key\n"); + while (level <= i) { + x = _sl_create_node(key, data); + x->next = update[level]->next; + update[level]->next->prev = x; + x->prev = update[level]; + update[level]->next = x; + if (level > 0) { + x->below = update[level - 1]->next; + update[level - 1]->next->above = x; + } + ++level; + } + } + _sl_clean_top(sl); + free(update); + + return res; +} + +sl_node *sl_find(slist *sl, int key) +{ + sl_node *foo; + + foo = sl->topleft; + while (1) { + while (key >= foo->next->key) { + foo = foo->next; + } + if (foo->below == NULL) { + break; + } + foo = foo->below; + } + + if (foo->key != key) { + foo = NULL; + } + return foo; +} + +int sl_remove(slist *sl, int key) +{ + sl_node *foo; + sl_node *x; + int result; + + foo = sl->topleft; + while (1) { + while (key >= foo->next->key) { + foo = foo->next; + } + if (foo->below == NULL) { + break; + } + foo = foo->below; + } + + if (foo->key == key) { + result = 1; + + while (foo != NULL) { + foo->prev->next = foo->next; + foo->next->prev = foo->prev; + free(foo->data); + x = foo; + foo = foo->above; + free(x); + } + + _sl_clean_top(sl); + } else { + result = 0; + } + + return result; +} diff --git a/skiplist.h b/skiplist.h new file mode 100644 index 0000000..301c8ff --- /dev/null +++ b/skiplist.h @@ -0,0 +1,63 @@ +#ifndef __SKIPLIST +#define __SKIPLIST + +#include +#include +#include + +#define KEY_INF_MIN INT_MIN /* -INFINITY */ +#define KEY_INF_MAX INT_MAX /* +INFINITY */ + +#define SL_ADD_NEW 1 /* added new key */ +#define SL_ADD_SAME 0 /* added existing key */ +#define SL_ADD_INV -1 /* added invalid key (+-INFINITY) */ + +/* skip list node */ +typedef struct skiplist_node { + int key; + void *data; + struct skiplist_node *next; + struct skiplist_node *prev; + struct skiplist_node *below; + struct skiplist_node *above; +} sl_node; + +/* skiplist object */ +typedef struct skiplist { + sl_node *topleft; + sl_node *bottomleft; + sl_node *topright; + sl_node *bottomright; + int maxlevel; +} slist; + +/* + * Initialize empty skip list. + */ +void sl_init(slist *sl); +/* + * Free skip list memory. After free list is not initialized + * for subsequent use. + */ +void sl_free(slist *sl); +/* + * Print skip list level by level and in down to up form. + */ +void sl_print(const slist *sl); +/* + * Add new key with data into skip list. + */ +int sl_add(slist *sl, int key, void *data); +/* + * Find node in skip list with given key. + * If key is not found returns NULL. + */ +sl_node *sl_find(slist *sl, int key); +/* + * Remove node from skip list with given key. + * If key is found and removed return 1, otherwise 0. + */ +int sl_remove(slist *sl, int key); + +#endif + diff --git a/x.c b/x.c new file mode 100644 index 0000000..c45282b --- /dev/null +++ b/x.c @@ -0,0 +1,45 @@ +#include "x.h" + +extern int dodebug; + +static void _log_stderr(const char *fmt, va_list ap) +{ + char buf[MAXLINE]; + + vsnprintf(buf, MAXLINE-1, fmt, ap); + fflush(stdout); + fputs(buf, stderr); + fflush(stderr); +} + +void fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + _log_stderr(fmt, ap); + va_end(ap); + exit(1); +} + +void debug(const char *fmt, ...) +{ + va_list ap; + + if (dodebug) { + va_start(ap, fmt); + _log_stderr(fmt, ap); + va_end(ap); + } +} + +void *xmalloc(size_t size) +{ + void *foo = malloc(size); + + if (foo == NULL) { + fatal("Not enough memory\n"); + } + return foo; +} + diff --git a/x.h b/x.h new file mode 100644 index 0000000..e7c3024 --- /dev/null +++ b/x.h @@ -0,0 +1,18 @@ +#include +#include +#include + +#define MAXLINE 1024 + +/* + * Print message and exit. + */ +void fatal(const char *fmt, ...); +/* + * malloc, if failed then fatal exit. + */ +void *xmalloc(size_t size); +/* + * Print debug message if debug is enabled. + */ +void debug(const char *fmt, ...);