radare2/libr/util/skiplist.c
pancake 2ba1e80323 Use R_LIKELY and r_return in the skiplist api ##util
* NULL data is still supported
2022-07-20 16:52:24 +02:00

317 lines
8.4 KiB
C

// (c) 2016-2022 Jeffrey Crowell, Riccardo Schirone(ret2libc), pancake
// BSD 3 Clause License
// radare2
// 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 <r_skiplist.h>
#include <r_util/r_assert.h>
#define SKIPLIST_MAX_DEPTH 31
static RSkipListNode *r_skiplist_node_new(void *data, int level) {
RSkipListNode *res = R_NEW0 (RSkipListNode);
if (R_LIKELY (res)) {
res->forward = R_NEWS0 (RSkipListNode *, level + 1);
if (R_UNLIKELY (!res->forward)) {
free (res);
return NULL;
}
res->data = data;
}
return res;
}
static void r_skiplist_node_free(RSkipList *list, RSkipListNode *node) {
if (node) {
if (list->freefn && node->data) {
list->freefn (node->data);
}
free (node->forward);
free (node);
}
}
static inline void init_head(RSkipListNode *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 RSkipListNode *find_insertpoint(RSkipList *list, void *data, RSkipListNode **updates, bool by_data) {
RSkipListNode *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) < 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(RSkipList* list, void* data, bool by_data) {
int i;
RSkipListNode *update[SKIPLIST_MAX_DEPTH + 1];
// locate delete points in the lists of all levels
RSkipListNode *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) != 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];
}
r_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.
R_API RSkipList* r_skiplist_new(RListFree freefn, RListComparator comparefn) {
RSkipList *list = R_NEW0 (RSkipList);
if (R_LIKELY (list)) {
list->head = r_skiplist_node_new (NULL, SKIPLIST_MAX_DEPTH);
if (R_UNLIKELY (!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
R_API void r_skiplist_purge(RSkipList *list) {
r_return_if_fail (list);
RSkipListNode *n = list->head->forward[0];
while (n != list->head) {
RSkipListNode *x = n;
n = n->forward[0];
r_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)
R_API void r_skiplist_free(RSkipList *list) {
if (list) {
r_skiplist_purge (list);
r_skiplist_node_free (list, list->head);
free (list);
}
}
// Inserts an element to the skiplist, and returns a pointer to the element's node.
R_API RSkipListNode* r_skiplist_insert(RSkipList* list, void* data) {
r_return_val_if_fail (list, NULL);
RSkipListNode *update[SKIPLIST_MAX_DEPTH + 1];
int i, x_level, new_level;
// locate insertion points in the lists of all levels
RSkipListNode *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)) {
return x;
}
// randomly choose the number of levels the new node will be put in
const ut32 ptr = (ut32)((size_t)data);
ut32 rand_res = (ptr % 251) ^ (ptr % 65521) ^ (ptr % 4294967291) ^ ptr;
for (x_level = 0; (rand_res & 1) && (x_level < SKIPLIST_MAX_DEPTH); x_level++) {
rand_res >>= 1;
}
// 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 = r_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;
}
R_API bool r_skiplist_insert_autofree(RSkipList* list, void* data) {
r_return_val_if_fail (list, false);
RSkipListNode* node = r_skiplist_insert (list, data);
if (node && data != node->data) { // duplicate
if (list->freefn) {
list->freefn (data);
}
return false;
}
return true;
}
// Delete node with data as it's payload.
R_API bool r_skiplist_delete(RSkipList* list, void* data) {
r_return_val_if_fail (list, false);
return delete_element (list, data, true);
}
// Delete the given RSkipListNode from the skiplist
R_API bool r_skiplist_delete_node(RSkipList *list, RSkipListNode *node) {
r_return_val_if_fail (list, false);
return delete_element (list, node, false);
}
R_API RSkipListNode* r_skiplist_find(RSkipList* list, void* data) {
r_return_val_if_fail (list, NULL);
RSkipListNode* x = find_insertpoint (list, data, NULL, true);
if (x != list->head && list->compare (x->data, data) == 0) {
return x;
}
return NULL;
}
R_API RSkipListNode* r_skiplist_find_geq(RSkipList* list, void* data) {
r_return_val_if_fail (list, NULL);
RSkipListNode* x = find_insertpoint (list, data, NULL, true);
return x != list->head ? x : NULL;
}
R_API RSkipListNode* r_skiplist_find_leq(RSkipList* list, void* data) {
r_return_val_if_fail (list, NULL);
RSkipListNode *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) <= 0) {
x = x->forward[i];
}
}
return x != list->head ? x : NULL;
}
// Move all the elements of `l2` in `l1`.
R_API void r_skiplist_join(RSkipList *l1, RSkipList *l2) {
r_return_if_fail (l1 && l2);
RSkipListNode *it;
void *data;
r_skiplist_foreach (l2, it, data) {
r_skiplist_insert (l1, data);
}
r_skiplist_purge (l2);
}
// Returns the first data element in the list, if present, NULL otherwise
R_API void *r_skiplist_get_first(RSkipList *list) {
r_return_val_if_fail (list, NULL);
RSkipListNode *res = list->head->forward[0];
return res == list->head ? NULL : res->data;
}
// Returns the nth data element in the list, if present, NULL otherwise
R_API void *r_skiplist_get_n(RSkipList *list, int n) {
r_return_val_if_fail (list && n >= 0, NULL);
int count = 0;
RSkipListNode *node;
void *data;
r_skiplist_foreach (list, node, data) {
if (count == n) {
return data;
}
++count;
}
return NULL;
}
R_API void* r_skiplist_get_geq(RSkipList* list, void* data) {
RSkipListNode *x = r_skiplist_find_geq (list, data);
return x ? x->data : NULL;
}
R_API void* r_skiplist_get_leq(RSkipList* list, void* data) {
RSkipListNode *x = r_skiplist_find_leq (list, data);
return x ? x->data : NULL;
}
// Return true if the list is empty
R_API bool r_skiplist_empty(RSkipList *list) {
r_return_val_if_fail (list, false);
return list->size == 0;
}
// Return a new allocated RList 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.
R_API RList *r_skiplist_to_list(RSkipList *list) {
r_return_val_if_fail (list, NULL);
RList *res = r_list_new ();
if (R_UNLIKELY (!res)) {
return NULL;
}
RSkipListNode *n;
void *data;
r_skiplist_foreach (list, n, data) {
r_list_append (res, data);
}
return res;
}