------------------------------ skiplist.h ------------------------------

#ifndef _OLIO_SKIPLIST_H_
#define _OLIO_SKIPLIST_H_

#include <inttypes.h>

/* a skiplist sorted array with logarithmic behavior for searches, inserts, and deletes.
 * similar to a b-tree, but a simpler algorithm.

 * the current implementation uses a simple, static height increment on key addition, 
 * and this assumes that the entry keys are added with either:
 *  1) a generally sequential pattern, or
 *  2) a random pattern
 * 
 * TODO: top-down deterministic height management. a bit more complicated, and hasn't
 * been necessary yet.
 * Munro, J et al. "Deterministic Skip Lists." Proc. ACM-SIAM Symposium on Discrete Algo. 1992
 */

/* maximum height the skiplist can grow to */
#define OLIO_SKIPLIST_MAX_HEIGHT 16
/* height is built up geometrically with this base */
#define OLIO_SKIPLIST_HIGHER_EVERY 4

typedef struct _olio_skiplist_entry {
	struct _olio_skiplist_entry * next;
	void * data;
	uint32_t key;
} olio_skiplist_entry;

typedef struct _olio_skiplist {
	struct _olio_skiplist_entry heads[OLIO_SKIPLIST_MAX_HEIGHT];
	uint32_t count;
	uint8_t height;
} olio_skiplist;

#ifdef __cplusplus
extern "C" {
#endif

/* basic init/free functions for the skiplist */
void olio_skiplist_init(olio_skiplist *);
void olio_skiplist_free(olio_skiplist *);

/* find the entry for a particular key */
void * olio_skiplist_find(olio_skiplist *, uint32_t key);

/* prepare for a sequential search, with keys >= key */
void olio_skiplist_range_begin(olio_skiplist *, uint32_t key, void ** opaque);
/* iterate on the sequential search, with keys <= mkey */
void * olio_skiplist_range_next(olio_skiplist *, uint32_t mkey,	
				uint32_t * ckey, void ** opaque);

/* add an entry to the skiplist */
int olio_skiplist_add(olio_skiplist *, uint32_t key, void * data);

#ifdef OLIO_DEBUG
/* show the entries (and keys) at each height level */
void olio_skiplist_display(olio_skiplist * sk);
#endif

#ifdef __cplusplus
} /* extern C */
#endif

#endif /* _OLIO_SKIPLIST_H_ */

------------------------------ skiplist.c ------------------------------

#include <stdlib.h>

#include "skiplist.h"

void olio_skiplist_init(olio_skiplist * sk)
{
	int i;

	/* initialize the lowest height head */
	sk->heads[0].next = NULL;
	sk->heads[0].key = 0xffffffff;
	sk->heads[0].data = NULL;

	/* initialize higher level heads */
	for (i = 1; i < OLIO_SKIPLIST_MAX_HEIGHT; i++) {
		sk->heads[i].next = NULL;
		sk->heads[i].key = 0xffffffff;
		/* the head's data is the head below it */
		sk->heads[i].data = &sk->heads[i-1];
	}

	/* count of items */
	sk->count = 0;
	/* how much height is the skip list using */
	sk->height = 1;
}

void olio_skiplist_free(olio_skiplist * sk)
{
	olio_skiplist_entry * a;
	olio_skiplist_entry * b;
	int i;

	for (i = 0; i < OLIO_SKIPLIST_MAX_HEIGHT; i++) {
		/* for each head, free everything on that level */
		a = sk->heads[i].next;
		while (a != NULL) {
			b = a->next;
			free(a);
			a = b;
		}
		sk->heads[i].next = NULL;
	}

	/* reset to initialized state */
	sk->count = 0;
	sk->height = 1;
}

void * olio_skiplist_find(olio_skiplist * sk, uint32_t key)
{
	/* use the current height to skip past unused layers */
	void * el = &sk->heads[sk->height-1];
	olio_skiplist_entry * h = NULL;
	uint8_t cd = sk->height;

	/* while we are above the data (bottom-most) layer */
	while (cd > 0) {
		/* previous el is the entry for this level */
		h = (olio_skiplist_entry *) el;
		/* skip along this level until we would exceed our key */
		while (h->next != NULL && h->next->key <= key)
			h = h->next;
		/* store el with this entry's data */
		el = h->data;
		/* next level down... */
		cd--;
	}

	/* make sure we have an entry with a matching key */
	if (h->key != key)
		return NULL;

	return el;
}

