forked from Minki/linux
ca2270292e
At the cost of an extra pointer, we can avoid the O(logN) cost of finding the first element in the tree (smallest node), which is something required for any of the strlist or intlist traversals (XXX_for_each_entry()). There are a number of users in perf of these (particularly strlists), including probes, and buildid. Signed-off-by: Davidlohr Bueso <dbueso@suse.de> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Namhyung Kim <namhyung@kernel.org> Link: http://lkml.kernel.org/r/20181206191819.30182-5-dave@stgolabs.net Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
142 lines
2.6 KiB
C
142 lines
2.6 KiB
C
/*
|
|
* Based on strlist.c by:
|
|
* (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
|
|
*
|
|
* Licensed under the GPLv2.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "rblist.h"
|
|
|
|
int rblist__add_node(struct rblist *rblist, const void *new_entry)
|
|
{
|
|
struct rb_node **p = &rblist->entries.rb_root.rb_node;
|
|
struct rb_node *parent = NULL, *new_node;
|
|
bool leftmost = true;
|
|
|
|
while (*p != NULL) {
|
|
int rc;
|
|
|
|
parent = *p;
|
|
|
|
rc = rblist->node_cmp(parent, new_entry);
|
|
if (rc > 0)
|
|
p = &(*p)->rb_left;
|
|
else if (rc < 0) {
|
|
p = &(*p)->rb_right;
|
|
leftmost = false;
|
|
}
|
|
else
|
|
return -EEXIST;
|
|
}
|
|
|
|
new_node = rblist->node_new(rblist, new_entry);
|
|
if (new_node == NULL)
|
|
return -ENOMEM;
|
|
|
|
rb_link_node(new_node, parent, p);
|
|
rb_insert_color_cached(new_node, &rblist->entries, leftmost);
|
|
++rblist->nr_entries;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node)
|
|
{
|
|
rb_erase_cached(rb_node, &rblist->entries);
|
|
--rblist->nr_entries;
|
|
rblist->node_delete(rblist, rb_node);
|
|
}
|
|
|
|
static struct rb_node *__rblist__findnew(struct rblist *rblist,
|
|
const void *entry,
|
|
bool create)
|
|
{
|
|
struct rb_node **p = &rblist->entries.rb_root.rb_node;
|
|
struct rb_node *parent = NULL, *new_node = NULL;
|
|
bool leftmost = true;
|
|
|
|
while (*p != NULL) {
|
|
int rc;
|
|
|
|
parent = *p;
|
|
|
|
rc = rblist->node_cmp(parent, entry);
|
|
if (rc > 0)
|
|
p = &(*p)->rb_left;
|
|
else if (rc < 0) {
|
|
p = &(*p)->rb_right;
|
|
leftmost = false;
|
|
}
|
|
else
|
|
return parent;
|
|
}
|
|
|
|
if (create) {
|
|
new_node = rblist->node_new(rblist, entry);
|
|
if (new_node) {
|
|
rb_link_node(new_node, parent, p);
|
|
rb_insert_color_cached(new_node,
|
|
&rblist->entries, leftmost);
|
|
++rblist->nr_entries;
|
|
}
|
|
}
|
|
|
|
return new_node;
|
|
}
|
|
|
|
struct rb_node *rblist__find(struct rblist *rblist, const void *entry)
|
|
{
|
|
return __rblist__findnew(rblist, entry, false);
|
|
}
|
|
|
|
struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry)
|
|
{
|
|
return __rblist__findnew(rblist, entry, true);
|
|
}
|
|
|
|
void rblist__init(struct rblist *rblist)
|
|
{
|
|
if (rblist != NULL) {
|
|
rblist->entries = RB_ROOT_CACHED;
|
|
rblist->nr_entries = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void rblist__exit(struct rblist *rblist)
|
|
{
|
|
struct rb_node *pos, *next = rb_first_cached(&rblist->entries);
|
|
|
|
while (next) {
|
|
pos = next;
|
|
next = rb_next(pos);
|
|
rblist__remove_node(rblist, pos);
|
|
}
|
|
}
|
|
|
|
void rblist__delete(struct rblist *rblist)
|
|
{
|
|
if (rblist != NULL) {
|
|
rblist__exit(rblist);
|
|
free(rblist);
|
|
}
|
|
}
|
|
|
|
struct rb_node *rblist__entry(const struct rblist *rblist, unsigned int idx)
|
|
{
|
|
struct rb_node *node;
|
|
|
|
for (node = rb_first_cached(&rblist->entries); node;
|
|
node = rb_next(node)) {
|
|
if (!idx--)
|
|
return node;
|
|
}
|
|
|
|
return NULL;
|
|
}
|