/* $Id: container.c,v 1.16 2000/05/27 13:38:14 jens Exp $ */
/*
 * Copyright (c) 1999, 2000
 *      Jens A. Nilsson, jnilsson@ludd.luth.se. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */
#ifndef lint
static char const cvsid[] = "$Id: container.c,v 1.16 2000/05/27 13:38:14 jens Exp $";
#endif

#include <sys/errno.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include "container.h"

#ifdef NO_E_ERR
#include <err.h>
#else
#include "e_err.h"
#endif


int container_err_exit = 0;

void insque(void *entry, void *pred);	
void remque(void *entry);


struct cl_que *
cl_init(void)
{
	struct cl_que *p;

	if ((p = malloc(sizeof(*p))) == NULL) {
		if (container_err_exit)
			err(1, "cl_init: malloc");
		warn("cl_init: malloc");
		return NULL;
	}
	p->q_forw = p;
	p->q_back = p;
	p->q_data = NULL;
	return p;
}

void
cl_free(struct cl_que *headp, cl_free_f free_fun)
{
	struct	cl_que *p;

	if (headp == NULL)
		return;
	while (headp != headp->q_forw) {
		p = headp->q_forw;
		if (free_fun != NULL)
			free_fun(p->q_data);
		remque(p);
		free(p);
	}
	free(headp);
}

int
cl_walk(struct cl_que *headp, struct cl_que **pos, TYPE **data)
{
	int		res;

	if (pos == NULL)
		return cl_peek(headp, data);
	if (*pos == NULL) {
		if ((res = cl_peek_pos(headp, pos)) < 0)
			return res;
		if (data != NULL)
			*data = CL_DATA(*pos);
		return 0;
	}
	if (*pos == headp || CL_NEXT(*pos) == headp)
		return -1;
	*pos = CL_NEXT(*pos);
	if (data != NULL)
		*data = CL_DATA(*pos);
	return 0;
}

int
cl_walk_back(struct cl_que *headp, struct cl_que **pos, TYPE **data)
{
	int		res;

	if (pos == NULL)
		return cl_tail_peek(headp, data);
	if (*pos == NULL) {
		if ((res = cl_tail_peek_pos(headp, pos)) < 0)
			return res;
		if (data != NULL)
			*data = CL_DATA(*pos);
		return 0;
	}
	if (*pos == headp || CL_PREV(*pos) == headp)
		return -1;
	*pos = CL_PREV(*pos);
	if (data != NULL)
		*data = CL_DATA(*pos);
	return 0;
}

unsigned
cl_count(struct cl_que *cl)
{
	unsigned	num;
	struct		cl_que *p;

	num = 0;
	for (p = CL_NEXT(cl); p != cl; p = CL_NEXT(p))
		num++;
	return num;
}

int
cl_push(struct cl_que *headp, TYPE *data)
{
	struct cl_que *p;

	p = malloc(sizeof(*p));
	if (p == NULL) {
		if (container_err_exit)
			err(1, "cl_push: malloc");
		warn("cl_push: malloc");
		return -1;
	}
	p->q_data = data;
	insque(p, headp);
	return 0;
}

int
cl_tail_push(struct cl_que *headp, TYPE *data)
{
	struct cl_que *p;

	p = malloc(sizeof(*p));
	if (p == NULL) {
		if (container_err_exit)
			err(1, "cl_que: malloc");
		warn("cl_que: malloc");
		return -1;
	}
	p->q_data = data;
	insque(p, headp->q_back);
	return 0;
}


int
cl_pop(struct cl_que * headp, TYPE **data)
{
	struct cl_que *p;

	if (headp == NULL || headp->q_forw == headp) {
		errno = ENOENT;
		return -1;
	}
	p = headp->q_forw;
	remque(p);
	*data = p->q_data;
	free(p);
	return 0;
}

int
cl_tail_pop(struct cl_que *headp, TYPE **data)
{
	struct cl_que *p;

	if (headp == NULL || headp->q_back == headp) {
		errno = ENOENT;
		return -1;
	}
	p = headp->q_back;
	remque(p);
	*data = p->q_data;
	free(p);
	return 0;
}

int
cl_peek_pos(struct cl_que * headp, struct cl_que **pos)
{
	if (headp == NULL || headp->q_forw == headp) {
		errno = ENOENT;
		return -1;
	}
	if (pos != NULL)
		*pos = headp->q_forw;
	return 0;
}

int
cl_peek(struct cl_que *headp, TYPE **data)
{
	struct	cl_que *pos;
	int		res;

	if ((res = cl_peek_pos(headp, &pos)) < 0)
		return res;
	if (data != NULL)
		*data = pos->q_data;
	return 0;
}

int
cl_tail_peek_pos(struct cl_que * headp, struct cl_que **pos)
{
	if (headp == NULL || headp->q_back == headp) {
		errno = ENOENT;
		return -1;
	}
	if (pos != NULL)
		*pos = headp->q_back;
	return 0;
}

int
cl_tail_peek(struct cl_que *headp, TYPE **data)
{
	struct	cl_que *pos;
	int		res;

	if ((res = cl_tail_peek_pos(headp, &pos)) < 0)
		return res;
	if (data != NULL)
		*data = pos->q_data;
	return 0;
}

static void
cl_ins_sort_nomalloc(struct cl_que *headp, struct cl_que *p,
	cl_comp_f comp_fun)
{
	struct	cl_que *q;

	for (q = CL_NEXT(headp); q != headp; q = CL_NEXT(q))
		if (comp_fun(CL_DATA(q), CL_DATA(p)) > 0)
			break;

	insque(p, q->q_back);
}


int 
cl_ins_sort(struct cl_que *headp, TYPE *data, cl_comp_f comp_fun)
{
	struct cl_que *p;

	p = malloc(sizeof(*p));
	if (p == NULL) {
		if (container_err_exit)
			err(1, "cl_ins_sort: malloc");
		warn("cl_ins_sort: malloc");
		return -1;
	}
	p->q_data = data;

	if (CL_EMPTY(headp))
		insque(p, headp);
	else
		cl_ins_sort_nomalloc(headp, p, comp_fun);

	return 0;
}

void
cl_ins_after_pos(struct cl_que *pos, struct cl_que **new_pos, TYPE *data)
{
	if (cl_push(pos, data) < 0)
		return;
	if (new_pos != NULL)
		*new_pos = CL_NEXT(pos);
}

int 
cl_find_pos(struct cl_que *headp, struct cl_que **pos,
	TYPE **data, cl_find_f is_data, TYPE *comp_obj)
{
	struct cl_que *q;

	for (q = CL_NEXT(headp); q != headp; q = CL_NEXT(q)) {
		if (is_data(CL_DATA(q), comp_obj)) {
			if (data != NULL)
				*data = CL_DATA(q);
			if (pos != NULL)
				*pos = q;
			return 0;
		}
	}
	errno = ENOENT;
	return -1;
}

