2017-05-19 22:22:40 +02:00
|
|
|
/************************************************************************
|
|
|
|
*
|
|
|
|
* SKIPLIST.C - Skiplist functions for use in Nagios event/object lists
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Notes:
|
|
|
|
*
|
|
|
|
* These function implement a slightly modified skiplist from that
|
|
|
|
* described by William Pugh (ftp://ftp.cs.umd.edu/pub/skipLists/skiplists.pdf).
|
|
|
|
* The structures and function were modified to allow the list to act
|
|
|
|
* like a priority queue for the Nagios event list/queue(s). Multiple nodes with
|
2019-04-18 17:09:18 +02:00
|
|
|
* the same key value are allowed on the list to accommodate multiple events
|
2017-05-19 22:22:40 +02:00
|
|
|
* occurring at the same (second) point in time. Implemented peek() and pop()
|
|
|
|
* functions to allow for quick event queue processing, and a method to delete
|
|
|
|
* a specific list item, based on its pointer, rather than its data value. Again,
|
|
|
|
* this is useful for the Nagios event queue.
|
|
|
|
*
|
|
|
|
* License:
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
************************************************************************/
|
|
|
|
|
2017-05-19 23:37:19 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "skiplist.h"
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct skiplistnode_struct {
|
|
|
|
void *data;
|
|
|
|
struct skiplistnode_struct *forward[1]; /* this must be the last element of the struct, as we allocate # of elements during runtime*/
|
|
|
|
} skiplistnode;
|
|
|
|
|
|
|
|
struct skiplist_struct {
|
|
|
|
int current_level;
|
|
|
|
int max_levels;
|
|
|
|
float level_probability;
|
|
|
|
unsigned long items;
|
|
|
|
int allow_duplicates;
|
|
|
|
int append_duplicates;
|
|
|
|
int (*compare_function)(void *, void *);
|
|
|
|
skiplistnode *head;
|
|
|
|
};
|
|
|
|
|
|
|
|
unsigned long skiplist_num_items(skiplist *list) {
|
|
|
|
return list ? list->items : 0;
|
|
|
|
}
|
2017-05-19 22:22:40 +02:00
|
|
|
|
2017-05-19 23:37:19 +02:00
|
|
|
static skiplistnode *skiplist_new_node(skiplist *list, int node_levels) {
|
|
|
|
skiplistnode *newnode = NULL;
|
|
|
|
register int x = 0;
|
2017-05-19 22:22:40 +02:00
|
|
|
|
2017-05-19 23:37:19 +02:00
|
|
|
if(list == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if(node_levels < 0 || node_levels > list->max_levels)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* allocate memory for node + variable number of level pointers */
|
|
|
|
if((newnode = (skiplistnode *)malloc(sizeof(skiplistnode) + (node_levels * sizeof(skiplistnode *))))) {
|
|
|
|
|
|
|
|
/* initialize forward pointers */
|
|
|
|
for(x = 0; x < node_levels; x++)
|
|
|
|
newnode->forward[x] = NULL;
|
|
|
|
|
|
|
|
/* initialize data pointer */
|
|
|
|
newnode->data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return newnode;
|
|
|
|
}
|
2017-05-19 22:22:40 +02:00
|
|
|
|
|
|
|
|
|
|
|
skiplist *skiplist_new(int max_levels, float level_probability, int allow_duplicates, int append_duplicates, int (*compare_function)(void *, void *)) {
|
|
|
|
skiplist *newlist = NULL;
|
|
|
|
|
|
|
|
/* alloc memory for new list structure */
|
|
|
|
if((newlist = (skiplist *)malloc(sizeof(skiplist)))) {
|
|
|
|
|
|
|
|
/* initialize levels, etc. */
|
|
|
|
newlist->current_level = 0;
|
|
|
|
newlist->max_levels = max_levels;
|
|
|
|
newlist->level_probability = level_probability;
|
|
|
|
newlist->allow_duplicates = allow_duplicates;
|
|
|
|
newlist->append_duplicates = append_duplicates;
|
|
|
|
newlist->items = 0;
|
|
|
|
newlist->compare_function = compare_function;
|
|
|
|
|
|
|
|
/* initialize head node */
|
|
|
|
newlist->head = skiplist_new_node(newlist, max_levels);
|
|
|
|
}
|
|
|
|
|
|
|
|
return newlist;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-19 23:37:19 +02:00
|
|
|
static int skiplist_random_level(skiplist *list) {
|
|
|
|
int level = 0;
|
|
|
|
float r = 0.0;
|
|
|
|
|
|
|
|
if(list == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for(level = 0; level < list->max_levels; level++) {
|
|
|
|
r = ((float)rand() / (float)RAND_MAX);
|
|
|
|
if(r > list->level_probability)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (level >= list->max_levels) ? list->max_levels - 1 : level;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-19 22:22:40 +02:00
|
|
|
int skiplist_insert(skiplist *list, void *data) {
|
|
|
|
skiplistnode **update = NULL;
|
|
|
|
skiplistnode *thisnode = NULL;
|
|
|
|
skiplistnode *nextnode = NULL;
|
|
|
|
skiplistnode *newnode = NULL;
|
|
|
|
int level = 0;
|
|
|
|
int x = 0;
|
|
|
|
|
|
|
|
if(list == NULL || data == NULL) {
|
|
|
|
return SKIPLIST_ERROR_ARGS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check to make sure we don't have duplicates */
|
|
|
|
/* NOTE: this could made be more efficient */
|
|
|
|
if(list->allow_duplicates == FALSE) {
|
|
|
|
if(skiplist_find_first(list, data, NULL))
|
|
|
|
return SKIPLIST_ERROR_DUPLICATE;
|
|
|
|
}
|
|
|
|
|
2017-05-19 23:37:19 +02:00
|
|
|
/* initialize update vector */
|
|
|
|
if((update = (skiplistnode **)malloc(sizeof(skiplistnode *) * list->max_levels)) == NULL) {
|
|
|
|
return SKIPLIST_ERROR_MEMORY;
|
|
|
|
}
|
|
|
|
for(x = 0; x < list->max_levels; x++)
|
|
|
|
update[x] = NULL;
|
|
|
|
|
2017-05-19 22:22:40 +02:00
|
|
|
/* find proper position for insert, remember pointers with an update vector */
|
|
|
|
thisnode = list->head;
|
|
|
|
for(level = list->current_level; level >= 0; level--) {
|
|
|
|
|
|
|
|
while((nextnode = thisnode->forward[level])) {
|
|
|
|
if(list->append_duplicates == TRUE) {
|
|
|
|
if(list->compare_function(nextnode->data, data) > 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if(list->compare_function(nextnode->data, data) >= 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
thisnode = nextnode;
|
|
|
|
}
|
|
|
|
|
|
|
|
update[level] = thisnode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get a random level the new node should be inserted at */
|
|
|
|
level = skiplist_random_level(list);
|
|
|
|
|
|
|
|
/* we're adding a new level... */
|
|
|
|
if(level > list->current_level) {
|
|
|
|
/*printf("NEW LEVEL!\n");*/
|
|
|
|
list->current_level++;
|
|
|
|
level = list->current_level;
|
|
|
|
update[level] = list->head;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* create a new node */
|
|
|
|
if((newnode = skiplist_new_node(list, level)) == NULL) {
|
|
|
|
/*printf("NODE ERROR\n");*/
|
|
|
|
free(update);
|
|
|
|
return SKIPLIST_ERROR_MEMORY;
|
|
|
|
}
|
|
|
|
newnode->data = data;
|
|
|
|
|
|
|
|
/* update pointers to insert node at proper location */
|
|
|
|
do {
|
|
|
|
thisnode = update[level];
|
|
|
|
newnode->forward[level] = thisnode->forward[level];
|
|
|
|
thisnode->forward[level] = newnode;
|
|
|
|
|
|
|
|
}
|
|
|
|
while(--level >= 0);
|
|
|
|
|
|
|
|
/* update counters */
|
|
|
|
list->items++;
|
|
|
|
|
|
|
|
/* free memory */
|
|
|
|
free(update);
|
|
|
|
|
|
|
|
return SKIPLIST_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int skiplist_empty(skiplist *list) {
|
|
|
|
skiplistnode *this = NULL;
|
|
|
|
skiplistnode *next = NULL;
|
|
|
|
int level = 0;
|
|
|
|
|
|
|
|
if(list == NULL)
|
|
|
|
return ERROR;
|
|
|
|
|
|
|
|
/* free all list nodes (but not header) */
|
|
|
|
for(this = list->head->forward[0]; this != NULL; this = next) {
|
|
|
|
next = this->forward[0];
|
|
|
|
free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reset level pointers */
|
|
|
|
for(level = list->current_level; level >= 0; level--)
|
|
|
|
list->head->forward[level] = NULL;
|
|
|
|
|
|
|
|
/* reset list level */
|
|
|
|
list->current_level = 0;
|
|
|
|
|
|
|
|
/* reset items */
|
|
|
|
list->items = 0;
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int skiplist_free(skiplist **list) {
|
|
|
|
skiplistnode *this = NULL;
|
|
|
|
skiplistnode *next = NULL;
|
|
|
|
|
|
|
|
if(list == NULL)
|
|
|
|
return ERROR;
|
|
|
|
if(*list == NULL)
|
|
|
|
return OK;
|
|
|
|
|
|
|
|
/* free header and all list nodes */
|
|
|
|
for(this = (*list)->head; this != NULL; this = next) {
|
|
|
|
next = this->forward[0];
|
|
|
|
free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free list structure */
|
|
|
|
free(*list);
|
|
|
|
*list = NULL;
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* get first item in list */
|
|
|
|
void *skiplist_peek(skiplist *list) {
|
|
|
|
|
|
|
|
if(list == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* return first item */
|
|
|
|
return list->head->forward[0]->data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* get/remove first item in list */
|
|
|
|
void *skiplist_pop(skiplist *list) {
|
|
|
|
skiplistnode *thisnode = NULL;
|
|
|
|
void *data = NULL;
|
|
|
|
int level = 0;
|
|
|
|
|
|
|
|
if(list == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* get first item */
|
|
|
|
thisnode = list->head->forward[0];
|
|
|
|
if(thisnode == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* get data for first item */
|
|
|
|
data = thisnode->data;
|
|
|
|
|
|
|
|
/* remove first item from queue - update forward links from head to first node */
|
|
|
|
for(level = 0; level <= list->current_level; level++) {
|
|
|
|
if(list->head->forward[level] == thisnode)
|
|
|
|
list->head->forward[level] = thisnode->forward[level];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free deleted node */
|
|
|
|
free(thisnode);
|
|
|
|
|
|
|
|
/* adjust items */
|
|
|
|
list->items--;
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* get first item in list */
|
|
|
|
void *skiplist_get_first(skiplist *list, void **node_ptr) {
|
|
|
|
skiplistnode *thisnode = NULL;
|
|
|
|
|
|
|
|
if(list == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* get first node */
|
|
|
|
thisnode = list->head->forward[0];
|
|
|
|
|
|
|
|
/* return pointer to node */
|
|
|
|
if(node_ptr)
|
|
|
|
*node_ptr = (void *)thisnode;
|
|
|
|
|
|
|
|
if(thisnode)
|
|
|
|
return thisnode->data;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* get next item in list */
|
|
|
|
void *skiplist_get_next(void **node_ptr) {
|
|
|
|
skiplistnode *thisnode = NULL;
|
|
|
|
skiplistnode *nextnode = NULL;
|
|
|
|
|
|
|
|
if(node_ptr == NULL || *node_ptr == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
thisnode = (skiplistnode *)(*node_ptr);
|
|
|
|
nextnode = thisnode->forward[0];
|
|
|
|
|
|
|
|
*node_ptr = (void *)nextnode;
|
|
|
|
|
|
|
|
if(nextnode)
|
|
|
|
return nextnode->data;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* first first item in list */
|
|
|
|
void *skiplist_find_first(skiplist *list, void *data, void **node_ptr) {
|
|
|
|
skiplistnode *thisnode = NULL;
|
|
|
|
skiplistnode *nextnode = NULL;
|
|
|
|
int level = 0;
|
|
|
|
|
|
|
|
if(list == NULL || data == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
thisnode = list->head;
|
|
|
|
for(level = list->current_level; level >= 0; level--) {
|
|
|
|
while((nextnode = thisnode->forward[level])) {
|
|
|
|
if(list->compare_function(nextnode->data, data) >= 0)
|
|
|
|
break;
|
|
|
|
thisnode = nextnode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we found it! */
|
|
|
|
if(nextnode && list->compare_function(nextnode->data, data) == 0) {
|
|
|
|
if(node_ptr)
|
|
|
|
*node_ptr = (void *)nextnode;
|
|
|
|
return nextnode->data;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if(node_ptr)
|
|
|
|
*node_ptr = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* find next match */
|
|
|
|
void *skiplist_find_next(skiplist *list, void *data, void **node_ptr) {
|
|
|
|
skiplistnode *thisnode = NULL;
|
|
|
|
skiplistnode *nextnode = NULL;
|
|
|
|
|
|
|
|
if(list == NULL || data == NULL || node_ptr == NULL)
|
|
|
|
return NULL;
|
|
|
|
if(*node_ptr == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
thisnode = (skiplistnode *)(*node_ptr);
|
|
|
|
nextnode = thisnode->forward[0];
|
|
|
|
|
|
|
|
if(nextnode) {
|
|
|
|
if(list->compare_function(nextnode->data, data) == 0) {
|
|
|
|
*node_ptr = (void *)nextnode;
|
|
|
|
return nextnode->data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*node_ptr = NULL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* delete first matching item from list */
|
|
|
|
int skiplist_delete_first(skiplist *list, void *data) {
|
|
|
|
skiplistnode **update = NULL;
|
|
|
|
skiplistnode *thisnode = NULL;
|
|
|
|
skiplistnode *nextnode = NULL;
|
|
|
|
int level = 0;
|
|
|
|
int top_level = 0;
|
|
|
|
int deleted = FALSE;
|
|
|
|
int x = 0;
|
|
|
|
|
|
|
|
if(list == NULL || data == NULL)
|
|
|
|
return ERROR;
|
|
|
|
|
|
|
|
/* initialize update vector */
|
|
|
|
if((update = (skiplistnode **)malloc(sizeof(skiplistnode *) * list->max_levels)) == NULL)
|
|
|
|
return ERROR;
|
|
|
|
for(x = 0; x < list->max_levels; x++)
|
|
|
|
update[x] = NULL;
|
|
|
|
|
|
|
|
/* find location in list */
|
|
|
|
thisnode = list->head;
|
|
|
|
for(top_level = level = list->current_level; level >= 0; level--) {
|
|
|
|
while((nextnode = thisnode->forward[level])) {
|
|
|
|
if(list->compare_function(nextnode->data, data) >= 0)
|
|
|
|
break;
|
|
|
|
thisnode = nextnode;
|
|
|
|
}
|
|
|
|
update[level] = thisnode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we found a match! */
|
|
|
|
if(list->compare_function(nextnode->data, data) == 0) {
|
|
|
|
|
|
|
|
/* adjust level pointers to bypass (soon to be) removed node */
|
|
|
|
for(level = 0; level <= top_level; level++) {
|
|
|
|
|
|
|
|
thisnode = update[level];
|
|
|
|
if(thisnode->forward[level] != nextnode)
|
|
|
|
break;
|
|
|
|
|
|
|
|
thisnode->forward[level] = nextnode->forward[level];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free node memory */
|
|
|
|
free(nextnode);
|
|
|
|
|
|
|
|
/* adjust top/current level of list is necessary */
|
|
|
|
while(list->head->forward[top_level] == NULL && top_level > 0)
|
|
|
|
top_level--;
|
|
|
|
list->current_level = top_level;
|
|
|
|
|
|
|
|
/* adjust items */
|
|
|
|
list->items--;
|
|
|
|
|
|
|
|
deleted = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free memory */
|
|
|
|
free(update);
|
|
|
|
|
|
|
|
return deleted;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* delete all matching items from list */
|
2017-05-19 23:37:19 +02:00
|
|
|
int skiplist_delete(skiplist *list, void *data) {
|
2017-05-19 22:22:40 +02:00
|
|
|
int deleted = 0;
|
|
|
|
int total_deleted = 0;
|
|
|
|
|
|
|
|
/* NOTE: there is a more efficient way to do this... */
|
|
|
|
while((deleted = skiplist_delete_first(list, data)) == 1)
|
|
|
|
total_deleted++;
|
|
|
|
|
|
|
|
return total_deleted;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* delete specific node from list */
|
|
|
|
int skiplist_delete_node(skiplist *list, void *node_ptr) {
|
|
|
|
void *data = NULL;
|
|
|
|
skiplistnode **update = NULL;
|
|
|
|
skiplistnode *thenode = NULL;
|
|
|
|
skiplistnode *thisnode = NULL;
|
|
|
|
skiplistnode *nextnode = NULL;
|
|
|
|
int level = 0;
|
|
|
|
int top_level = 0;
|
|
|
|
int deleted = FALSE;
|
|
|
|
int x = 0;
|
|
|
|
|
|
|
|
if(list == NULL || node_ptr == NULL)
|
|
|
|
return ERROR;
|
|
|
|
|
|
|
|
/* we'll need the data from the node to first find the node */
|
|
|
|
thenode = (skiplistnode *)node_ptr;
|
|
|
|
data = thenode->data;
|
|
|
|
|
|
|
|
/* initialize update vector */
|
|
|
|
if((update = (skiplistnode **)malloc(sizeof(skiplistnode *) * list->max_levels)) == NULL)
|
|
|
|
return ERROR;
|
|
|
|
for(x = 0; x < list->max_levels; x++)
|
|
|
|
update[x] = NULL;
|
|
|
|
|
|
|
|
/* find location in list */
|
|
|
|
thisnode = list->head;
|
|
|
|
for(top_level = level = list->current_level; level >= 0; level--) {
|
|
|
|
while((nextnode = thisnode->forward[level])) {
|
|
|
|
|
|
|
|
/* next node would be too far */
|
|
|
|
if(list->compare_function(nextnode->data, data) > 0)
|
|
|
|
break;
|
|
|
|
/* this is the exact node we want */
|
|
|
|
if(list->compare_function(nextnode->data, data) == 0 && nextnode == thenode)
|
|
|
|
break;
|
|
|
|
|
|
|
|
thisnode = nextnode;
|
|
|
|
}
|
|
|
|
update[level] = thisnode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we found a match! (value + pointers match) */
|
|
|
|
if(nextnode && list->compare_function(nextnode->data, data) == 0 && nextnode == thenode) {
|
|
|
|
|
|
|
|
/* adjust level pointers to bypass (soon to be) removed node */
|
|
|
|
for(level = 0; level <= top_level; level++) {
|
|
|
|
|
|
|
|
thisnode = update[level];
|
|
|
|
if(thisnode->forward[level] != nextnode)
|
|
|
|
break;
|
|
|
|
|
|
|
|
thisnode->forward[level] = nextnode->forward[level];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free node memory */
|
|
|
|
free(nextnode);
|
|
|
|
|
|
|
|
/* adjust top/current level of list is necessary */
|
|
|
|
while(list->head->forward[top_level] == NULL && top_level > 0)
|
|
|
|
top_level--;
|
|
|
|
list->current_level = top_level;
|
|
|
|
|
|
|
|
/* adjust items */
|
|
|
|
list->items--;
|
|
|
|
|
|
|
|
deleted = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free memory */
|
|
|
|
free(update);
|
|
|
|
|
|
|
|
return deleted;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|