/* * Copyright (c) 2016 Jiri Svoboda * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup libc * @{ */ /** @file Ordered dictionary. * * Implementation based on red-black trees. * Note that non-data ('leaf') nodes are implemented as NULLs, not * as actual nodes. */ #include #include #include #include #include #include #include static void odict_pgu(odlink_t *, odlink_t **, odict_child_sel_t *, odlink_t **, odict_child_sel_t *, odlink_t **); static void odict_rotate_left(odlink_t *); static void odict_rotate_right(odlink_t *); static void odict_swap_node(odlink_t *, odlink_t *); static void odict_replace_subtree(odlink_t *, odlink_t *); static void odict_unlink(odlink_t *); static void odict_link_child_a(odlink_t *, odlink_t *); static void odict_link_child_b(odlink_t *, odlink_t *); static void odict_sibling(odlink_t *, odlink_t *, odict_child_sel_t *, odlink_t **); static odlink_t *odict_search_start_node(odict_t *, void *, odlink_t *); /** Print subtree. * * Print subtree rooted at @a cur * * @param cur Root of tree to print */ static void odict_print_tree(odlink_t *cur) { if (cur == NULL) { printf("0"); return; } printf("[%p/%c", cur, cur->color == odc_red ? 'r' : 'b'); if (cur->a != NULL || cur->b != NULL) { putchar(' ' ); odict_print_tree(cur->a); putchar(','); odict_print_tree(cur->b); } putchar(']'); } /** Validate ordered dictionary subtree. * * Verify that red-black tree properties are satisfied. * * @param cur Root of tree to verify * @param rbd Place to store black depth of the subtree * * @return EOK on success, EINVAL on failure */ static errno_t odict_validate_tree(odlink_t *cur, int *rbd) { errno_t rc; int bd_a, bd_b; int cur_d; if (cur->up == NULL) { /* Verify root pointer */ if (cur->odict->root != cur) { printf("cur->up == NULL and yet cur != root\n"); return EINVAL; } /* Verify root color */ if (cur->color != odc_black) { printf("Root is not black\n"); return EINVAL; } } if (cur->a != NULL) { /* Verify symmetry of a - up links */ if (cur->a->up != cur) { printf("cur->a->up != cur\n"); return EINVAL; } /* Verify that if a node is red, its left child is red */ if (cur->a->color == odc_red && cur->color == odc_red) { printf("cur->a is red, cur is red\n"); return EINVAL; } /* Recurse to left child */ rc = odict_validate_tree(cur->a, &bd_a); if (rc != EOK) return rc; } else { bd_a = -1; } if (cur->b != NULL) { /* Verify symmetry of b - up links */ if (cur->b->up != cur) { printf("cur->b->up != cur\n"); return EINVAL; } /* Verify that if a node is red, its right child is red */ if (cur->b->color == odc_red && cur->color == odc_red) { printf("cur->b is red, cur is red\n"); return EINVAL; } /* Recurse to right child */ rc = odict_validate_tree(cur->b, &bd_b); if (rc != EOK) return rc; } else { bd_b = -1; } /* Verify that black depths of both children are equal */ if (bd_a >= 0 && bd_b >= 0) { if (bd_a != bd_b) { printf("Black depth %d != %d\n", bd_a, bd_b); return EINVAL; } } cur_d = cur->color == odc_black ? 1 : 0; if (bd_a >= 0) *rbd = bd_a + cur_d; else if (bd_b >= 0) *rbd = bd_b + cur_d; else *rbd = cur_d; return EOK; } /** Validate ordered dictionary properties. * * @param odict Ordered dictionary */ errno_t odict_validate(odict_t *odict) { int bd; errno_t rc; if (odict->root == NULL) return EOK; rc = odict_validate_tree(odict->root, &bd); if (rc != EOK) odict_print_tree(odict->root); return rc; } /** Initialize ordered dictionary. * * @param odict Ordered dictionary * @param getkey Funcition to get key * @param cmp Function to compare entries */ void odict_initialize(odict_t *odict, odgetkey_t getkey, odcmp_t cmp) { odict->root = NULL; list_initialize(&odict->entries); odict->getkey = getkey; odict->cmp = cmp; } /** Initialize ordered dictionary link. * * @param odlink Ordered dictionary link */ void odlink_initialize(odlink_t *odlink) { odlink->odict = NULL; odlink->up = NULL; odlink->a = NULL; odlink->b = NULL; link_initialize(&odlink->lentries); } /** Insert entry in ordered dictionary. * * Insert entry in ordered dictionary, placing it after other entries * with the same key. * * @param odlink New entry * @param odict Ordered dictionary * @param hint An entry that might be near the new entry or @c NULL */ void odict_insert(odlink_t *odlink, odict_t *odict, odlink_t *hint) { int d; odlink_t *cur; odlink_t *p; odlink_t *g; odlink_t *u; odict_child_sel_t pcs, gcs; assert(!odlink_used(odlink)); if (odict->root == NULL) { /* odlink is the root node */ odict->root = odlink; odlink->odict = odict; odlink->color = odc_black; list_append(&odlink->lentries, &odict->entries); return; } cur = odict_search_start_node(odict, odict->getkey(odlink), hint); while (true) { d = odict->cmp(odict->getkey(odlink), odict->getkey(cur)); if (d < 0) { if (cur->a == NULL) { odict_link_child_a(odlink, cur); break; } cur = cur->a; } else { if (cur->b == NULL) { odict_link_child_b(odlink, cur); break; } cur = cur->b; } } odlink->color = odc_red; while (true) { /* Fix up odlink and its parent potentially being red */ if (odlink->up == NULL) { odlink->color = odc_black; break; } if (odlink->up->color == odc_black) break; /* Get parent, grandparent, uncle */ odict_pgu(odlink, &p, &pcs, &g, &gcs, &u); if (g == NULL) { p->color = odc_black; break; } if (p->color == odc_red && u != NULL && u->color == odc_red) { /* Parent and uncle are both red */ p->color = odc_black; u->color = odc_black; g->color = odc_red; odlink = g; continue; } /* Parent is red but uncle is black, odlink-P-G is trans */ if (pcs != gcs) { if (gcs == odcs_a) { /* odlink is right child of P */ /* P is left child of G */ odict_rotate_left(p); } else { /* odlink is left child of P */ /* P is right child of G */ odict_rotate_right(p); } odlink = p; odict_pgu(odlink, &p, &pcs, &g, &gcs, &u); } /* odlink-P-G is now cis */ assert(pcs == gcs); if (pcs == odcs_a) { /* odlink is left child of P */ /* P is left child of G */ odict_rotate_right(g); } else { /* odlink is right child of P */ /* P is right child of G */ odict_rotate_left(g); } p->color = odc_black; g->color = odc_red; break; } } /** Remove entry from ordered dictionary. * * @param odlink Ordered dictionary link */ void odict_remove(odlink_t *odlink) { odlink_t *n; odlink_t *c; odlink_t *p; odlink_t *s; odlink_t *sc, *st; odict_child_sel_t pcs; if (odlink->a != NULL && odlink->b != NULL) { n = odict_next(odlink, odlink->odict); assert(n != NULL); odict_swap_node(odlink, n); } /* odlink has at most one child */ if (odlink->a != NULL) { assert(odlink->b == NULL); c = odlink->a; } else { c = odlink->b; } if (odlink->color == odc_red) { /* We can remove it harmlessly */ assert(c == NULL); odict_unlink(odlink); return; } /* odlink->color == odc_black */ if (c != NULL && c->color == odc_red) { /* Child is red: swap colors of S and C */ c->color = odc_black; odict_replace_subtree(c, odlink); odlink->up = odlink->a = odlink->b = NULL; odlink->odict = NULL; list_remove(&odlink->lentries); return; } /* There cannot be one black child */ assert(c == NULL); n = NULL; p = odlink->up; odict_unlink(odlink); /* We removed one black node, creating imbalance */ again: /* Case 1: N is the new root */ if (p == NULL) return; odict_sibling(n, p, &pcs, &s); /* Paths through N have one less black node than paths through S */ /* Case 2: S is red */ if (s->color == odc_red) { assert(p->color == odc_black); p->color = odc_red; s->color = odc_black; if (n == p->a) odict_rotate_left(p); else odict_rotate_right(p); odict_sibling(n, p, &pcs, &s); /* Now S is black */ assert(s->color == odc_black); } /* Case 3: P, S and S's children are black */ if (p->color == odc_black && s->color == odc_black && (s->a == NULL || s->a->color == odc_black) && (s->b == NULL || s->b->color == odc_black)) { /* * Changing S to red means all paths through S or N have one * less black node than they should. So redo the same for P. */ s->color = odc_red; n = p; p = n->up; goto again; } /* Case 4: P is red, S and S's children are black */ if (p->color == odc_red && s->color == odc_black && (s->a == NULL || s->a->color == odc_black) && (s->b == NULL || s->b->color == odc_black)) { /* Swap colors of S and P */ s->color = odc_red; p->color = odc_black; return; } /* N is the left child */ if (pcs == odcs_a) { st = s->a; sc = s->b; } else { st = s->b; sc = s->a; } /* Case 5: S is black and S's trans child is red, S's cis child is black */ if (s->color == odc_black && (st != NULL && st->color == odc_red) && (sc == NULL || sc->color == odc_black)) { /* N is the left child */ if (pcs == odcs_a) odict_rotate_right(s); else odict_rotate_left(s); s->color = odc_red; s->up->color = odc_black; /* Now N has a black sibling whose cis child is red */ odict_sibling(n, p, &pcs, &s); /* N is the left child */ if (pcs == odcs_a) { st = s->a; sc = s->b; } else { st = s->b; sc = s->a; } } /* Case 6: S is black, S's cis child is red */ assert(s->color == odc_black); assert(sc != NULL); assert(sc->color == odc_red); if (pcs == odcs_a) odict_rotate_left(p); else odict_rotate_right(p); s->color = p->color; p->color = odc_black; sc->color = odc_black; } /** Update dictionary after entry key has been changed. * * After the caller modifies the key of an entry, they need to call * this function so that the dictionary can update itself accordingly. * * @param odlink Ordered dictionary entry * @param odict Ordered dictionary */ void odict_key_update(odlink_t *odlink, odict_t *odict) { odlink_t *n; n = odict_next(odlink, odict); odict_remove(odlink); odict_insert(odlink, odict, n); } /** Return true if entry is in a dictionary. * * @param odlink Ordered dictionary entry * @return @c true if entry is in a dictionary, @c false otherwise */ bool odlink_used(odlink_t *odlink) { return odlink->odict != NULL; } /** Return true if ordered dictionary is empty. * * @param odict Ordered dictionary * @return @c true if @a odict is emptry, @c false otherwise */ bool odict_empty(odict_t *odict) { return odict->root == NULL; } /** Return the number of entries in @a odict. * * @param odict Ordered dictionary */ unsigned long odict_count(odict_t *odict) { unsigned long cnt; odlink_t *cur; cnt = 0; cur = odict_first(odict); while (cur != NULL) { ++cnt; cur = odict_next(cur, odict); } return cnt; } /** Return first entry in a list or @c NULL if list is empty. * * @param odict Ordered dictionary * @return First entry */ odlink_t *odict_first(odict_t *odict) { link_t *link; link = list_first(&odict->entries); if (link == NULL) return NULL; return list_get_instance(link, odlink_t, lentries); } /** Return last entry in a list or @c NULL if list is empty * * @param odict Ordered dictionary * @return Last entry */ odlink_t *odict_last(odict_t *odict) { link_t *link; link = list_last(&odict->entries); if (link == NULL) return NULL; return list_get_instance(link, odlink_t, lentries); } /** Return previous entry in list or @c NULL if @a link is the first one. * * @param odlink Entry * @param odict Ordered dictionary * @return Previous entry */ odlink_t *odict_prev(odlink_t *odlink, odict_t *odict) { link_t *link; link = list_prev(&odlink->lentries, &odlink->odict->entries); if (link == NULL) return NULL; return list_get_instance(link, odlink_t, lentries); } /** Return next entry in dictionary or @c NULL if @a odlink is the last one * * @param odlink Entry * @param odict Ordered dictionary * @return Next entry */ odlink_t *odict_next(odlink_t *odlink, odict_t *odict) { link_t *link; link = list_next(&odlink->lentries, &odlink->odict->entries); if (link == NULL) return NULL; return list_get_instance(link, odlink_t, lentries); } /** Find first entry whose key is equal to @a key/ * * @param odict Ordered dictionary * @param key Key * @param hint Nearby entry * @return Pointer to entry on success, @c NULL on failure */ odlink_t *odict_find_eq(odict_t *odict, void *key, odlink_t *hint) { odlink_t *geq; geq = odict_find_geq(odict, key, hint); if (geq == NULL) return NULL; if (odict->cmp(odict->getkey(geq), key) == 0) return geq; else return NULL; } /** Find last entry whose key is equal to @a key/ * * @param odict Ordered dictionary * @param key Key * @param hint Nearby entry * @return Pointer to entry on success, @c NULL on failure */ odlink_t *odict_find_eq_last(odict_t *odict, void *key, odlink_t *hint) { odlink_t *leq; leq = odict_find_leq(odict, key, hint); if (leq == NULL) return NULL; if (odict->cmp(odict->getkey(leq), key) == 0) return leq; else return NULL; } /** Find first entry whose key is greater than or equal to @a key * * @param odict Ordered dictionary * @param key Key * @param hint Nearby entry * @return Pointer to entry on success, @c NULL on failure */ odlink_t *odict_find_geq(odict_t *odict, void *key, odlink_t *hint) { odlink_t *cur; odlink_t *next; int d; cur = odict_search_start_node(odict, key, hint); if (cur == NULL) return NULL; while (true) { d = odict->cmp(odict->getkey(cur), key); if (d >= 0) next = cur->a; else next = cur->b; if (next == NULL) break; cur = next; } if (d >= 0) { return cur; } else { return odict_next(cur, odict); } } /** Find last entry whose key is greater than @a key. * * @param odict Ordered dictionary * @param key Key * @param hint Nearby entry * @return Pointer to entry on success, @c NULL on failure */ odlink_t *odict_find_gt(odict_t *odict, void *key, odlink_t *hint) { odlink_t *leq; leq = odict_find_leq(odict, key, hint); if (leq != NULL) return odict_next(leq, odict); else return odict_first(odict); } /** Find last entry whose key is less than or equal to @a key * * @param odict Ordered dictionary * @param key Key * @param hint Nearby entry * @return Pointer to entry on success, @c NULL on failure */ odlink_t *odict_find_leq(odict_t *odict, void *key, odlink_t *hint) { odlink_t *cur; odlink_t *next; int d; cur = odict_search_start_node(odict, key, hint); if (cur == NULL) return NULL; while (true) { d = odict->cmp(key, odict->getkey(cur)); if (d >= 0) next = cur->b; else next = cur->a; if (next == NULL) break; cur = next; } if (d >= 0) { return cur; } else { return odict_prev(cur, odict); } } /** Find last entry whose key is less than @a key. * * @param odict Ordered dictionary * @param key Key * @param hint Nearby entry * @return Pointer to entry on success, @c NULL on failure */ odlink_t *odict_find_lt(odict_t *odict, void *key, odlink_t *hint) { odlink_t *geq; geq = odict_find_geq(odict, key, hint); if (geq != NULL) return odict_prev(geq, odict); else return odict_last(odict); } /** Return parent, grandparent and uncle. * * @param n Node * @param p Place to store pointer to parent of @a n * @param pcs Place to store position of @a n w.r.t. @a p * @param g Place to store pointer to grandparent of @a n * @param gcs Place to store position of @a p w.r.t. @a g * @param u Place to store pointer to uncle of @a n */ static void odict_pgu(odlink_t *n, odlink_t **p, odict_child_sel_t *pcs, odlink_t **g, odict_child_sel_t *gcs, odlink_t **u) { *p = n->up; if (*p == NULL) { /* No parent */ *g = NULL; *u = NULL; return; } if ((*p)->a == n) { *pcs = odcs_a; } else { assert((*p)->b == n); *pcs = odcs_b; } *g = (*p)->up; if (*g == NULL) { /* No grandparent */ *u = NULL; return; } if ((*g)->a == *p) { *gcs = odcs_a; *u = (*g)->b; } else { assert((*g)->b == *p); *gcs = odcs_b; *u = (*g)->a; } } /** Return sibling and parent w.r.t. parent. * * @param n Node * @param p Parent of @ an * @param pcs Place to store position of @a n w.r.t. @a p. * @param rs Place to strore pointer to sibling */ static void odict_sibling(odlink_t *n, odlink_t *p, odict_child_sel_t *pcs, odlink_t **rs) { if (p->a == n) { *pcs = odcs_a; *rs = p->b; } else { *pcs = odcs_b; *rs = p->a; } } /** Ordered dictionary left rotation. * * Q P * P C <- A Q * A B B C * */ static void odict_rotate_left(odlink_t *p) { odlink_t *q; q = p->b; assert(q != NULL); /* Replace P with Q as the root of the subtree */ odict_replace_subtree(q, p); /* Relink P under Q, B under P */ p->up = q; p->b = q->a; if (p->b != NULL) p->b->up = p; q->a = p; /* Fix odict root */ if (p->odict->root == p) p->odict->root = q; } /** Ordered dictionary right rotation. * * Q P * P C -> A Q * A B B C * */ static void odict_rotate_right(odlink_t *q) { odlink_t *p; p = q->a; assert(p != NULL); /* Replace Q with P as the root of the subtree */ odict_replace_subtree(p, q); /* Relink Q under P, B under Q */ q->up = p; q->a = p->b; if (q->a != NULL) q->a->up = q; p->b = q; /* Fix odict root */ if (q->odict->root == q) q->odict->root = p; } /** Swap two nodes. * * Swap position of two nodes in the tree, keeping their identity. * This means we don't copy the contents, instead we shuffle around pointers * from and to the nodes. * * @param a First node * @param b Second node */ static void odict_swap_node(odlink_t *a, odlink_t *b) { odlink_t *n; odict_color_t c; /* Backlink from A's parent */ if (a->up != NULL && a->up != b) { if (a->up->a == a) { a->up->a = b; } else { assert(a->up->b == a); a->up->b = b; } } /* Backlink from A's left child */ if (a->a != NULL && a->a != b) a->a->up = b; /* Backling from A's right child */ if (a->b != NULL && a->b != b) a->b->up = b; /* Backlink from B's parent */ if (b->up != NULL && b->up != a) { if (b->up->a == b) { b->up->a = a; } else { assert(b->up->b == b); b->up->b = a; } } /* Backlink from B's left child */ if (b->a != NULL && b->a != a) b->a->up = a; /* Backling from B's right child */ if (b->b != NULL && b->b != a) b->b->up = a; /* Swap links going out of A and out of B */ n = a->up; a->up = b->up; b->up = n; n = a->a; a->a = b->a; b->a = n; n = a->b; a->b = b->b; b->b = n; c = a->color; a->color = b->color; b->color = c; /* When A and B are adjacent, fix self-loops that might have arisen */ if (a->up == a) a->up = b; if (a->a == a) a->a = b; if (a->b == a) a->b = b; if (b->up == b) b->up = a; if (b->a == b) b->a = a; if (b->b == b) b->b = a; /* Fix odict root */ if (a == a->odict->root) a->odict->root = b; else if (b == a->odict->root) a->odict->root = a; } /** Replace subtree. * * Replace subtree @a old with another subtree @a n. This makes the parent * point to the new subtree root and the up pointer of @a n to point to * the parent. * * @param old Subtree to be replaced * @param n New subtree */ static void odict_replace_subtree(odlink_t *n, odlink_t *old) { if (old->up != NULL) { if (old->up->a == old) { old->up->a = n; } else { assert(old->up->b == old); old->up->b = n; } } else { assert(old->odict->root == old); old->odict->root = n; } n->up = old->up; } /** Unlink node. * * @param n Ordered dictionary node */ static void odict_unlink(odlink_t *n) { if (n->up != NULL) { if (n->up->a == n) { n->up->a = NULL; } else { assert(n->up->b == n); n->up->b = NULL; } n->up = NULL; } else { assert(n->odict->root == n); n->odict->root = NULL; } if (n->a != NULL) { n->a->up = NULL; n->a = NULL; } if (n->b != NULL) { n->b->up = NULL; n->b = NULL; } n->odict = NULL; list_remove(&n->lentries); } /** Link node as left child. * * Append new node @a n as left child of existing node @a old. * * @param n New node * @param old Old node */ static void odict_link_child_a(odlink_t *n, odlink_t *old) { old->a = n; n->up = old; n->odict = old->odict; list_insert_before(&n->lentries, &old->lentries); } /** Link node as right child. * * Append new node @a n as right child of existing node @a old. * * @param n New node * @param old Old node */ static void odict_link_child_b(odlink_t *n, odlink_t *old) { old->b = n; n->up = old; n->odict = old->odict; list_insert_after(&n->lentries, &old->lentries); } /** Get node where search should be started. * * @param odict Ordered dictionary * @param key Key being searched for * @param hint Node that might be near the search target or @c NULL * * @return Node from where search should be started */ static odlink_t *odict_search_start_node(odict_t *odict, void *key, odlink_t *hint) { odlink_t *a; odlink_t *b; odlink_t *cur; int d, da, db; assert(hint == NULL || hint->odict == odict); /* If the key is greater than the maximum, start search in the maximum */ b = odict_last(odict); if (b != NULL) { d = odict->cmp(odict->getkey(b), key); if (d < 0) return b; } /* If the key is less tna the minimum, start search in the minimum */ a = odict_first(odict); if (a != NULL) { d = odict->cmp(key, odict->getkey(a)); if (d < 0) return a; } /* * Proposition: Let A, B be two BST nodes such that B is a descendant * of A. Let N be a node such that either key(A) < key(N) < key(B) * Then N is a descendant of A. * Corollary: We can start searching for N from A, instead from * the root. * * Proof: When walking the BST in order, visit_tree(A) does a * visit_tree(A->a), visit(A), visit(A->b). If key(A) < key(B), * we will first visit A, then while visiting all nodes with values * between A and B we will not leave subtree A->b. */ /* If there is no hint, start search from the root */ if (hint == NULL) return odict->root; /* * Start from hint and walk up to the root, keeping track of * minimum and maximum. Once key is strictly between them, * we can return the current node, which we've proven to be * an ancestor of a potential node with the given key */ a = b = cur = hint; while (cur->up != NULL) { cur = cur->up; d = odict->cmp(odict->getkey(cur), odict->getkey(a)); if (d < 0) a = cur; d = odict->cmp(odict->getkey(b), odict->getkey(cur)); if (d < 0) b = cur; da = odict->cmp(odict->getkey(a), key); db = odict->cmp(key, odict->getkey(b)); if (da < 0 && db < 0) { /* Both a and b are descendants of cur */ return cur; } } return odict->root; } /** @} */