int 
cl_find(struct cl_que *headp, TYPE **data, cl_find_f is_data, TYPE *comp_obj)
{
	return cl_find_pos(headp, NULL, data, is_data, comp_obj);
}

void
cl_map(struct cl_que *headp, cl_map_f map_fun, TYPE *arg_obj)
{
	struct cl_que *q;

	for (q = CL_NEXT(headp); q != headp; q = CL_NEXT(q))
		map_fun(CL_DATA(q), arg_obj);
}

void
cl_sort(struct cl_que *headp, cl_comp_f comp_fun)
{
	struct	cl_que *q, *p;

	if (CL_EMPTY(headp))
		return;

	q = CL_NEXT(headp);
	remque(headp);
	CL_NEXT(headp) = headp;
	CL_PREV(headp) = headp;
	while (CL_NOT_EMPTY(q)) {
		p = CL_NEXT(q);
		remque(q);
		cl_ins_sort_nomalloc(headp, q, comp_fun);
		q = p;
	}
	cl_ins_sort_nomalloc(headp, q, comp_fun);
}

int
cl_rem_pos(struct cl_que *q)
{
	if (q == CL_NEXT(q)) {
		errno = ENOENT;
		return -1;
	}
	remque(q);
	free(q);
	return 0;
}

int
cl_rem(struct cl_que *headp, TYPE **data, cl_find_f is_data, TYPE *comp_obj)
{
	struct cl_que *p;

	if (cl_find_pos(headp, &p, data, is_data, comp_obj) < 0)
		return -1;
	return cl_rem_pos(p);
}

#define FIFO_CHUNK 20

#define FIFO_DATA_SIZE(s)	((s) * sizeof(void *))

#define FIFO_COUNT(fifo)												\
		(((fifo)->fifo_start <= (fifo)->fifo_end) ?						\
			((fifo)->fifo_end - (fifo)->fifo_start) : 					\
			((fifo)->fifo_size - ((fifo)->fifo_start + (fifo)->fifo_end)))

static int
fifo_incr(struct fifo_list *f)
{
	void		**p;
	unsigned	size;

	size = f->fifo_size * 2;
	if ((p = realloc(f->fifo_data, FIFO_DATA_SIZE(size))) == NULL)
		return -1;
	f->fifo_data = p;
	if (f->fifo_start > f->fifo_end) {
		(void)memcpy(p + f->fifo_size, p, FIFO_DATA_SIZE(f->fifo_end));
		f->fifo_end += f->fifo_size;
	}
	f->fifo_size = size;
	return 0;
}

static void
fifo_decr(struct fifo_list *f)
{
	void		**p;
	unsigned	start, end, size;

	if (f->fifo_size == FIFO_CHUNK)
		return;
	start = f->fifo_start; end = f->fifo_end; size = f->fifo_size / 2;
	if ((p = malloc(FIFO_DATA_SIZE(size))) == NULL)
		return;
	if (f->fifo_start < f->fifo_end) {
		/* copy from start to end */
		(void)memcpy(p, f->fifo_data + start, FIFO_DATA_SIZE(end - start));

	} else if (f->fifo_start > f->fifo_end) {
		/* copy from start to end of buffer */
		(void)memcpy(p, f->fifo_data + start, FIFO_DATA_SIZE(size - start));

		/* copy from beginning of buffer to end */
		(void)memcpy(p + size - start, f->fifo_data, FIFO_DATA_SIZE(end));
	}
	start = 0; end = FIFO_COUNT(f);
	free(f->fifo_data);
	f->fifo_data = p; f->fifo_start = start;
	f->fifo_end = end; f->fifo_size = size;
}

struct fifo_list * 
fifo_init(void)
{
	struct	fifo_list *f;

	if ((f = calloc((size_t) 1, sizeof(*f))) == NULL) {
		if (container_err_exit)
			err(1, "fifo_init: malloc");
		warn("fifo_init: malloc");
		return NULL;
	}
	if ((f->fifo_data = malloc(FIFO_DATA_SIZE(FIFO_CHUNK))) == NULL) {
		if (container_err_exit)
			err(1, "fifo_init: malloc");
		warn("fifo_init: malloc");
		free(f);
		return NULL;
	}
	f->fifo_size = FIFO_CHUNK;
	return f;
}

struct fifo_list * 
fifo_clone(struct fifo_list *f)
{
	struct	fifo_list *fc;

	if ((fc = malloc(sizeof(*fc))) == NULL) {
		if (container_err_exit)
			err(1, "fifo_clone: malloc");
		warn("fifo_clone: malloc");
		return NULL;
	}
	(void)memcpy(fc, f, sizeof(*fc));
	if ((fc->fifo_data = malloc(FIFO_DATA_SIZE(f->fifo_size))) == NULL) {
		if (container_err_exit)
			err(1, "fifo_clone: malloc");
		warn("fifo_clone: malloc");
		free(fc);
		return NULL;
	}
	(void)memcpy(fc->fifo_data, f->fifo_data, FIFO_DATA_SIZE(f->fifo_size));
	return fc;
}

void
fifo_free(struct fifo_list *f, fifo_free_f free_fun)
{
	unsigned	i;

	if (f->fifo_start != f->fifo_end) {
		if (f->fifo_start < f->fifo_end) {
			for (i = f->fifo_start; i < f->fifo_end;)
				if (free_fun != NULL)
					free_fun(f->fifo_data[i++]);
		} else {
			for (i = f->fifo_start; i < f->fifo_size;)
				if (free_fun != NULL)
					free_fun(f->fifo_data[i++]);
			for (i = 0; i < f->fifo_end;)
				if (free_fun != NULL)
					free_fun(f->fifo_data[i++]);
		}
	}
	free(f->fifo_data);
	free(f);
}

int
fifo_push(struct fifo_list *f, TYPE *data)
{
	if (FIFO_COUNT(f) == (f->fifo_size - 1))
		if (fifo_incr(f) < 0)
			return -1;

	f->fifo_data[f->fifo_end] = data;
	f->fifo_end = (f->fifo_end + 1) % f->fifo_size;
	return 0;
}

int
fifo_pop(struct fifo_list *f, TYPE **data)
{
	if (f == NULL || f->fifo_start == f->fifo_end) {
		errno = ENOENT;
		return -1;
	}
	*data = f->fifo_data[f->fifo_start];
	f->fifo_start = (f->fifo_start + 1) % f->fifo_size;
	if (FIFO_COUNT(f) < ((f->fifo_size - 1) / 2))
		fifo_decr(f);
	return 0;
}

