summaryrefslogtreecommitdiffstats
path: root/zix
diff options
context:
space:
mode:
Diffstat (limited to 'zix')
-rw-r--r--zix/btree.c194
-rw-r--r--zix/btree.h10
2 files changed, 120 insertions, 84 deletions
diff --git a/zix/btree.c b/zix/btree.c
index b03a6c5..e7912d0 100644
--- a/zix/btree.c
+++ b/zix/btree.c
@@ -317,6 +317,28 @@ zix_btree_insert(ZixBTree* const t, void* const e)
return ZIX_STATUS_SUCCESS;
}
+ZIX_PRIVATE ZixBTreeIter*
+zix_btree_iter_new(const ZixBTree* const t)
+{
+ const size_t s = t->height * sizeof(ZixBTreeIterFrame);
+ ZixBTreeIter* const i = (ZixBTreeIter*)malloc(sizeof(ZixBTreeIter) + s);
+ if (i) {
+ i->level = 0;
+ }
+ return i;
+}
+
+ZIX_PRIVATE void
+zix_btree_iter_set_frame(ZixBTreeIter* const ti,
+ ZixBTreeNode* const n,
+ const uint16_t i)
+{
+ if (ti) {
+ ti->stack[ti->level].node = n;
+ ti->stack[ti->level].index = i;
+ }
+}
+
ZIX_PRIVATE bool
zix_btree_node_is_minimal(ZixBTreeNode* const n)
{
@@ -450,67 +472,100 @@ zix_btree_remove_max(ZixBTree* const t, ZixBTreeNode* n)
return n->vals[--n->n_vals];
}
-ZIX_PRIVATE ZixStatus
-zix_btree_remove_rec(ZixBTree* const t,
- ZixBTreeNode* const n,
- const void* const e,
- void** const removed)
+ZIX_API ZixStatus
+zix_btree_remove(ZixBTree* const t,
+ const void* const e,
+ void** const out,
+ ZixBTreeIter** const next)
{
- /* To remove in a single walk down, the tree is adjusted along the way so
- that the current node always has at least one more value than the
- minimum required in general. Thus, there is always room to remove
- without adjusting on the way back up. */
- assert(n == t->root || !zix_btree_node_is_minimal(n));
+ ZixBTreeNode* n = t->root;
+ ZixBTreeIter* ti = NULL;
+ const bool user_iter = next && *next;
+ if (next) {
+ if (!*next && !(*next = zix_btree_iter_new(t))) {
+ return ZIX_STATUS_NO_MEM;
+ }
+ ti = *next;
+ ti->level = 0;
+ }
- bool equal = false;
- const uint16_t i = zix_btree_node_find(t, n, e, &equal);
+ while (true) {
+ /* To remove in a single walk down, the tree is adjusted along the way
+ so that the current node always has at least one more value than the
+ minimum required in general. Thus, there is always room to remove
+ without adjusting on the way back up. */
+ assert(n == t->root || !zix_btree_node_is_minimal(n));
- if (n->is_leaf) {
- if (equal) {
- // Found in leaf node
- *removed = zix_btree_aerase(n->vals, --n->n_vals, i);
- return ZIX_STATUS_SUCCESS;
- } else {
- // Not found in leaf node, or tree
- return ZIX_STATUS_NOT_FOUND;
- }
- } else if (equal) {
- // Found in internal node
- if (!zix_btree_node_is_minimal(n->children[i])) {
- // Left child can remove without merge
- *removed = n->vals[i];
- n->vals[i] = zix_btree_remove_max(t, n->children[i]);
- return ZIX_STATUS_SUCCESS;
- } else if (!zix_btree_node_is_minimal(n->children[i + 1])) {
- // Right child can remove without merge
- *removed = n->vals[i];
- n->vals[i] = zix_btree_remove_min(t, n->children[i + 1]);
- return ZIX_STATUS_SUCCESS;
- } else {
- // Both preceding and succeeding child are minimal
- ZixBTreeNode* const merged = zix_btree_merge(t, n, i);
- return zix_btree_remove_rec(t, merged, e, removed);
- }
- } else {
- // Not found in internal node, key is in/under children[i]
- if (zix_btree_node_is_minimal(n->children[i])) {
- if (i > 0 && !zix_btree_node_is_minimal(n->children[i - 1])) {
- // Child's left sibling has at least one key to steal
- ZixBTreeNode* const rhs = zix_btree_rotate_right(n, i);
- return zix_btree_remove_rec(t, rhs, e, removed);
- } else if (i < n->n_vals &&
- !zix_btree_node_is_minimal(n->children[i + 1])) {
- // Child's right sibling has at least one key to steal
- ZixBTreeNode* const lhs = zix_btree_rotate_left(n, i);
- return zix_btree_remove_rec(t, lhs, e, removed);
+ bool equal = false;
+ const uint16_t i = zix_btree_node_find(t, n, e, &equal);
+ zix_btree_iter_set_frame(ti, n, i);
+ if (n->is_leaf) {
+ if (equal) {
+ // Found in leaf node
+ *out = zix_btree_aerase(n->vals, --n->n_vals, i);
+ if (ti && i == n->n_vals) {
+ if (i == 0) {
+ ti->stack[ti->level = 0].node = NULL;
+ } else {
+ --ti->stack[ti->level].index;
+ zix_btree_iter_increment(ti);
+ }
+ }
+ --t->size;
+ return ZIX_STATUS_SUCCESS;
+ } else {
+ // Not found in leaf node, or tree
+ if (ti && !user_iter) {
+ zix_btree_iter_free(ti);
+ *next = NULL;
+ }
+ return ZIX_STATUS_NOT_FOUND;
+ }
+ } else if (equal) {
+ // Found in internal node
+ if (!zix_btree_node_is_minimal(n->children[i])) {
+ // Left child can remove without merge
+ *out = n->vals[i];
+ n->vals[i] = zix_btree_remove_max(t, n->children[i]);
+ --t->size;
+ return ZIX_STATUS_SUCCESS;
+ } else if (!zix_btree_node_is_minimal(n->children[i + 1])) {
+ // Right child can remove without merge
+ *out = n->vals[i];
+ n->vals[i] = zix_btree_remove_min(t, n->children[i + 1]);
+ --t->size;
+ return ZIX_STATUS_SUCCESS;
} else {
- // Both child's siblings are minimal, merge them
- const uint16_t m = (i < n->n_vals) ? i : i - 1;
- ZixBTreeNode* const merged = zix_btree_merge(t, n, m);
- return zix_btree_remove_rec(t, merged, e, removed);
+ // Both preceding and succeeding child are minimal
+ n = zix_btree_merge(t, n, i);
}
} else {
- return zix_btree_remove_rec(t, n->children[i], e, removed);
+ // Not found in internal node, key is in/under children[i]
+ if (zix_btree_node_is_minimal(n->children[i])) {
+ if (i > 0 && !zix_btree_node_is_minimal(n->children[i - 1])) {
+ // Steal a key from child's left sibling
+ n = zix_btree_rotate_right(n, i);
+ } else if (i < n->n_vals &&
+ !zix_btree_node_is_minimal(n->children[i + 1])) {
+ // Steal a key from child's right sibling
+ n = zix_btree_rotate_left(n, i);
+ } else {
+ // Both child's siblings are minimal, merge them
+ if (i < n->n_vals) {
+ n = zix_btree_merge(t, n, i);
+ } else {
+ n = zix_btree_merge(t, n, i - 1);
+ if (ti) {
+ --ti->stack[ti->level].index;
+ }
+ }
+ }
+ } else {
+ n = n->children[i];
+ }
+ }
+ if (ti) {
+ ++ti->level;
}
}
@@ -519,27 +574,6 @@ zix_btree_remove_rec(ZixBTree* const t,
}
ZIX_API ZixStatus
-zix_btree_remove(ZixBTree* const t, const void* const e, void** const removed)
-{
- const ZixStatus st = zix_btree_remove_rec(t, t->root, e, removed);
- if (!st) {
- --t->size;
- }
- return st;
-}
-
-ZIX_PRIVATE ZixBTreeIter*
-zix_btree_iter_new(const ZixBTree* const t)
-{
- const size_t s = t->height * sizeof(ZixBTreeIterFrame);
- ZixBTreeIter* const i = (ZixBTreeIter*)malloc(sizeof(ZixBTreeIter) + s);
- if (i) {
- i->level = 0;
- }
- return i;
-}
-
-ZIX_API ZixStatus
zix_btree_find(const ZixBTree* const t,
const void* const e,
ZixBTreeIter** const ti)
@@ -553,9 +587,7 @@ zix_btree_find(const ZixBTree* const t,
bool equal = false;
const uint16_t i = zix_btree_node_find(t, n, e, &equal);
- // Update iterator stack
- (*ti)->stack[(*ti)->level].node = n;
- (*ti)->stack[(*ti)->level].index = i;
+ zix_btree_iter_set_frame(*ti, n, i);
if (equal) {
return ZIX_STATUS_SUCCESS;
@@ -588,9 +620,7 @@ zix_btree_lower_bound(const ZixBTree* const t,
bool equal = false;
const uint16_t i = zix_btree_node_find(t, n, e, &equal);
- // Update iterator stack
- (*ti)->stack[(*ti)->level].node = n;
- (*ti)->stack[(*ti)->level].index = i;
+ zix_btree_iter_set_frame(*ti, n, i);
if (equal) {
found_level = (*ti)->level;
diff --git a/zix/btree.h b/zix/btree.h
index 0cd549a..3acd3a4 100644
--- a/zix/btree.h
+++ b/zix/btree.h
@@ -78,10 +78,16 @@ ZIX_API ZixStatus
zix_btree_insert(ZixBTree* t, void* e);
/**
- Remove the value `e` from `t`, set `removed` to the removed pointer.
+ Remove the value `e` from `t`.
+
+ @param out Set to point to the removed pointer (which may not equal `e`).
+
+ @param next If non-NULL, pointed to the value following `e`. If *next is
+ also non-NULL, the iterator is reused, otherwise a new one is allocated. To
+ reuse an iterator, no items may have been added since its creation.
*/
ZIX_API ZixStatus
-zix_btree_remove(ZixBTree* t, const void* e, void** removed);
+zix_btree_remove(ZixBTree* t, const void* e, void** out, ZixBTreeIter** next);
/**
Set `ti` to an element equal to `e` in `t`.