Initial commit.
This commit is contained in:
commit
57e0a96f18
8 changed files with 588 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
*.o
|
||||
*.so
|
||||
sl
|
||||
|
28
LICENSE
Normal file
28
LICENSE
Normal file
|
@ -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.
|
||||
|
4
README.rst
Normal file
4
README.rst
Normal file
|
@ -0,0 +1,4 @@
|
|||
skiplist
|
||||
=========
|
||||
|
||||
Academic example of skip list implementation in C.
|
134
main.c
Normal file
134
main.c
Normal file
|
@ -0,0 +1,134 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#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;
|
||||
}
|
||||
|
292
skiplist.c
Normal file
292
skiplist.c
Normal file
|
@ -0,0 +1,292 @@
|
|||
#include <strings.h>
|
||||
#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;
|
||||
}
|
63
skiplist.h
Normal file
63
skiplist.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
#ifndef __SKIPLIST
|
||||
#define __SKIPLIST
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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
|
||||
|
45
x.c
Normal file
45
x.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
18
x.h
Normal file
18
x.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#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, ...);
|
Loading…
Reference in a new issue