/* Creates a new node as a child of |dst| on side |dir|. Copies data and |tavl_balance| from |src| into the new node, applying |copy()|, if non-null. Returns nonzero only if fully successful. Regardless of success, integrity of the tree structure is assured, though failure may leave a null pointer in a |tavl_data| member. */ static int copy_node (struct tavl_table *tree, struct tavl_node *dst, int dir, const struct tavl_node *src, tavl_copy_func *copy) { struct tavl_node *new = tree->tavl_alloc->libavl_malloc (tree->tavl_alloc, sizeof *new); if (new == NULL) return 0; new->tavl_link[dir] = dst->tavl_link[dir]; new->tavl_tag[dir] = TAVL_THREAD; new->tavl_link[!dir] = dst; new->tavl_tag[!dir] = TAVL_THREAD; dst->tavl_link[dir] = new; dst->tavl_tag[dir] = TAVL_CHILD; new->tavl_balance = src->tavl_balance; if (copy == NULL) new->tavl_data = src->tavl_data; else { new->tavl_data = copy (src->tavl_data, tree->tavl_param); if (new->tavl_data == NULL) return 0; } return 1; } /* Destroys |new| with |tavl_destroy (new, destroy)|, first initializing the right link in |new| that has not yet been initialized. */ static void copy_error_recovery (struct tavl_node *p, struct tavl_table *new, tavl_item_func *destroy) { new->tavl_root = p; if (p != NULL) { while (p->tavl_tag[1] == TAVL_CHILD) p = p->tavl_link[1]; p->tavl_link[1] = NULL; } tavl_destroy (new, destroy); } /* Copies |org| to a newly created tree, which is returned. If |copy != NULL|, each data item in |org| is first passed to |copy|, and the return values are inserted into the tree, with |NULL| return values taken as indications of failure. On failure, destroys the partially created new tree, applying |destroy|, if non-null, to each item in the new tree so far, and returns |NULL|. If |allocator != NULL|, it is used for allocation in the new tree. Otherwise, the same allocator used for |org| is used. */ struct tavl_table * tavl_copy (const struct tavl_table *org, tavl_copy_func *copy, tavl_item_func *destroy, struct libavl_allocator *allocator) { struct tavl_table *new; const struct tavl_node *p; struct tavl_node *q; struct tavl_node rp, rq; assert (org != NULL); new = tavl_create (org->tavl_compare, org->tavl_param, allocator != NULL ? allocator : org->tavl_alloc); if (new == NULL) return NULL; new->tavl_count = org->tavl_count; if (new->tavl_count == 0) return new; p = &rp; rp.tavl_link[0] = org->tavl_root; rp.tavl_tag[0] = TAVL_CHILD; q = &rq; rq.tavl_link[0] = NULL; rq.tavl_tag[0] = TAVL_THREAD; for (;;) { if (p->tavl_tag[0] == TAVL_CHILD) { if (!copy_node (new, q, 0, p->tavl_link[0], copy)) { copy_error_recovery (rq.tavl_link[0], new, destroy); return NULL; } p = p->tavl_link[0]; q = q->tavl_link[0]; } else { while (p->tavl_tag[1] == TAVL_THREAD) { p = p->tavl_link[1]; if (p == NULL) { q->tavl_link[1] = NULL; new->tavl_root = rq.tavl_link[0]; return new; } q = q->tavl_link[1]; } p = p->tavl_link[1]; q = q->tavl_link[1]; } if (p->tavl_tag[1] == TAVL_CHILD) if (!copy_node (new, q, 1, p->tavl_link[1], copy)) { copy_error_recovery (rq.tavl_link[0], new, destroy); return NULL; } } }