Add skip list implementation (many thanks to Keith Packard)

The files here are copied directly from the standalone skiplist module
available from:

	git clone git://cworth.org/~cworth/skiplist

In particular the files come from the double branch and the following
commit on that branch:

	8b5a439c68e220cf1514d9b3141a1dbdce8af585

Also of interest is the original skiplist module hosted by Keith Packard
that is the original implementation on which these files were based.
Since the cworth/skiplist branched off of keithp's, Keith has also
now implemented a doubly-linked variant which might be interesting for
further simplification of the code. See:

	git clone git://keithp.com/git/skiplist

and the double-link branch there.
This commit is contained in:
Carl Worth 2006-09-20 10:27:35 -07:00
parent 02804773e7
commit c2509f8a72
2 changed files with 325 additions and 0 deletions

View file

@ -0,0 +1,87 @@
/*
* Copyright © 2006 Keith Packard
* Copyright © 2006 Carl Worth
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#ifndef SKIPLIST_H
#define SKIPLIST_H
#define MAX_LEVEL 31
/*
* Skip list element. In order to use the skip list, the caller must
* generate a structure for list elements that has as its final member
* a skip_elt_t, (which will be allocated with variable size).
*
* The caller must also pass the size of the structure to
* skip_list_init.
*/
typedef struct _skip_elt {
int prev_index;
struct _skip_elt *prev;
struct _skip_elt *next[1];
} skip_elt_t;
#define SKIP_LIST_ELT_TO_DATA(type, elt) ((type *) ((char *) (elt) - (sizeof (type) - sizeof (skip_elt_t))))
typedef int
(*skip_list_compare_t) (void *list, void *a, void *b);
typedef struct _skip_list {
skip_list_compare_t compare;
size_t elt_size;
size_t data_size;
skip_elt_t *chains[MAX_LEVEL];
int max_level;
} skip_list_t;
/* Initialize a new skip list. The compare function accepts a pointer
* to the list as well as pointers to two elements. The function must
* return a value greater than zero, zero, or less then 0 if the first
* element is considered respectively greater than, equal to, or less
* than the second element. The size of each object, (as computed by
* sizeof) is passed for elt_size. Note that the structure used for
* list elements must have as its final member a skip_elt_t
*/
void
skip_list_init (skip_list_t *list,
skip_list_compare_t compare,
size_t elt_size);
/* Insert a new element into the list at the correct sort order as
* determined by compare. Data will be copied (elt_size bytes from
* <data> via memcpy). The new element is returned. */
void *
skip_list_insert (skip_list_t *list, void *data);
/* Find an element which compare considers equal to <data> */
void *
skip_list_find (skip_list_t *list, void *data);
/* Delete an element which compare considers equal to <data> */
void
skip_list_delete (skip_list_t *list, void *data);
/* Delete the given element from the list. */
void
skip_list_delete_given (skip_list_t *list, skip_elt_t *given);
#endif

238
src/cairo-skiplist.c Normal file
View file

