rizin/librz/util/skiplist.c
2024-02-11 13:28:33 +08:00

302 lines
7.7 KiB
C

// SPDX-FileCopyrightText: 2016 Jeffrey Crowell
// SPDX-FileCopyrightText: 2016 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
// Skiplists are a probabilistic datastructure than can be used as a k-v store
// with average case O(lg n) lookup time, and worst case O(n).
// https://en.wikipedia.org/wiki/Skip_list
#include <rz_skiplist.h>
#define SKIPLIST_MAX_DEPTH 31
static RzSkipListNode *rz_skiplist_node_new(void *data, int level) {
RzSkipListNode *res = RZ_NEW0(RzSkipListNode);
if (!res) {
return NULL;
}
res->forward = RZ_NEWS0(RzSkipListNode *, level + 1);
if (!res->forward) {
free(res);
return NULL;
}
res->data = data;
return res;
}
static void rz_skiplist_node_free(RzSkipList *list, RzSkipListNode *node) {
if (node) {
if (list->freefn && node->data) {
list->freefn(node->data);
}
free(node->forward);
free(node);
}
}
static void init_head(RzSkipListNode *head) {
int i;
for (i = 0; i <= SKIPLIST_MAX_DEPTH; i++) {
head->forward[i] = head;
}
}
// Find the insertion/deletion point for the element `data` in the list.
// The array `updates`, if provided, is filled with the nodes that need to be
// updated for each layer.
//
// NOTE: `updates` should be big enough to contain `list->list_level + 1`
// elements, when provided.
static RzSkipListNode *find_insertpoint(RzSkipList *list, void *data, RzSkipListNode **updates, bool by_data) {
RzSkipListNode *x = list->head;
int i;
for (i = list->list_level; i >= 0; i--) {
if (by_data) {
while (x->forward[i] != list->head && list->compare(x->forward[i]->data, data, NULL) < 0) {
x = x->forward[i];
}
} else {
while (x->forward[i] != list->head && x->forward[i] != data) {
x = x->forward[i];
}
}
if (updates) {
updates[i] = x;
}
}
x = x->forward[0];
return x;
}
static bool delete_element(RzSkipList *list, void *data, bool by_data) {
int i;
RzSkipListNode *update[SKIPLIST_MAX_DEPTH + 1], *x;
// locate delete points in the lists of all levels
x = find_insertpoint(list, data, update, by_data);
// do nothing if the element is not present in the list
if (x == list->head || list->compare(x->data, data, NULL) != 0) {
return false;
}
// update forward links for all `update` points,
// by removing the element from the list in each level
for (i = 0; i <= list->list_level; i++) {
if (update[i]->forward[i] != x) {
break;
}
update[i]->forward[i] = x->forward[i];
}
rz_skiplist_node_free(list, x);
// update the level of the list
while ((list->list_level > 0) &&
(list->head->forward[list->list_level] == list->head)) {
list->list_level--;
}
list->size--;
return true;
}
// Takes in a pointer to the function to free a list element, and a pointer to
// a function that returns 0 on equality between two elements, and -1 or 1
// when unequal (for sorting).
// Returns a new heap-allocated skiplist.
RZ_API RzSkipList *rz_skiplist_new(RzListFree freefn, RzListComparator comparefn) {
RzSkipList *list = RZ_NEW0(RzSkipList);
if (!list) {
return NULL;
}
list->head = rz_skiplist_node_new(NULL, SKIPLIST_MAX_DEPTH);
if (!list->head) {
free(list);
return NULL;
}
init_head(list->head);
list->list_level = 0;
list->size = 0;
list->freefn = freefn;
list->compare = comparefn;
return list;
}
// Remove all elements from the list
RZ_API void rz_skiplist_purge(RzSkipList *list) {
RzSkipListNode *n;
if (!list) {
return;
}
n = list->head->forward[0];
while (n != list->head) {
RzSkipListNode *x = n;
n = n->forward[0];
rz_skiplist_node_free(list, x);
}
init_head(list->head);
list->size = 0;
list->list_level = 0;
}
// Free the entire list and it's element (if freefn is specified)
RZ_API void rz_skiplist_free(RzSkipList *list) {
if (!list) {
return;
}
rz_skiplist_purge(list);
rz_skiplist_node_free(list, list->head);
free(list);
}
// Inserts an element to the skiplist, and returns a pointer to the element's
// node.
RZ_API RzSkipListNode *rz_skiplist_insert(RzSkipList *list, void *data) {
RzSkipListNode *update[SKIPLIST_MAX_DEPTH + 1];
RzSkipListNode *x;
int i, x_level, new_level;
// locate insertion points in the lists of all levels
x = find_insertpoint(list, data, update, true);
// check whether the element is already in the list
if (x != list->head && !list->compare(x->data, data, NULL)) {
return x;
}
// randomly choose the number of levels the new node will be put in
for (x_level = 0; rand() < RAND_MAX / 2 && x_level < SKIPLIST_MAX_DEPTH; x_level++) {
;
}
// update the `update` array with default values when the current node
// has a level greater than the current one
new_level = list->list_level;
if (x_level > list->list_level) {
for (i = list->list_level + 1; i <= x_level; i++) {
update[i] = list->head;
}
new_level = x_level;
}
x = rz_skiplist_node_new(data, x_level);
if (!x) {
return NULL;
}
// update forward links for all `update` points,
// by inserting the new element in the list in each level
for (i = 0; i <= x_level; i++) {
x->forward[i] = update[i]->forward[i];
update[i]->forward[i] = x;
}
list->list_level = new_level;
list->size++;
return x;
}
// Delete node with data as it's payload.
RZ_API bool rz_skiplist_delete(RzSkipList *list, void *data) {
return delete_element(list, data, true);
}
// Delete the given RzSkipListNode from the skiplist
RZ_API bool rz_skiplist_delete_node(RzSkipList *list, RzSkipListNode *node) {
return delete_element(list, node, false);
}
RZ_API RzSkipListNode *rz_skiplist_find(RzSkipList *list, void *data) {
RzSkipListNode *x = find_insertpoint(list, data, NULL, true);
if (x != list->head && list->compare(x->data, data, NULL) == 0) {
return x;
}
return NULL;
}
RZ_API RzSkipListNode *rz_skiplist_find_geq(RzSkipList *list, void *data) {
RzSkipListNode *x = find_insertpoint(list, data, NULL, true);
return x != list->head ? x : NULL;
}
RZ_API RzSkipListNode *rz_skiplist_find_leq(RzSkipList *list, void *data) {
RzSkipListNode *x = list->head;
int i;
for (i = list->list_level; i >= 0; i--) {
while (x->forward[i] != list->head && list->compare(x->forward[i]->data, data, NULL) <= 0) {
x = x->forward[i];
}
}
return x != list->head ? x : NULL;
}
// Move all the elements of `l2` in `l1`.
RZ_API void rz_skiplist_join(RzSkipList *l1, RzSkipList *l2) {
RzSkipListNode *it;
void *data;
rz_skiplist_foreach (l2, it, data) {
(void)rz_skiplist_insert(l1, data);
}
rz_skiplist_purge(l2);
}
// Returns the first data element in the list, if present, NULL otherwise
RZ_API void *rz_skiplist_get_first(RzSkipList *list) {
if (!list) {
return NULL;
}
RzSkipListNode *res = list->head->forward[0];
return res == list->head ? NULL : res->data;
}
// Returns the nth data element in the list, if present, NULL otherwise
RZ_API void *rz_skiplist_get_n(RzSkipList *list, int n) {
int count = 0;
RzSkipListNode *node;
void *data;
if (!list || n < 0) {
return NULL;
}
rz_skiplist_foreach (list, node, data) {
if (count == n) {
return data;
}
++count;
}
return NULL;
}
RZ_API void *rz_skiplist_get_geq(RzSkipList *list, void *data) {
RzSkipListNode *x = rz_skiplist_find_geq(list, data);
return x ? x->data : NULL;
}
RZ_API void *rz_skiplist_get_leq(RzSkipList *list, void *data) {
RzSkipListNode *x = rz_skiplist_find_leq(list, data);
return x ? x->data : NULL;
}
// Return true if the list is empty
RZ_API bool rz_skiplist_empty(RzSkipList *list) {
return list->size == 0;
}
// Return a new allocated RzList representing the given `list`
//
// NOTE: the data will be shared between the two lists. The user of this
// function should choose which list will "own" the data pointers.
RZ_API RzList *rz_skiplist_to_list(RzSkipList *list) {
RzList *res = rz_list_new();
RzSkipListNode *n;
void *data;
rz_skiplist_foreach (list, n, data) {
rz_list_append(res, data);
}
return res;
}