[ccan] [PATCH] btree: Add custom allocator interface.
stuartl at longlandclan.id.au
stuartl at longlandclan.id.au
Sun Feb 7 11:10:39 AEDT 2016
From: Stuart Longland <me at vk4msl.id.au>
This provides a way for btree to be used with external allocator
libraries such as the tal or talloc modules.
---
ccan/btree/btree.c | 194 +++++++++++++++++++++++++++++++----------------------
ccan/btree/btree.h | 29 +++++---
2 files changed, 136 insertions(+), 87 deletions(-)
diff --git a/ccan/btree/btree.c b/ccan/btree/btree.c
index 636edbc..5bb2435 100644
--- a/ccan/btree/btree.c
+++ b/ccan/btree/btree.c
@@ -29,12 +29,15 @@
#define MAX (BTREE_ITEM_MAX)
#define MIN (BTREE_ITEM_MAX >> 1)
-static struct btree_node *node_alloc(int internal);
+static struct btree_node *node_alloc(const struct btree_allocator* alloc, int internal);
static void node_delete(struct btree_node *node, struct btree *btree);
+static void node_free(struct btree_node *node);
static void branch_begin(btree_iterator iter);
static void branch_end(btree_iterator iter);
static void begin_end_lr(btree_iterator iter, struct btree_node *node, int lr);
+static void* default_malloc(const struct btree_allocator* alloc, size_t size);
+static void default_free(const struct btree_allocator* alloc, void* ptr);
/*
* If iter->node has parent, returns 1 and ascends the iterator such that
@@ -65,11 +68,19 @@ static int node_walk_forward(const struct btree_node *node,
struct btree *btree_new(btree_search_t search)
{
- struct btree *btree = calloc(1, sizeof(struct btree));
- struct btree_node *node = node_alloc(0);
+ return btree_new_with_alloc(search, &BTREE_DEFAULT_ALLOCATOR);
+}
+
+struct btree *btree_new_with_alloc(btree_search_t search,
+ const struct btree_allocator *alloc)
+{
+ struct btree *btree = (*(alloc->malloc))(alloc, sizeof(struct btree));
+ struct btree_node *node = node_alloc(alloc, 0);
node->parent = NULL;
node->count = 0;
node->depth = 0;
+ memset(btree, 0, sizeof(struct btree));
+ btree->alloc = alloc;
btree->root = node;
btree->search = search;
btree->multi = false;
@@ -79,16 +90,16 @@ struct btree *btree_new(btree_search_t search)
void btree_delete(struct btree *btree)
{
node_delete(btree->root, btree);
- free(btree);
+ (*(btree->alloc->free))(btree->alloc, btree);
}
bool btree_insert(struct btree *btree, const void *item)
{
btree_iterator iter;
-
+
if (btree_find_last(btree, item, iter) && !btree->multi)
return false;
-
+
btree_insert_at(iter, item);
return true;
}
@@ -98,41 +109,41 @@ bool btree_remove(struct btree *btree, const void *key)
btree_iterator iter;
bool success = false;
bool multi = btree->multi;
-
+
do {
if (btree_find_first(btree, key, iter)) {
btree_remove_at(iter);
success = true;
}
} while (multi);
-
+
return success;
}
void *btree_lookup(struct btree *btree, const void *key)
{
btree_iterator iter;
-
+
if (btree_find_first(btree, key, iter))
return iter->item;
-
+
return NULL;
}
int btree_begin_end_lr(const struct btree *btree, btree_iterator iter, int lr)
{
struct btree_node *node;
-
+
iter->btree = (struct btree *)btree;
begin_end_lr(iter, btree->root, lr);
-
+
/* Set iter->item if any items exist. */
node = iter->node;
if (node->count) {
iter->item = (void*)node->item[iter->k - lr];
return 1;
}
-
+
return 0;
}
@@ -147,7 +158,7 @@ int btree_deref(btree_iterator iter)
}
} while (iter->k >= iter->node->count);
}
-
+
iter->item = (void*)iter->node->item[iter->k];
return 1;
}
@@ -165,7 +176,7 @@ int btree_prev(btree_iterator iter)
}
} while (iter->k == 0);
}
-
+
iter->item = (void*)iter->node->item[--iter->k];
return 1;
}
@@ -188,28 +199,28 @@ int btree_find_lr(const struct btree *btree, const void *key,
unsigned int k;
unsigned int depth;
int found = 0;
-
+
iter->btree = (struct btree *)btree;
iter->item = NULL;
-
+
depth = node->depth;
for (;;) {
int f = 0;
k = btree->search(key, node->item, node->count, lr, &f);
-
+
if (f) {
iter->item = (void*)node->item[k - lr];
found = 1;
}
if (!depth--)
break;
-
+
node = node->branch[k];
}
-
+
iter->node = node;
iter->k = k;
-
+
return found;
}
@@ -231,10 +242,10 @@ void btree_insert_at(btree_iterator iter, const void *item)
struct btree_node *xr = NULL;
struct btree_node *p;
struct btree *btree = iter->btree;
-
+
/* btree_insert_at always sets iter->item to item. */
iter->item = (void*)item;
-
+
/*
* If node is not a leaf, fall to the end of the left branch of item[k]
* so that it will be a leaf. This does not modify the iterator's logical
@@ -242,7 +253,7 @@ void btree_insert_at(btree_iterator iter, const void *item)
*/
if (iter->node->depth)
branch_end(iter);
-
+
/*
* First try inserting item into this node.
* If it's too big, split it, and repeat by
@@ -254,23 +265,23 @@ void btree_insert_at(btree_iterator iter, const void *item)
} else {
for (;;) {
node_split(&x, &xr, iter->node, iter->k);
-
+
if (!ascend(iter))
break;
-
+
if (iter->node->count < MAX) {
node_insert(x, xr, iter->node, iter->k);
goto finished;
}
}
-
+
/*
* If splitting came all the way up to the root, create a new root whose
* left branch is the current root, median is x, and right branch is the
* half split off from the root.
*/
assert(iter->node == btree->root);
- p = node_alloc(1);
+ p = node_alloc(btree->alloc, 1);
p->parent = NULL;
p->count = 1;
p->depth = btree->root->depth + 1;
@@ -283,7 +294,7 @@ void btree_insert_at(btree_iterator iter, const void *item)
xr->k = 1;
btree->root = p;
}
-
+
finished:
btree->count++;
iter->node = NULL;
@@ -293,10 +304,10 @@ int btree_remove_at(btree_iterator iter)
{
struct btree *btree = iter->btree;
struct btree_node *root;
-
+
if (!btree_deref(iter))
return 0;
-
+
if (!iter->node->depth) {
node_remove_leaf_item(iter->node, iter->k);
if (iter->node->count >= MIN || !iter->node->parent)
@@ -307,21 +318,21 @@ int btree_remove_at(btree_iterator iter)
* with its successor (which will always be in a leaf), then remove
* the original copy of the successor.
*/
-
+
/* Save pointer to condemned item. */
const void **x = &iter->node->item[iter->k];
-
+
/* Descend to successor. */
iter->k++;
branch_begin(iter);
-
+
/* Replace condemned item with successor. */
*x = iter->node->item[0];
-
+
/* Remove successor. */
node_remove_leaf_item(iter->node, 0);
}
-
+
/*
* Restore nodes that fall under their minimum count. This may
* propagate all the way up to the root.
@@ -333,7 +344,7 @@ int btree_remove_at(btree_iterator iter)
break;
node_restore(iter->node, iter->k);
}
-
+
/*
* If combining came all the way up to the root, and it has no more
* dividers, delete it and make its only branch the root.
@@ -344,9 +355,9 @@ int btree_remove_at(btree_iterator iter)
if (root->count == 0) {
btree->root = root->branch[0];
btree->root->parent = NULL;
- free(root);
+ node_free(root);
}
-
+
finished:
btree->count--;
iter->node = NULL;
@@ -363,7 +374,7 @@ static int elevate(btree_iterator a, btree_iterator b)
{
while (a->node->depth < b->node->depth)
ascend(a);
-
+
if (a->k == b->k)
return -1;
return 0;
@@ -373,16 +384,16 @@ int btree_cmp_iters(const btree_iterator iter_a, const btree_iterator iter_b)
{
btree_iterator a = {*iter_a}, b = {*iter_b};
int ad, bd;
-
+
ad = btree_deref(a);
bd = btree_deref(b);
-
+
/* Check cases where one or both iterators are at the end. */
if (!ad)
return bd ? 1 : 0;
if (!bd)
return ad ? -1 : 0;
-
+
/* Bring iterators to the same depth. */
if (a->node->depth < b->node->depth) {
if (elevate(a, b))
@@ -391,19 +402,19 @@ int btree_cmp_iters(const btree_iterator iter_a, const btree_iterator iter_b)
if (elevate(b, a))
return 1;
}
-
+
/* Bring iterators to the same node. */
while (a->node != b->node) {
ascend(a);
ascend(b);
}
-
+
/* Now we can compare by k directly. */
if (a->k < b->k)
return -1;
if (a->k > b->k)
return 1;
-
+
return 0;
}
@@ -421,20 +432,23 @@ btree_search_implement
/************************* Private functions *************************/
-static struct btree_node *node_alloc(int internal)
+static struct btree_node *node_alloc(const struct btree_allocator *alloc,
+ int internal)
{
struct btree_node *node;
size_t isize = internal
? sizeof(struct btree_node*) * (BTREE_ITEM_MAX+1)
: 0;
- node = malloc(sizeof(struct btree_node) + isize);
+ node = (*(alloc->malloc))(alloc, \
+ sizeof(struct btree_node) + isize);
+ node->alloc = alloc;
return node;
}
static void node_delete(struct btree_node *node, struct btree *btree)
{
unsigned int i, count = node->count;
-
+
if (!node->depth) {
if (btree->destroy) {
for (i=0; i<count; i++)
@@ -448,8 +462,14 @@ static void node_delete(struct btree_node *node, struct btree *btree)
}
node_delete(node->branch[count], btree);
}
-
- free(node);
+
+ node_free(node);
+}
+
+/* Free the allocated node using its allocator */
+static void node_free(struct btree_node *node)
+{
+ (*(node->alloc->free))(node->alloc, node);
}
/* Set iter to beginning of branch pointed to by iter. */
@@ -493,11 +513,11 @@ static void node_insert(const void *x, struct btree_node *xr,
struct btree_node *p, unsigned int k)
{
unsigned int i;
-
+
for (i = p->count; i-- > k;)
p->item[i+1] = p->item[i];
p->item[k] = x;
-
+
if (p->depth) {
k++;
for (i = p->count+1; i-- > k;) {
@@ -508,7 +528,7 @@ static void node_insert(const void *x, struct btree_node *xr,
xr->parent = p;
xr->k = k;
}
-
+
p->count++;
}
@@ -524,7 +544,7 @@ static void node_split(const void **x, struct btree_node **xr,
{
unsigned int i, split;
struct btree_node *l = p, *r;
-
+
/*
* If k <= MIN, item will be inserted into left subtree, so give l
* fewer items initially.
@@ -535,17 +555,17 @@ static void node_split(const void **x, struct btree_node **xr,
split = MIN;
else
split = MIN + 1;
-
+
/*
* If l->depth is 0, allocate a leaf node.
* Otherwise, allocate an internal node.
*/
- r = node_alloc(l->depth);
-
+ r = node_alloc(l->alloc, l->depth);
+
/* l and r will be siblings, so they will have the same parent and depth. */
r->parent = l->parent;
r->depth = l->depth;
-
+
/*
* Initialize items/branches of right side.
* Do not initialize r's leftmost branch yet because we don't know
@@ -561,11 +581,11 @@ static void node_split(const void **x, struct btree_node **xr,
r->branch[i-split]->k = i-split;
}
}
-
+
/* Update counts. */
l->count = split;
r->count = MAX - split;
-
+
/*
* The nodes are now split, but the key isn't inserted yet.
*
@@ -576,7 +596,7 @@ static void node_split(const void **x, struct btree_node **xr,
node_insert(*x, *xr, l, k);
else
node_insert(*x, *xr, r, k - split);
-
+
/*
* Give l's rightmost branch to r because l's rightmost item
* is going up to become the median.
@@ -586,7 +606,7 @@ static void node_split(const void **x, struct btree_node **xr,
r->branch[0]->parent = r;
r->branch[0]->k = 0;
}
-
+
/*
* Take up l's rightmost item to make it the median.
* That item's right branch is now r.
@@ -643,24 +663,24 @@ static void move_left(struct btree_node *node, unsigned int k)
{
struct btree_node *l = node->branch[k], *r = node->branch[k+1], *mv;
unsigned int i;
-
+
l->item[l->count] = node->item[k];
node->item[k] = r->item[0];
for (i = 1; i < r->count; i++)
r->item[i-1] = r->item[i];
-
+
if (r->depth) {
mv = r->branch[0];
l->branch[l->count+1] = mv;
mv->parent = l;
mv->k = l->count+1;
-
+
for (i = 1; i <= r->count; i++) {
r->branch[i-1] = r->branch[i];
r->branch[i-1]->k = i-1;
}
}
-
+
l->count++;
r->count--;
}
@@ -669,12 +689,12 @@ static void move_right(struct btree_node *node, unsigned int k)
{
struct btree_node *l = node->branch[k], *r = node->branch[k+1];
unsigned int i;
-
+
for (i = r->count; i--;)
r->item[i+1] = r->item[i];
r->item[0] = node->item[k];
node->item[k] = l->item[l->count-1];
-
+
if (r->depth) {
for (i = r->count+1; i--;) {
r->branch[i+1] = r->branch[i];
@@ -684,7 +704,7 @@ static void move_right(struct btree_node *node, unsigned int k)
r->branch[0]->parent = r;
r->branch[0]->k = 0;
}
-
+
l->count--;
r->count++;
}
@@ -695,12 +715,12 @@ static void combine(struct btree_node *node, unsigned int k)
struct btree_node *l = node->branch[k], *r = node->branch[k+1], *mv;
const void **o = &l->item[l->count];
unsigned int i;
-
+
//append node->item[k] followed by right node's items to left node
*o++ = node->item[k];
for (i=0; i<r->count; i++)
*o++ = r->item[i];
-
+
//if applicable, append right node's branches to left node
if (r->depth) {
for (i=0; i<=r->count; i++) {
@@ -710,25 +730,25 @@ static void combine(struct btree_node *node, unsigned int k)
mv->k = l->count + i + 1;
}
}
-
+
//remove k and its right branch from parent node
for (i = k+1; i < node->count; i++) {
node->item[i-1] = node->item[i];
node->branch[i] = node->branch[i+1];
node->branch[i]->k = i;
}
-
+
//don't forget to update the left and parent node's counts and to free the right node
l->count += r->count + 1;
node->count--;
- free(r);
+ node_free(r);
}
static int node_walk_backward(const struct btree_node *node,
btree_action_t action, void *ctx)
{
unsigned int i, count = node->count;
-
+
if (!node->depth) {
for (i=count; i--;)
if (!action((void*)node->item[i], ctx))
@@ -743,7 +763,7 @@ static int node_walk_backward(const struct btree_node *node,
return 0;
}
}
-
+
return 1;
}
@@ -751,7 +771,7 @@ static int node_walk_forward(const struct btree_node *node,
btree_action_t action, void *ctx)
{
unsigned int i, count = node->count;
-
+
if (!node->depth) {
for (i=0; i<count; i++)
if (!action((void*)node->item[i], ctx))
@@ -766,6 +786,22 @@ static int node_walk_forward(const struct btree_node *node,
if (!node_walk_forward(node->branch[count], action, ctx))
return 0;
}
-
+
return 1;
}
+
+/* Default allocator implementation */
+const struct btree_allocator BTREE_DEFAULT_ALLOCATOR = {
+ .malloc = default_malloc,
+ .free = default_free,
+};
+
+static void* default_malloc(const struct btree_allocator* alloc, size_t size)
+{
+ return malloc(size);
+}
+
+static void default_free(const struct btree_allocator* alloc, void* ptr)
+{
+ free(ptr);
+}
diff --git a/ccan/btree/btree.h b/ccan/btree/btree.h
index fdf198d..a413be8 100644
--- a/ccan/btree/btree.h
+++ b/ccan/btree/btree.h
@@ -43,20 +43,30 @@ btree_lookup
*/
#define BTREE_ITEM_MAX 20
+/* Allocator interface, for use with tal/talloc, etc */
+struct btree_allocator {
+ void *(*malloc)(const struct btree_allocator *alloc, size_t size);
+ void (*free)(const struct btree_allocator *alloc, void *ptr);
+};
+
+/* Default allocator using malloc/free in libc */
+extern const struct btree_allocator BTREE_DEFAULT_ALLOCATOR;
+
struct btree_node {
struct btree_node *parent;
-
+ const struct btree_allocator *alloc;
+
/* Number of items (rather than branches). */
unsigned char count;
-
+
/* 0 if node is a leaf, 1 if it has leaf children, etc. */
unsigned char depth;
-
+
/* node->parent->branch[node->k] == this */
unsigned char k;
-
+
const void *item[BTREE_ITEM_MAX];
-
+
/*
* Allocated to BTREE_ITEM_MAX+1 items if this is
* an internal node, 0 items if it is a leaf.
@@ -68,7 +78,7 @@ typedef struct btree_iterator_s {
struct btree *btree;
struct btree_node *node;
unsigned int k;
-
+
/*
* The relationship between item and (node, k) depends on what function
* set it. It is mainly for convenience.
@@ -105,10 +115,11 @@ typedef int (*btree_action_t)(void *item, void *ctx);
struct btree {
struct btree_node *root;
size_t count; /* Total number of items in B-tree */
-
+
btree_search_t search;
bool multi;
-
+
+ const struct btree_allocator *alloc;
/*
* These are set to NULL by default.
*
@@ -123,6 +134,8 @@ struct btree {
};
struct btree *btree_new(btree_search_t search);
+struct btree *btree_new_with_alloc(btree_search_t search,
+ const struct btree_allocator *alloc);
void btree_delete(struct btree *btree);
/* Inserts an item into the btree. If an item already exists that is equal
--
2.4.10
More information about the ccan
mailing list