int
fifo_peek(struct fifo_list *f, TYPE **data)
{
	if (f == NULL || f->fifo_start == f->fifo_end) {
		errno = ENOENT;
		return -1;
	}
	if (data != NULL)
		*data = f->fifo_data[f->fifo_start];
	return 0;
}

unsigned
fifo_count(struct fifo_list *f)
{
	return FIFO_COUNT(f);
}

int
fifo_walk(struct fifo_list *f, ssize_t *pos, TYPE **data)
{
	if (*pos > f->fifo_size) {
		errno = ENOENT;
		return -1;
	}
	if (data != NULL)
		*data = f->fifo_data[(f->fifo_start + *pos) % f->fifo_size];
	(*pos)++;
	return 0;
}

int
fifo_walk_back(struct fifo_list *f, ssize_t *pos, TYPE **data)
{
	if (*pos < 0) {
		errno = ENOENT;
		return -1;
	}
	if (data != NULL)
		*data = f->fifo_data[(f->fifo_start + *pos) % f->fifo_size];
	(*pos)--;
	return 0;
}

/* XXX the following four functions are not tested, may not work */
int
fifo_find_peek(struct fifo_list *f, TYPE **data, unsigned *num,
	fifo_find_f find_fun, TYPE *comp_obj)
{
	unsigned	i, j, count;

	count = FIFO_COUNT(f);
	for (i = j = 0; j < count; j++, i = (i + 1) % f->fifo_size) {
		if (find_fun(f->fifo_data[i], comp_obj)) {
			if (data != NULL)
				*data = f->fifo_data[i];
			if (num != NULL)
				*num = j;
			return 0;
		}
	}
	errno = ENOENT;
	return -1;
}

int
fifo_find_rem(struct fifo_list *f, TYPE **data, unsigned *num,
	fifo_find_f find_fun, TYPE *comp_obj)
{
	unsigned	n;

	if (fifo_find_peek(f, data, &n, find_fun, comp_obj) < 0)
		return -1;
	if (fifo_num_rem(f, n, data) < 0)
		return -1;
	if (num != NULL)
		*num = n;
	return 0;
}

int
fifo_num_peek(struct fifo_list *f, unsigned num, TYPE **data)
{
	unsigned	count;

	count = FIFO_COUNT(f);
	if (num > count) {
		errno = ENOENT;
		return -1;
	}
	if (data != NULL)
		*data = f->fifo_data[(num + f->fifo_start) % f->fifo_size];
	return 0;
}

int
fifo_num_rem(struct fifo_list *f, unsigned num, TYPE **data)
{
	unsigned	count, pos, start, end;
	TYPE		**ldata;

	if (num == 0)
		return fifo_pop(f, data);

	count = FIFO_COUNT(f);
	if (num > count) {
		errno = ENOENT;
		return -1;
	}

	pos = (num + f->fifo_start) % f->fifo_size;
	start = f->fifo_start;
	end = f->fifo_end;
	ldata = f->fifo_data;
	
	if (start < pos) {
		(void)memmove(ldata + start + 1, ldata + start,
			FIFO_DATA_SIZE(pos - start));
		f->fifo_start++;
	} else {
		(void)memmove(ldata + pos, ldata + pos + 1, FIFO_DATA_SIZE(end - pos));
		f->fifo_end--;
	}
	if (FIFO_COUNT(f) < (f->fifo_size / 2))
		fifo_decr(f);
	return 0;
}



#define STACK_CHUNK 20

#define STACK_DATA_SIZE(s)	((s) * sizeof(void *))

static int
stack_incr(struct stack_list *s)
{
	void		**p;
	unsigned	size;

	size = s->stack_size * 2;
	if ((p = realloc(s->stack_data, STACK_DATA_SIZE(size))) == NULL)
		return -1;
	s->stack_data = p;
	s->stack_size = size;
	return 0;
}

static void
stack_decr(struct stack_list *s)
{
	void		**p;
	unsigned	size;

	if (s->stack_size == STACK_CHUNK)
		return;
	size = s->stack_size / 2;
	if ((p = realloc(s->stack_data, STACK_DATA_SIZE(size))) == NULL)
		return;
	s->stack_data = p;
}

struct stack_list *
stack_init(void)
{
	struct	stack_list *s;

	if ((s = calloc((size_t) 1, sizeof(*s))) == NULL) {
		if (container_err_exit)
			err(1, "stack_init: malloc");
		warn("stack_init: malloc");
		return NULL;
	}
	if ((s->stack_data = malloc(STACK_DATA_SIZE(STACK_CHUNK))) == NULL) {
		if (container_err_exit)
			err(1, "stack_init: malloc");
		warn("stack_init: malloc");
		free(s);
		return NULL;
	}
	s->stack_size = STACK_CHUNK;
	return s;
}

void
stack_free(struct stack_list *s, stack_free_f free_fun)
{
	unsigned	i;

	for (i = s->stack_end; i > 0; )
		if (free_fun != NULL)
			free_fun(s->stack_data[i++]);
	free(s->stack_data);
	free(s);
}

int
stack_push(struct stack_list *s, TYPE *data)
{
	if (s->stack_size == s->stack_end)
		if (stack_incr(s) < 0)
			return -1;
	s->stack_data[s->stack_end++] = data;
	return 0;
}

int
stack_pop(struct stack_list *s, TYPE **data)
{
	if (s == NULL || s->stack_end == 0) {
		errno = ENOENT;
		return -1;
	}

	*data = s->stack_data[--s->stack_end];
	if (s->stack_end < (s->stack_size / 2))
		stack_decr(s);
	return 0;
}

int
stack_peek(struct stack_list *s, TYPE **data)
{
	if (s == NULL || s->stack_end == 0) {
		errno = ENOENT;
		return -1;
	}
	if (data != NULL)
		*data = s->stack_data[s->stack_end - 1];
	return 0;
}

unsigned
stack_count(struct stack_list *s)
{
	return s->stack_end;
}

int
stack_walk(struct stack_list *s, ssize_t *pos, TYPE **data)
{
	/* The fifos data structure is the same */
	/* LINTED s */
	return fifo_walk((struct fifo_list *)s, pos, data);
}

int
stack_walk_back(struct stack_list *s, ssize_t *pos, TYPE **data)
{
	/* The fifos data structure is the same */
	/* LINTED s */
	return fifo_walk_back((struct fifo_list *)s, pos, data);
}



#define PAT_IS_NODE(node, leg) ((node)->pat_is_node & (leg + 1))
#define PAT_IS_LEAF(node, leg) (!PAT_IS_NODE(node, leg))

#define INT_BITS	32


#ifdef CONTAINER_DEBUG
static int
#else
static __inline int
#endif
bit_is_set(int bit_nr, const u_int8_t *bit_field)
{
	unsigned	num_bits, one;

	num_bits = 8;
	one = 1;
	/* LINTED, it's OK i know what I'm doing */
    return ((bit_field[bit_nr / num_bits] >> (bit_nr % num_bits)) & one);
}

#ifdef CONTAINER_DEBUG
static int
#else
static __inline int
#endif
lowest_diff_bit(const u_int8_t *n1, size_t l1, const u_int8_t *n2, size_t l2)
{
	int		bit_field;	/* XXX because of ffs declaration */
	int		diff_bit;
	u_int8_t	zeroes = 0;
	int		sl1, sl2;

	sl1 = l1;
	sl2 = l2;
	if (sl1 == 0) { sl1 = -1; n1 = &zeroes; }
	if (sl2 == 0) { sl2 = -1; n2 = &zeroes; }
	for (;;) {
		if (sl1 < 0 && sl2 < 0)
			errx(2, "lowest_diff_bit: keys are equal, this shouldn't happen");
		bit_field = *n1 ^ *n2;
		diff_bit = ffs(bit_field) - 1;
		if (diff_bit >= 0)
			return diff_bit;
		if (sl1 >= 0) { sl1--; n1++; } else { sl1 = -1; n1 = &zeroes; }
		if (sl2 >= 0) { sl2--; n2++; } else { sl2 = -1; n2 = &zeroes; }
	}
}

static int
pat_keys_match(const u_int8_t *k1, size_t l1, const u_int8_t *k2, size_t l2)
{
	u_int8_t zeroes = 0;

	if (l1 == 0)
		k1 = &zeroes;
	else if (l2 == 0)
		k2 = &zeroes;
	for (;;) {
		if (l1 == 0 && l2 == 0)
			break;
		if (*k1 != *k2)
			return 0;
		if (l1 != 0) { l1--; k1++; } else k1 = &zeroes;
		if (l2 != 0) { l2--; k2++; } else k2 = &zeroes;
	}
	return 1;
}


static __inline void
pat_make_node(struct pat_node_s *node, int leg, struct pat_node_s *child)
{
	node->pat_node[leg] = child;
	node->pat_is_node |= leg + 1; /* mark it as a node */
}

static __inline void
pat_make_leaf(struct pat_node_s *node, int leg,
	const void *key, size_t key_len, TYPE *data)
{
	node->pat_key[leg] = key;
	node->pat_key_len[leg] = key_len;
	node->pat_data[leg] = data;
	node->pat_is_node &= ~(leg + 1); /* mark it as a leaf */
}


int
pat_ins(struct pat_tree *pat, const void *key, size_t key_len, TYPE *data)
{
	struct	pat_node_s *p;
	struct	pat_node_s **p_list;
	int		i, j;
	int		bit_set, node_set, diff_bit;

	/* Stop the walk */
	if (pat->pat_walk_stack != NULL)
		stack_free(pat->pat_walk_stack, NULL);
/*** to few elements ***/
	if (pat->pat_keys < 2) {

		/* we allready have one element */
		if (pat->pat_keys == 1) {
			if (pat_keys_match(pat->pat_key, (unsigned)pat->pat_key_len,
				key, (unsigned)key_len)) {
				errno = EEXIST;
				return -1;
			}
			pat->pat_root = malloc(sizeof(struct pat_node_s));
			if (pat->pat_root == NULL) {
				if (container_err_exit)
					err(1, "pat_ins: malloc");
				warn("pat_ins: malloc");
				return -1;
			}
			diff_bit = lowest_diff_bit(pat->pat_key, (unsigned)pat->pat_key_len,
				key, (unsigned)key_len);
			bit_set = bit_is_set(diff_bit, key);

			pat_make_leaf(pat->pat_root, bit_set, key, key_len, data);

			/* copy root element in to the other leaf */
			pat_make_leaf(pat->pat_root, !bit_set,
				pat->pat_key, (unsigned)pat->pat_key_len, pat->pat_data);

			pat->pat_keys = 2;
			pat->pat_key_len_max = 1;
			return 0;
		}

		/* this is the first element */
		pat->pat_key = key;
		pat->pat_key_len = key_len;
		pat->pat_data = data;
		pat->pat_keys = 1;
		pat->pat_key_len_max = 1;
		return 0;
	}


/*** common case ***/
	pat->pat_key_len_max = pat->pat_keys;	/* XXX too lazy to fix this */
	if ((p_list = alloca((size_t)pat->pat_key_len_max * sizeof(*p_list)))
		== NULL)
		err(1, "alloca");

	/*
	 * Walk down the tree on the matching path, record
	 * the path for later use.
	 */
	p = pat->pat_root;
	for (i = 0;; i++) {
		p_list[i] = p;
		node_set = bit_is_set(p->pat_bit, key);
		if (PAT_IS_LEAF(p, node_set)) {
			if (pat_keys_match(p->pat_key[node_set],
				(unsigned)p->pat_key_len[node_set],
				key, key_len)) {
				errno = EEXIST;
				return -1;
			}
			break;
		}
		p = p->pat_node[node_set];
	}


	/*
	 * Find the differing compared to found leaf. Locate at which
	 * node the element should be inserted.
	 */
	diff_bit = lowest_diff_bit(
		p->pat_key[node_set], (unsigned)p->pat_key_len[node_set], key, key_len);
	for (j = i; j > 0 && p_list[j]->pat_bit > diff_bit; j--);

	if ((p = malloc(sizeof(*p))) == NULL) {
		if (container_err_exit)
			err(1, "pat_ins: malloc");
		warn("pat_ins: malloc");
		return -1;
	}
	pat->pat_keys++;
	p->pat_bit = diff_bit;

	/* The root is to be replaced */
	if (j == 0 && pat->pat_root->pat_bit > diff_bit) {
		/* which leg does old root sort under? */
		bit_set = bit_is_set(diff_bit, key);

		/* put old root under p->pat_node[!bit_set] */
		pat_make_node(p, !bit_set, pat->pat_root);

		/* put key and data in other leg */
		pat_make_leaf(p, bit_set, key, key_len, data);

		/* replace root */
		pat->pat_root = p;
		return 0;
	}

	/* put our new node under p_list[j] */

	node_set = bit_is_set(p_list[j]->pat_bit, key);
	bit_set = bit_is_set(diff_bit, key);

	/* put data in on of p's legs */
	pat_make_leaf(p, bit_set, key, key_len, data);

	/* put what's under p_list[j][node_set] under p's other leg */
	if (PAT_IS_LEAF(p_list[j], node_set))
		pat_make_leaf(p, !bit_set,
				p_list[j]->pat_key[node_set],
				(unsigned)p_list[j]->pat_key_len[node_set],
				p_list[j]->pat_data[node_set]);
	else
		pat_make_node(p, !bit_set, p_list[j]->pat_node[node_set]);

	pat_make_node(p_list[j], node_set, p);
	return 0;
}

struct pat_tree *
pat_init(void)
{
	struct	pat_tree *pat;

	if ((pat = calloc((size_t)1, sizeof(*pat))) == NULL) {
		if (container_err_exit)
			err(1, "pat_init: calloc");
		warn("pat_init: calloc");
		return NULL;
	}
	pat->pat_keys = 0;
	return pat;
}

static void
pat_free_helper(struct pat_node_s *n,
	pat_free_f free_fun_data, pat_free_key_f free_fun_key)
{
	int		i;

	for (i = 0; i < 2; i++) {
		if (PAT_IS_LEAF(n, i)) {
			if (free_fun_data != NULL)
				free_fun_data(n->pat_data[i]);
			if (free_fun_key != NULL)
				free_fun_key(&n->pat_key[i]);
		} else
			pat_free_helper(n->pat_node[i], free_fun_data, free_fun_key);
	}
	free(n);
}

void
pat_free(struct pat_tree *pat,
	pat_free_f free_fun_data, pat_free_key_f free_fun_key)
{
	const void *p[1];

	if (pat->pat_keys == 1) {
		if (free_fun_data != NULL)
			free_fun_data(pat->pat_data);
		if (free_fun_key != NULL) {
			p[0] = pat->pat_key;
			free_fun_key(&p[0]);	/* fool gcc */
		}
	} else if (pat->pat_keys != 0)
		pat_free_helper(pat->pat_root, free_fun_data, free_fun_key);

	free(pat);
}

static void
pat_map_helper(struct pat_node_s *n, pat_map_f map_fun, TYPE *arg_obj)
{
	int		i;

	for (i = 0; i < 2; i++) {
		if (PAT_IS_LEAF(n, i))
			map_fun(n->pat_key[i], (unsigned)n->pat_key_len[i],
				n->pat_data[i], arg_obj);
		else
			pat_map_helper(n->pat_node[i], map_fun, arg_obj);
	}
}

void
pat_map(struct pat_tree *pat, pat_map_f map_fun, TYPE *arg_obj)
{
	if (pat->pat_keys == 1)
		map_fun(pat->pat_key, (unsigned)pat->pat_key_len,
			pat->pat_data, arg_obj);
	else if (pat->pat_keys != 0)
		pat_map_helper(pat->pat_root, map_fun, arg_obj);
}

static int
pat_walk_helper(struct pat_tree *pat, int forw,
	struct pat_node_s **pos, TYPE **data)
{
	struct stack_list *s;
	struct	pat_node_s *p, *op;
	int		back;

	back = (forw + 1) % 2;

	if (pat->pat_keys == 0) {
		errno = ENOENT;
		return -1;
	}
	s = pat->pat_walk_stack;
	if (*pos == NULL) {
		if (s != NULL)
			stack_free(s, NULL);
		if ((s = stack_init()) == NULL) {
			warn("pat_walk: stack_init");
			return -1;
		}
		pat->pat_walk_stack = s;
		if (stack_push(s, pat->pat_root) < 0) {
			warn("pat_walk: stack_push");
			goto ret_bad;
		}
		pat->pat_walk_did_forw = 0;
	}
	if (stack_peek(s, (TYPE **)&p) < 0)
		goto ret_bad;

	/* Seek bottom of leg forw. Put the path on a stack. */
	if (pat->pat_walk_did_forw == 0) {
		while (PAT_IS_NODE(p, forw)) {
			p = p->pat_node[forw];
			if (stack_push(s, p) < 0) {
				warn("pat_walk: stack_push");
				goto ret_bad;
			}
		}
		pat->pat_walk_did_forw = 1;
		*pos = p;
		*data = p->pat_data[forw];
		return 0;
	}

	/* Are at bottom of leg forw, continue on leg back */

	/* Leg back is a leaf */
	if (PAT_IS_LEAF(p, back)) {
		*data = p->pat_data[back];
		if (stack_pop(s, (TYPE **)&op) < 0)
			return 0;
		while (stack_pop(s, (TYPE **)&p) == 0) {
			if (PAT_IS_LEAF(p, back) || op != p->pat_node[back]) {
				if (stack_push(s, (TYPE *)p) < 0)
					return -1;
				return 0;
			}
			op = p;
		}
		return 0;
	}

	/* Prepare for a decend to bottom on leg 0 of the new node */
	p = p->pat_node[back];
	pat->pat_walk_did_forw = 0;
	if (stack_push(s, p) < 0) {
		warn("pat_walk: stack_push");
		goto ret_bad;
	}
	return pat_walk_helper(pat, forw, pos, data);

ret_bad:
	if (s != NULL)
		stack_free(s, NULL);
	s = NULL;
	return -1;
}

int
pat_walk(struct pat_tree *pat, struct pat_node_s **pos, TYPE **data)
{
	return pat_walk_helper(pat, 1, pos, data);
}

int
pat_walk_back(struct pat_tree *pat, struct pat_node_s **pos, TYPE **data)
{
	return pat_walk_helper(pat, 0, pos, data);
}


int
pat_find(struct pat_tree *pat, const void *key, size_t key_len, TYPE **data)
{
	struct	pat_node_s *p;
	int		bit_set;

/****** too few elements ******/
	if (pat->pat_keys < 2) {
		if (pat->pat_keys == 0) {
			errno = ENOENT;
			return -1;
		}
		if (!pat_keys_match(pat->pat_key, (unsigned)pat->pat_key_len,
			key, key_len)) {
			errno = ENOENT;
			return -1;
		}
		if (data != NULL)
			*data = pat->pat_data;
		return 0;
	}

/******* common case *******/
	/*
	 * Walk down the tree until a leaf is found,
	 * if key matches the element is found.
	 */
	for (p = pat->pat_root;; ) {
		bit_set = bit_is_set(p->pat_bit, key);
		if (PAT_IS_LEAF(p, bit_set)) {
			if (pat_keys_match(p->pat_key[bit_set],
				(unsigned)p->pat_key_len[bit_set], key, key_len)) {
				if (data != NULL)
					*data = p->pat_data[bit_set];
				return 0;
			}
			errno = ENOENT;
			return -1;
		}
		p = p->pat_node[bit_set];
	}
}

