From c2509f8a721ec489e1b44fa8a68be165363787a7 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Wed, 20 Sep 2006 10:27:35 -0700 Subject: [PATCH] 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. --- src/cairo-skiplist-private.h | 87 +++++++++++++ src/cairo-skiplist.c | 238 +++++++++++++++++++++++++++++++++++ 2 files changed, 325 insertions(+) create mode 100644 src/cairo-skiplist-private.h create mode 100644 src/cairo-skiplist.c diff --git a/src/cairo-skiplist-private.h b/src/cairo-skiplist-private.h new file mode 100644 index 000000000..616609ebe --- /dev/null +++ b/src/cairo-skiplist-private.h @@ -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 + * 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 */ +void * +skip_list_find (skip_list_t *list, void *data); + +/* Delete an element which compare considers equal to */ +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 diff --git a/src/cairo-skiplist.c b/src/cairo-skiplist.c new file mode 100644 index 000000000..49b78de6e --- /dev/null +++ b/src/cairo-skiplist.c @@ -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 +#include +#include +#include +#include + +#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)); +}