[PATCH] btree: Add custom allocator interface.
[email protected] Sun, 7 Feb 2016 10:10:39 +1000
| Newsgroups | org.ozlabs.lists.ccan |
|---|---|
| Message-ID | <[email protected]> |
From: Stuart Longland <[email protected]> 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 _______________________________________________ ccan mailing list [email protected] https://lists.ozlabs.org/listinfo/ccan