int
pat_rem(struct pat_tree *pat, const void *key, size_t key_len, TYPE **data)
{
	struct	pat_node_s *p;
	struct	pat_node_s *last;
	int		bit_set;
	int		last_bit_set;

	if (pat == NULL) {
		errno = ENOENT;
		return -1;
	}
	/* Stop the walk */
	if (pat->pat_walk_stack != NULL)
		stack_free(pat->pat_walk_stack, NULL);

/****** too few elements ******/
	if (pat->pat_keys < 3) {
		int		i;

		switch(pat->pat_keys) {
		case 0:
			errno = ENOENT;
			return -1;
		case 1:
			if (!pat_keys_match(pat->pat_key, (unsigned)pat->pat_key_len,
				key, key_len)) {
				errno = ENOENT;
				return -1;
			}
			pat->pat_keys = 0;
			if (data != NULL)
				*data = pat->pat_data;
			return 0;
		case 2:
			for (i = 0; i < 2; i++) {
				if (pat_keys_match(key, key_len,
					pat->pat_root->pat_key[i],
					(unsigned)pat->pat_root->pat_key_len[i])) {
					pat->pat_key = pat->pat_root->pat_key[!i];
					pat->pat_key_len = pat->pat_root->pat_key_len[!i];
					pat->pat_data = pat->pat_root->pat_data[!i];
					pat->pat_keys = 1;
					if (data != NULL)
						*data = pat->pat_root->pat_data[i];
					free(pat->pat_root);
					return 0;
				}
			}
			errno = ENOENT;
			return -1;

		default:
			break;
		}
	}

/******* common case *******/

	last = NULL;
	last_bit_set = 0; /* quiet gcc */
	for (p = pat->pat_root;; ) {
		bit_set = bit_is_set(p->pat_bit, key);
		if (PAT_IS_LEAF(p, bit_set)) {
			if (!pat_keys_match(p->pat_key[bit_set],
				(unsigned)p->pat_key_len[bit_set], key, key_len)) {
				errno = ENOENT;
				return -1;
			}
			break;
		}
		last = p;
		last_bit_set = bit_set;
		p = p->pat_node[bit_set];
	}

	if (last == NULL) { /* p == pat->pat_root */
		pat->pat_root = p->pat_node[!bit_set];
	} else {
		if (PAT_IS_LEAF(p, !bit_set))
			pat_make_leaf(last, last_bit_set,
				p->pat_key[!bit_set], (unsigned)p->pat_key_len[!bit_set],
				p->pat_data[!bit_set]);
	else
		pat_make_node(last, last_bit_set, p->pat_node[!bit_set]);
	}
	if (data != NULL)
		*data = p->pat_data[bit_set];
	free(p);
	pat->pat_keys--;
	return 0;
}

unsigned
pat_count(struct pat_tree *pat, int *num)
{
	return pat->pat_keys;
}


#define HEAP_CHUNK 2
#define HEAP_PARENT(i)														\
	/* LINTED (no worries) */												\
	((int) ((i) >> 1))
#define HEAP_LEFT(i)	((int) ((i) << 1))
#define HEAP_RIGHT(i)	(((int) ((i) << 1) + 1))


struct heap_list *
heap_init(heap_comp_f comp_fun)
{
	struct	heap_list *h;

	if ((h = malloc(sizeof(*h))) == NULL) {
		if (container_err_exit)
			err(1, "heap_init: malloc");
		warn("heap_init: malloc");
		return NULL;
	}
	h->heap_data = malloc(HEAP_CHUNK * sizeof(*h->heap_data));
	if (h->heap_data == NULL) {
		free(h);
		if (container_err_exit)
			err(1, "heap_init: malloc");
		warn("heap_init: malloc");
		return NULL;
	}
	
	h->heap_size = HEAP_CHUNK;
	h->heap_keys = 0;
	h->heap_comp_fun = comp_fun;
	return h;
}

struct heap_list *
heap_clone(struct heap_list *h)
{
	struct	heap_list *hc;

	if (h == NULL) {
		errno = 0;
		return NULL;
	}
	if ((hc = malloc(sizeof(*hc))) == NULL) {
		if (container_err_exit)
			err(1, "heap_clone: malloc");
		warn("heap_clone: malloc");
		return NULL;
	}
	(void)memcpy(hc, h, sizeof(*hc));
	hc->heap_data = malloc(sizeof(*hc->heap_data) * hc->heap_size);
	if (hc->heap_data == NULL) {
		if (container_err_exit)
			err(1, "heap_clone: malloc");
		warn("heap_clone: malloc");
		free(hc);
		return NULL;
	}
	(void)memcpy(hc->heap_data, h->heap_data,
		sizeof(*hc->heap_data) * hc->heap_size);
	return hc;
}

void
heap_free(struct heap_list *h, heap_free_f free_fun)
{
	int		i;
	void	**p;

	p = h->heap_data;
	if (free_fun != NULL)
		for (i = 0; i < h->heap_keys; i++)
			free_fun(p[i]);
	free(p);
	free(h);
}

static int
heap_inc(struct heap_list *h)
{
	void		**p;
	unsigned	size;

	size = h->heap_size * 2;
	if ((p = realloc(h->heap_data, size * sizeof(*p))) == NULL)
		return -1;
	h->heap_data = p;
	h->heap_size = size;
	return 0;
}

static void
heap_decr(struct heap_list *h)
{
	void		**p;
	unsigned	size;

	if (h->heap_size == HEAP_CHUNK)
		return;

	size = h->heap_size / 2;
	if ((p = realloc(h->heap_data, size * sizeof(*p))) == NULL)
		return;
	h->heap_data = p;
	h->heap_size = size;
}

static void
heap_exchange(struct heap_list *h, int a, int b)
{
	void	**p, *tmp;

	p = h->heap_data;
	tmp = p[a];
	p[a] = p[b];
	p[b] = tmp;
}

static void
heap_heapify(struct heap_list *h, int i)
{
	int		l, r, largest;
	void	**p;

restart:
	l = HEAP_LEFT(i);
	r = HEAP_RIGHT(i);
	p = h->heap_data;

	if (l < h->heap_keys && (h->heap_comp_fun(p[l], p[i]) > 0))
		largest = l;
	else
		largest = i;
	if (r < h->heap_keys && h->heap_comp_fun(p[r], p[largest]) > 0)
		largest = r;
	if (largest != i) {
		heap_exchange(h, i, largest);
		i = largest;
		goto restart;
	}
}