/* set up for a range search, >= key */
void olio_skiplist_range_begin(olio_skiplist * sk, uint32_t key, 
			       void ** opaque)
{
	/* use the current height to skip past unused layers */
	void * el = &sk->heads[sk->height-1];
	olio_skiplist_entry * h = NULL;
	uint8_t cd = sk->height;

	/* while we are above the data (bottom-most) layer */
	while (cd > 0) {
		/* previous el is the entry for this level */
		h = (olio_skiplist_entry *) el;
		/* skip along this level until we would exceed our key */
		while (h->next != NULL && h->next->key <= key)
			h = h->next;
		/* store el with this entry's data */
		el = h->data;
		/* next level down... */
		cd--;
	}

	/* if we have don't match the key, we want the next entry */
	if (h->key != key)
		*opaque = h->next;
	else
		*opaque = h;
}

/* iterator for range search, <= mkey */
void * olio_skiplist_range_next(olio_skiplist * sk, uint32_t mkey, 
				uint32_t * ckey, void ** opaque)
{
	olio_skiplist_entry * h = (olio_skiplist_entry *) (*opaque);
	if (h == NULL) return NULL;
	if (h->key > mkey) return NULL;
	*ckey = h->key;
	*opaque = h->next;
	return h->data;
}

int olio_skiplist_add(olio_skiplist * sk, uint32_t key, void * data)
{
	/* we need to store the previous entry for each level */
	olio_skiplist_entry * path[OLIO_SKIPLIST_MAX_HEIGHT];

	/* like the find above, except we have to start from the
	 * top to record the full path. we can't skip to the first
	 * used layer */
	void * el = &sk->heads[OLIO_SKIPLIST_MAX_HEIGHT-1];
	olio_skiplist_entry * h;
	uint8_t cd = OLIO_SKIPLIST_MAX_HEIGHT;

	uint32_t mm;

	/* while we are above the data layer */
	while (cd > 0) {
		/* previous el is the entry point for this level */
		h = (olio_skiplist_entry *) el;
		/* skip along this level until we would exceed our key */
		while (h->next != NULL && h->next->key <= key)
			h = h->next;
		/* store this node in our path list */
		path[cd-1] = h;
		/* store el with this entry's data */
		el = h->data;
		/* next level down... */
		cd--;
	}

	/* increment the list's entry count */
	mm = ++sk->count;

	/* now we insert an entry for each applicable layer,
	 * starting from the bottom */
	cd = 0;
	/* the first level's data attribute will be set to the 
	 * caller provided data pointer */
	el = data;

	/* limit of MAX_HEIGHT */
	while (cd < OLIO_SKIPLIST_MAX_HEIGHT) {
		/* allocated a new entry for this level */
		h = (olio_skiplist_entry *) 
			malloc(sizeof(olio_skiplist_entry));

		if (h == NULL) return -1;
		
		/* set the key and data fields */
		h->key = key;
		h->data = el;

		/* insert into the list at the current (cd) level */
		h->next = path[cd]->next;
		path[cd]->next = h;

		/* if count is not a modulus of HIGHER_EVERY,
		 * break out of the loop */
		if (mm % OLIO_SKIPLIST_HIGHER_EVERY) break;

		/* it is a modulus, so we'll loop again at
		 * the next level up. */

		/* our temporary count must be shifted down */
		mm /= OLIO_SKIPLIST_HIGHER_EVERY;
		/* the next entry's data will point to this 
		 * level's entry */
		el = h;

		cd++;
	}

	/* reset our max used height, if needed */
	if (cd + 1 > sk->height) sk->height = cd + 1;

	return 0;
}

#ifdef OLIO_DEBUG
#include <stdio.h>

void olio_skiplist_display(olio_skiplist * sk)
{
	olio_skiplist_entry * se;
	uint8_t h;

	fprintf(stderr, "skiplist:\n");
	fprintf(stderr, "count: %lu\n", sk->count);
	fprintf(stderr, "height: %u\n", sk->height);

	h = sk->height;
	while (h > 0) {
		fprintf(stderr, " @ height: %u\n", h);
		se = &sk->heads[h-1];
		while (se != NULL) {
			fprintf(stderr, "   key: 0x%x\n", se->key);
			se = se->next;
		}
		h--;
	}

}

#endif /* OLIO_DEBUG */