@ -0,0 +1,238 @@
/*
* Copyright © 2006 Keith Packard
* Copyright © 2006 Carl Worth
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include "cairo-skiplist-private.h"
#define ELT_DATA(elt) (void *) ((char*) (elt) - list->data_size)
#define NEXT_TO_ELT(next) (skip_elt_t *) ((char *) (next) - offsetof (skip_elt_t, next))
/*
* Initialize an empty skip list
*/
void
skip_list_init (skip_list_t *list,
skip_list_compare_t compare,
size_t elt_size)
{
int i;
list->compare = compare;
list->elt_size = elt_size;
list->data_size = elt_size - sizeof (skip_elt_t);
for (i = 0; i < MAX_LEVEL; i++)
list->chains[i] = NULL;
list->max_level = 0;
}
/*
* Generate a random level number, distributed
* so that each level is 1/4 as likely as the one before
*
* Note that level numbers run 1 <= level <= MAX_LEVEL
*/
static int
random_level (void)
{
/* tricky bit -- each bit is '1' 75% of the time */
long int bits = random () | random ();
int level = 0;
while (++level < MAX_LEVEL)
{
if (bits & 1)
break;
bits >>= 1;
}
return level;
}
/*
* Insert 'data' into the list
*/
void *
skip_list_insert (skip_list_t *list, void *data)
{
skip_elt_t **update[MAX_LEVEL];
char *data_and_elt;
skip_elt_t *elt, *prev, **next;
int i, level, prev_index;
level = random_level ();
prev_index = level - 1;
/*
* Find links along each chain
*/
prev = NULL;
next = list->chains;
for (i = list->max_level; --i >= 0; )
{
for (; (elt = next[i]); next = elt->next)
{
if (list->compare (list, ELT_DATA(elt), data) > 0)
break;
}
update[i] = next;
if (i == prev_index && next != list->chains)
prev = NEXT_TO_ELT (next);
}
/*
* Create new list element
*/
if (level > list->max_level)
{
level = list->max_level + 1;
prev_index = level - 1;
update[list->max_level] = list->chains;
list->max_level = level;
}
data_and_elt = malloc (list->elt_size + (level-1) * sizeof (skip_elt_t *));
memcpy (data_and_elt, data, list->data_size);
elt = (skip_elt_t *) (data_and_elt + list->data_size);
elt->prev_index = prev_index;
elt->prev = prev;
/*
* Insert into all chains
*/
for (i = 0; i < level; i++)
{
elt->next[i] = update[i][i];
if (elt->next[i] && elt->next[i]->prev_index == i)
elt->next[i]->prev = elt;
update[i][i] = elt;
}
return data_and_elt;
}
void *
skip_list_find (skip_list_t *list, void *data)
{
int i;
skip_elt_t **next = list->chains;
skip_elt_t *elt;
/*
* Walk chain pointers one level at a time
*/
for (i = list->max_level; --i >= 0;)
while (next[i] && list->compare (list, data, ELT_DATA(next[i])) > 0)
{
next = next[i]->next;
}
/*
* Here we are
*/
elt = next[0];
if (elt && list->compare (list, data, ELT_DATA (elt)) == 0)
return ELT_DATA (elt);
return NULL;
}
void
skip_list_delete (skip_list_t *list, void *data)
{
skip_elt_t **update[MAX_LEVEL], *prev[MAX_LEVEL];
skip_elt_t *elt, **next;
int i;
/*
* Find links along each chain
*/
next = list->chains;
for (i = list->max_level; --i >= 0; )
{
for (; (elt = next[i]); next = elt->next)
{
if (list->compare (list, ELT_DATA (elt), data) >= 0)
break;
}
update[i] = &next[i];
if (next == list->chains)
prev[i] = NULL;
else
prev[i] = NEXT_TO_ELT (next);
}
elt = next[0];
assert (list->compare (list, ELT_DATA (elt), data) == 0);
for (i = 0; i < list->max_level && *update[i] == elt; i++) {
*update[i] = elt->next[i];
if (elt->next[i] && elt->next[i]->prev_index == i)
elt->next[i]->prev = prev[i];
}
while (list->max_level > 0 && list->chains[list->max_level - 1] == NULL)
list->max_level--;
free (ELT_DATA (elt));
}
void
skip_list_delete_given (skip_list_t *list, skip_elt_t *given)
{
skip_elt_t **update[MAX_LEVEL], *prev[MAX_LEVEL];
skip_elt_t *elt, **next;
int i;
/*
* Find links along each chain
*/
if (given->prev)
next = given->prev->next;
else
next = list->chains;
for (i = given->prev_index + 1; --i >= 0; )
{
for (; (elt = next[i]); next = elt->next)
{
if (elt == given)
break;
}
update[i] = &next[i];
if (next == list->chains)
prev[i] = NULL;
else
prev[i] = NEXT_TO_ELT (next);
}
elt = next[0];
assert (elt == given);
for (i = 0; i < (given->prev_index + 1) && *update[i] == elt; i++) {
*update[i] = elt->next[i];
if (elt->next[i] && elt->next[i]->prev_index == i)
elt->next[i]->prev = prev[i];
}
while (list->max_level > 0 && list->chains[list->max_level - 1] == NULL)
list->max_level--;
free (ELT_DATA (elt));
}