int
heap_push(struct heap_list *h, TYPE *data)
{
	int		i;
	void	**p;

	if (h->heap_keys == h->heap_size)
		if (heap_inc(h) < 0)
			return -1;

	i = h->heap_keys;
	p = h->heap_data;
	while (i > 0 && h->heap_comp_fun(p[HEAP_PARENT(i)], data) < 0) {
		p[i] = p[HEAP_PARENT(i)];
		i = HEAP_PARENT(i);
	}
	p[i] = data;
	h->heap_keys++;
	return 0;
}

int
heap_pop(struct heap_list *h, TYPE **data)
{
	if (h == NULL || h->heap_keys == 0) {
		errno = ENOENT;
		return -1;
	}
	if (data != NULL)
		*data = h->heap_data[0];
	if (h->heap_keys < (h->heap_size / 2))
		heap_decr(h); 
	h->heap_data[0] = h->heap_data[--h->heap_keys];
	heap_heapify(h, 0);
	return 0;
}

int
heap_peek(struct heap_list *h, TYPE **data)
{
	if (h == NULL || h->heap_keys == 0) {
		errno = ENOENT;
		return -1;
	}
	if (data != NULL)
		*data = h->heap_data[0];
	return 0;
}

unsigned
heap_count(struct heap_list *h)
{
	return h->heap_keys;
}

int
heap_walk(struct heap_list *h, ssize_t *pos, TYPE **data)
{
	if (*pos > h->heap_size) {
		errno = ENOENT;
		return -1;
	}
	if (data != NULL)
		*data = h->heap_data[*pos];
	(*pos)++;
	return 0;
}

int
heap_walk_back(struct heap_list *h, ssize_t *pos, TYPE **data)
{
	if (*pos < 0) {
		errno = ENOENT;
		return -1;
	}
	if (data != NULL)
		*data = h->heap_data[*pos];
	(*pos)--;
	return 0;
}


struct hash_buf *
hash_init(size_t hash_size)
{
	struct	hash_buf *p;

	if ((p = malloc(sizeof(*p))) == NULL) {
		if (container_err_exit)
			err(1, "hash_init: malloc");
		warn("hash_init: malloc");
		return NULL;
	}
	if ((p->hash_data = calloc(hash_size, sizeof(*p->hash_data))) == NULL) {
		free(p);
		if (container_err_exit)
			err(1, "hash_init: malloc");
		warn("hash_init: malloc");
		return NULL;
	}
	p->hash_size = (u_int32_t)hash_size;
	return p;
}

void
hash_map(struct hash_buf *p, hash_map_f map_fun, TYPE *arg_obj)
{
	struct	hash_data_s *he;
	int		i;

	for (i = 0; i < p->hash_size; i++) {
		he = p->hash_data + i;
		if (he->he_data == NULL)
			continue;
		while (he != NULL) {
			map_fun(he->he_key, he->he_data, arg_obj);
			he = he->he_next;
		}
	}
}

struct hash_clone_helper {
	struct	hash_buf *hch_hb;
	int		hch_error;
};

static void
hash_clone_helper(u_int32_t key, TYPE *data, void *p)
{
	struct	hash_clone_helper *hch = p;

	if (hch->hch_error)
		return;
	if (hash_ins(hch->hch_hb, key, data) < 0) {
		hch->hch_error = 1;
		return;
	}
}

struct hash_buf *
hash_clone(struct hash_buf *p)
{
	struct	hash_buf *hb;
	struct	hash_clone_helper hch;
	int		oerrno;

	if ((hb = hash_init((size_t)p->hash_size)) == NULL)
		return NULL;
	hch.hch_error = 0;
	hch.hch_hb = hb;
	hash_map(p, hash_clone_helper, &hch);
	if (hch.hch_error) {
		oerrno = errno;
		hash_free(hb, NULL);
		errno = oerrno;
		return NULL;
	}
	return hb;
}

void
hash_free(struct hash_buf *p, hash_free_f free_fun)
{
	struct		hash_data_s *he, *q;
	u_int32_t	i;

	for (i = 0; i < p->hash_size; i++) {
		he = p->hash_data + i;
		if (he->he_data == NULL)
			continue;
		q = he;
		he = he->he_next;
		if (free_fun != NULL)
			free_fun(q->he_data);
		while (he != NULL) {
			q = he;
			he = he->he_next;
			if (free_fun != NULL)
				free_fun(q->he_data);
			free(q);
		}
	}
	free(p->hash_data);
	free(p);
}

size_t
hash_count(struct hash_buf *p)
{
	size_t	i, datas;
	struct	hash_data_s *he;

	for (i = 0, datas = 0; i < p->hash_size; i++) {
		he = p->hash_data + i;
		if (he->he_data == NULL)
			continue;
		datas++;
		he = he->he_next;
		while (he != NULL) {
			datas++;
			he = he->he_next;
		}
	}
	return datas;
}

int
hash_ins(struct hash_buf *p, u_int32_t key, TYPE *data)
{
	struct		hash_data_s *he, *q;
	u_int32_t	k;

	k = key % p->hash_size;
	if (p->hash_data[k].he_data == NULL) {
		p->hash_data[k].he_data = data;
		p->hash_data[k].he_key = key;
		return 0;
	}
	if ((he = calloc((size_t)1, sizeof(*he))) == NULL) {
		if (container_err_exit)
			err(1, "hash_ins: malloc");
		warn("hash_ins: malloc");
		return -1;
	}
	for (q = p->hash_data + k; q->he_next != NULL; q = q->he_next);
	q->he_next = he;
	he->he_data = data;
	he->he_key = key;
	return 0;
}

int
hash_find(struct hash_buf *p, u_int32_t key, TYPE **data)
{
	struct		hash_data_s *he;
	u_int32_t	k;

	k = key % p->hash_size;
	if (p->hash_data[k].he_data == NULL)		/* not found */
		return -1;

	he = p->hash_data + k;
	while (he != NULL) {
		if (he->he_key == key) {
			*data = he->he_data;
			return 0;
		}
		he = he->he_next;
	}
	errno = ENOENT;
	return -1;
}

int
hash_rem(struct hash_buf *p, u_int32_t key, TYPE **data)
{
	struct		hash_data_s *he, *q, *he_last;
	u_int32_t	k;

	k = key % p->hash_size;
	if (p->hash_data[k].he_data == NULL)		/* not found */
		return -1;

	he = p->hash_data + k;
	he_last = NULL; /* get rid of lint warning */
	while (he != NULL) {
		if (he->he_key == key) {			/* found it */
			*data = he->he_data;
			if (he == p->hash_data + k) {
				if (he->he_next != NULL) {	/* move up the folowing element	*/
					he->he_data = he->he_next->he_data;
					he->he_key = he->he_next->he_key;
					q = he->he_next;
					he->he_next = he->he_next->he_next;
					free(q);
				}
			} else {
				he_last->he_next = he->he_next;
				free(he);
			}
			return 0;
		}
		he_last = he;
		he = he->he_next;
	}
	return -1;

}

