mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-06-14 18:08:30 +02:00
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:
parent
02804773e7
commit
c2509f8a72
2 changed files with 325 additions and 0 deletions
87
src/cairo-skiplist-private.h
Normal file
87
src/cairo-skiplist-private.h
Normal 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
238
src/cairo-skiplist.c
Normal 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));
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue