static bool delete_node(const void *key, bnode_t *bnode, piojo_btree_t *tree) { bool found_p, deleted_p=FALSE; size_t i; bnode_t *next; iter_t iter; while (bnode != NULL){ i = bin_search(key, tree, bnode, &found_p); if (found_p){ /* Key in leaf, shrink the leaf and finish. */ if (bnode->leaf_p){ free_entry(&bnode->kvs[i], tree, &deleted_p); --bnode->ecnt; for (; i < bnode->ecnt; ++i){ copy_bentry(i + 1, bnode, i, bnode, tree); } return TRUE; } /* Key in internal node, move prev/next key up and delete it. */ iter.bnode = bnode; if (bnode->children[i]->ecnt >= tree->cmin){ free_entry(&bnode->kvs[i], tree, &deleted_p); iter.eidx = i; search_max(&iter); copy_bentry(iter.eidx, iter.bnode, i, bnode, tree); key = entry_key(i, bnode, tree); bnode = bnode->children[i]; }else if (bnode->children[i + 1]->ecnt >= tree->cmin){ free_entry(&bnode->kvs[i], tree, &deleted_p); iter.eidx = i + 1; search_min(&iter); copy_bentry(iter.eidx, iter.bnode, i, bnode, tree); key = entry_key(i, bnode, tree); bnode = bnode->children[i + 1]; }else{ /* Both node children are key deficient, merge and try again. */ PIOJO_ASSERT(bnode->children[i]->ecnt == tree->cmin - 1); PIOJO_ASSERT(bnode->children[i + 1]->ecnt == tree->cmin - 1); next = bnode->children[i]; merge_bnodes(tree, i, next, bnode->children[i + 1], bnode); bnode = next; } }else if (! bnode->leaf_p){ /* Key not in internal node, rebalance and try again. */ next = bnode->children[i]; if (next->ecnt < tree->cmin){ PIOJO_ASSERT(next->ecnt == tree->cmin - 1); next = rebalance_bnode(tree, i, next, bnode); } bnode = next; }else{ /* Key not in leaf. */ bnode = NULL; } } return FALSE; }
/** * Reads the previous key (order given by @a keycmp function). * @param[in] key * @param[in] tree * @param[out] data Entry value, can be @b NULL. * @return previous key or @b NULL if @a key is the first one. */ const void* piojo_btree_prev(const void *key, const piojo_btree_t *tree, void **data) { iter_t iter; PIOJO_ASSERT(tree); PIOJO_ASSERT(key); iter = search_node(key, tree); PIOJO_ASSERT(iter.bnode != NULL); if (! iter.bnode->leaf_p && iter.eidx < iter.bnode->ecnt + 1){ iter.bnode = iter.bnode->children[iter.eidx]; iter.eidx = iter.bnode->ecnt; search_max(&iter); }else if (iter.eidx > 0){ --iter.eidx; }else{ while (iter.bnode->parent != NULL){ iter.eidx = iter.bnode->pidx; iter.bnode = iter.bnode->parent; if (iter.eidx > 0){ --iter.eidx; if (data != NULL){ *data = entry_val(iter.eidx, iter.bnode, tree); } return entry_key(iter.eidx, iter.bnode, tree); } } return NULL; } if (data != NULL){ *data = entry_val(iter.eidx, iter.bnode, tree); } return entry_key(iter.eidx, iter.bnode, tree); }
static int _mtbl_sorter_compare(const void *va, const void *vb) { const struct entry *a = *((const struct entry **) va); const struct entry *b = *((const struct entry **) vb); return (bytes_compare(entry_key(a), a->len_key, entry_key(b), b->len_key)); }
static size_t bin_search(const void *key, const piojo_btree_t *tree, bnode_t *bnode, bool *found_p) { int cmpval; size_t mid, imin = 0, imax = bnode->ecnt - 1; *found_p = FALSE; if (bnode->ecnt > 0){ while (imin <= imax){ mid = imin + ((imax - imin) / 2); cmpval = tree->cmp_cb(key, entry_key(mid, bnode, tree)); if (cmpval == 0){ *found_p = TRUE; imin = mid; break; }else if (cmpval > 0){ imin = mid + 1; }else if (imin != mid){ imax = mid - 1; }else{ break; } } } return imin; }
/** * Deletes all entries in @a tree. * @param[out] tree Tree being cleared. */ void piojo_btree_clear(piojo_btree_t *tree) { void *key; PIOJO_ASSERT(tree); key = tree->allocator.alloc_cb(tree->eksize); PIOJO_ASSERT(key); while (tree->ecount > 0){ memcpy(key, entry_key(0, tree->root, tree), tree->eksize); delete_node(key, tree->root, tree); --tree->ecount; } tree->allocator.free_cb(key); }
/* Similar to search_node() but split bnodes before traversing them. */ static iter_t insert_node(const void *key, const void *data, piojo_btree_t *tree) { int cmpval; bool found_p; size_t idx=0, j; iter_t iter; bnode_t *bnode; if (tree->root->ecnt == tree->cmax - 1){ split_root(tree); } bnode = tree->root; iter.bnode = NULL; while (bnode->ecnt > 0){ idx = bin_search(key, tree, bnode, &found_p); if (found_p){ iter.bnode = bnode; iter.eidx = idx; return iter; }else if (bnode->leaf_p){ break; } if (bnode->children[idx]->ecnt == tree->cmax - 1){ split_bnode(tree, idx, bnode->children[idx], bnode); cmpval = tree->cmp_cb(key, entry_key(idx, bnode, tree)); if (cmpval == 0){ iter.bnode = bnode; iter.eidx = idx; return iter; }else if (cmpval > 0){ ++idx; } } bnode = bnode->children[idx]; } PIOJO_ASSERT(bnode->ecnt < tree->cmax - 1); for (j = bnode->ecnt; j > idx; --j){ copy_bentry(j - 1, bnode, j, bnode, tree); } init_entry(key, data, idx, bnode, tree); ++bnode->ecnt; return iter; }
static void init_entry(const void *key, const void *data, uint8_t eidx, const bnode_t *bnode, const piojo_btree_t *tree) { bool null_p = TRUE; piojo_alloc_if ator = tree->allocator; if (data == NULL){ data = &null_p; } memcpy(entry_key(eidx, bnode, tree), key, tree->eksize); bnode->kvs[eidx].value = ator.alloc_cb(tree->evsize); PIOJO_ASSERT(bnode->kvs[eidx].value); memcpy(entry_val(eidx, bnode, tree), data, tree->evsize); }
/** * Reads the last key in @a tree (order given by @a keycmp function). * @param[in] tree * @param[out] data Entry value, can be @b NULL. * @return last key or @b NULL if @a tree is empty. */ const void* piojo_btree_last(const piojo_btree_t *tree, void **data) { iter_t iter; PIOJO_ASSERT(tree); if (tree->ecount > 0){ iter.tree = tree; iter.eidx = tree->root->ecnt; iter.bnode = tree->root; search_max(&iter); if (data != NULL){ *data = entry_val(iter.eidx, iter.bnode, tree); } return entry_key(iter.eidx, iter.bnode, tree); } return NULL; }