/*
 * Skip Lists
 *
 *Refer to William Pugh's paper Skip Lists: A Probabalistic
 *Alternative to Balanced Trees
 *
 */

/* used by Skip Lists as a terminator of the list */
static struct skip_list_node sl_NIL_space = {NULL, 0xffffffff, {NULL}};
static struct skip_list_node *sl_NIL = &sl_NIL_space;

static struct skip_list_node *
sl_new_node(int level, u_int32_t key, TYPE *data)
{
	struct	skip_list_node *sln;
	size_t	size;

	
	size = sizeof(struct skip_list_node) + level * sizeof(void *);
	if ((sln = malloc(size)) == NULL) {
		if (container_err_exit)
			err(1, "sl_init: malloc");
		warn("sl_new_node: malloc");
		return NULL;
	}
	sln->sln_key = key;
	sln->sln_data = data;
	return sln;
}

struct skip_list *
sl_init(void)
{
	struct	skip_list *sl;
	int		i;

	if ((sl = malloc(sizeof(struct skip_list))) == NULL) {
		if (container_err_exit)
			err(1, "sl_init: malloc");
		warn("sl_init: malloc");
		return NULL;
	}
	sl->sl_level = 0;
	sl->sl_random_bits = (int)random();
	sl->sl_randoms_left = SL_BITS_IN_RANDOM / 2;
	if ((sl->sl_head = sl_new_node(SL_MAX_LEVELS, 0, NULL)) == NULL) {
		free(sl);
		return NULL;
	}
	for (i = 0; i < SL_MAX_LEVELS; i++)
		sl->sl_head->sln_forw[i] = sl_NIL;
	return sl;
}

void
sl_free(struct skip_list *sl, sl_free_f free_fun)
{
	struct	skip_list_node *p, *q;

	p = sl->sl_head;
	do {
		q = p->sln_forw[0];
		if (free_fun != NULL && p->sln_data != NULL)
			free_fun(p->sln_data);
		free(p);
		p = q;
	} while (p != sl_NIL);
	free(sl);
}

void
sl_find_first_pos(struct skip_list *sl,
	struct skip_list_node **pos, TYPE **data)
{
	if (pos != NULL)
		*pos = sl->sl_head->sln_forw[0];
	if (data != NULL)
		*data = sl->sl_head->sln_forw[0]->sln_data;
}

int
sl_walk(struct skip_list *sl, struct skip_list_node **pos, TYPE **data)
{
	if (pos == NULL)
		return 0;
	if ((*pos)->sln_forw[0] == sl_NIL) {
		errno = ENOENT;
		return -1;
	}
	*pos = (*pos)->sln_forw[0];
	if (data != NULL)
		*data = (*pos)->sln_data;
	return 0;
}

int
sl_find_pos(struct skip_list *sl, u_int32_t key,
	struct skip_list_node **pos, TYPE **data)
{
	int		i;
	struct	skip_list_node *p;

	p = sl->sl_head;
	for (i = sl->sl_level; i >= 0; i--) {
		for (; p->sln_forw[i]->sln_key < key; p = p->sln_forw[i]);
	}
	p = p->sln_forw[0];
	if (p->sln_key != key) {
		errno = ENOENT;
		return -1;
	}
	if (pos != NULL)
		*pos = p;
	if (data != NULL)
		*data = p->sln_data;
	return 0;
}

int
sl_find(struct skip_list *sl, u_int32_t key, TYPE **data)
{
	struct skip_list_node *p;

	if (sl_find_pos(sl, key, &p, data) < 0)
		return -1;
	return 0;
}


/* Helper to sl_ins() and sl_rem() */

static int
sl_random_level(struct skip_list *sl)
{
	int b, level;

	level = 0;
	do {
		b = sl->sl_random_bits & 3;
		if (b == 0)
			level++;
		sl->sl_random_bits >>= 2;
		if (--sl->sl_randoms_left == 0) {
			sl->sl_random_bits = (int)random();
			sl->sl_randoms_left = SL_BITS_IN_RANDOM / 2;
		}
	} while (b != 0);
	if (level > SL_MAX_LEVEL)
		return SL_MAX_LEVEL;
	return level;
}


int
sl_ins(struct skip_list *sl, u_int32_t key, TYPE *data)
{
	struct	skip_list_node *update[SL_MAX_LEVELS], *p;
	int		i, new_level, level;

	/* Locate element right before the locatation of insert.
	 * Keep track of all pointer that might point to the new
	 * element.
	 */
	p = sl->sl_head;
	level = sl->sl_level;
	for (i = level; i >= 0; i--) {
		for (; p->sln_forw[i]->sln_key < key; p = p->sln_forw[i]);
		update[i] = p;
	}

	/* Determine the new level and keep track of new pointers
	 * if necessary.
	 */
	new_level = sl_random_level(sl);
	if (new_level > level) {
		for (i = level + 1; i <= new_level; i++)
			update[i] = sl->sl_head;
		sl->sl_level = level = new_level;
	}

	/* create node */
	if ((p = sl_new_node(new_level, key, data)) == NULL)
		return -1;

	/* splice in out element and point that elements pointer up
	 * to new_level to what the recorded pointer pointed to.
	 * Point all recorded pointers up to new_level to the new
	 * element.
	 */
	for (i = 0; i <= new_level; i++) {
		p->sln_forw[i] = update[i]->sln_forw[i];
		update[i]->sln_forw[i] = p;
	}
	return 0;
}

int
sl_rem(struct skip_list *sl, u_int32_t key, TYPE **data)
{
	struct	skip_list_node *update[SL_MAX_LEVELS], *p;
	int		i, level;

	/* Locate element to be removed.
	 * Keep track of all pointer that might point to the new
	 * element.
	 */
	level = sl->sl_level;
	p = sl->sl_head;
	for (i = level; i >= 0; i--) {
		for (; p->sln_forw[i]->sln_key < key; p = p->sln_forw[i]);
		update[i] = p;
	}
	p = p->sln_forw[0];
	if (p->sln_key != key)
		return -1;

	/* Remove the found element from the list by:
	 * Point pointer pointing to the found element to
	 * what the pointers of the found element points to
	 */
	for (i = 0; i <= level; i++) {
		if (update[i]->sln_forw[i] != p)
			break;
		update[i]->sln_forw[i] = p->sln_forw[i];
	}
	if (data != NULL)
		*data = p->sln_data;
	free(p);

	/* Record the new level of the list */
	p = sl->sl_head;
	for (; level >= 0 && p->sln_forw[level] == sl_NIL; level--);
	sl->sl_level = level;
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1