skiplist/skiplist.c

328 lines
6.8 KiB
C

#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
/*
* Repeatedly toss a coin until we get tails.
* Denote with i the number of times coin came up heads
* and return it.
*/
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;
}
/*
* Remove 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;) {
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);
foo = sl->topleft;
debug("cleaned one level\n");
} else {
break;
}
}
}
/*
* Toss coin and get i.
* If i is greather than or equal to skip list max level then
* add one more level.
* Search for x and find the positions of the items with largest key
* less than x.
* Insert item into each level up to i.
* If key is found then just update its data.
*/
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;
}
/*
* Save nodes that will be updated with new node.
*/
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;
}
/*
* Start at the top left position.
* At the current position compare key with node's key.
* If key is found then return it.
* If key > node's key then scan forward.
* If key < node's key then drop down.
* If we drop down past the bottom list then return NULL.
*/
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;
}
/*
* Search for key and find positions with the key.
* Remove found positions.
* Remove all but one list containing only INFINITY keys.
*/
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;
}