aboutsummaryrefslogtreecommitdiff
path: root/src/cachetree.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cachetree.c')
-rw-r--r--src/cachetree.c484
1 files changed, 0 insertions, 484 deletions
diff --git a/src/cachetree.c b/src/cachetree.c
deleted file mode 100644
index de5a5c1..0000000
--- a/src/cachetree.c
+++ /dev/null
@@ -1,484 +0,0 @@
-/* cachetree.c - Implementation of the red-black tree for cache lookups. */
-
-/* This file is part of GDBM, the GNU data base manager.
- Copyright (C) 2019-2021 Free Software Foundation, Inc.
-
- GDBM is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
-
- GDBM is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GDBM. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "autoconf.h"
-#include "gdbmdefs.h"
-
-enum cache_node_color { RED, BLACK };
-
-struct cache_tree
-{
- cache_node *root; /* Root of the tree */
- cache_node *avail; /* List of available nodes, linked by parent field */
-};
-
-/* Allocate and return a new node. Pick the head item from the avail
- list and update the avail pointer. If the list is empty, malloc
- a new node.
- All members in the returned node are filled with 0.
-*/
-static cache_node *
-rbt_node_alloc (cache_tree *tree)
-{
- cache_node *n;
-
- n = tree->avail;
- if (n)
- tree->avail = n->parent;
- else
- {
- n = malloc (sizeof (*n));
- if (!n)
- return NULL;
- }
- memset (n, 0, sizeof (*n));
- return n;
-}
-
-/* Return the node N to the avail list in TREE. */
-static void
-rbt_node_dealloc (cache_tree *tree, cache_node *n)
-{
- n->parent = tree->avail;
- tree->avail = n;
-}
-
-/* Red-black tree properties:
- 1. Each node is either red or black.
- 2. The root node is black.
- 3. All leaves are black and contain no data.
- 4. Every red node has two children, and both are black.
- IOW, the parent of every red node is black.
- 5. All paths from any given node to its leaf nodes contain the same
- number of black nodes.
- */
-
-/* Auxiliary functions for accessing nodes. */
-
-/* Return the grandparent node of N.
- Prerequisite: N may not be root.
-*/
-static inline cache_node *
-grandparent (cache_node *n)
-{
- return n->parent->parent;
-}
-
-/* Return the sibling node of N.
- Prerequisite: N may not be root.
-*/
-static inline cache_node *
-sibling (cache_node *n)
-{
- return (n == n->parent->left) ? n->parent->right : n->parent->left;
-}
-
-/* Return the uncle node of N.
- Prerequisite: N must be at least 2 nodes away from root.
-*/
-static inline cache_node *
-uncle (cache_node *n)
-{
- return sibling (n->parent);
-}
-
-/* Returns the color of the node N.
- Empty leaves are represented by NULL, therefore NULL is assumed to
- be black (see property 3).
-*/
-static inline enum cache_node_color
-node_color (cache_node *n)
-{
- return n == NULL ? BLACK : n->color;
-}
-
-/* Replace the OLDN with NEWN.
- Does not modify OLDN. */
-static inline void
-replace_node (cache_tree *tree, cache_node *oldn, cache_node *newn)
-{
- if (oldn->parent == NULL)
- tree->root = newn;
- else if (oldn == oldn->parent->left)
- oldn->parent->left = newn;
- else
- oldn->parent->right = newn;
-
- if (newn != NULL)
- newn->parent = oldn->parent;
-}
-
-/* Rotate the TREE left over the node N. */
-static inline void
-rotate_left (cache_tree *tree, cache_node *n)
-{
- cache_node *right = n->right;
- replace_node (tree, n, right);
- n->right = right->left;
- if (right->left != NULL)
- right->left->parent = n;
- right->left = n;
- n->parent = right;
-}
-
-/* Rotate the TREE right over the node N. */
-static inline void
-rotate_right (cache_tree *tree, cache_node *n)
-{
- cache_node *left = n->left;
- replace_node (tree, n, left);
- n->left = left->right;
- if (left->right != NULL)
- left->right->parent = n;
- left->right = n;
- n->parent = left;
-}
-
-/* Node deletion */
-static inline void
-rbt_delete_fixup (cache_tree *tree, cache_node *n)
-{
- while (1)
- {
- if (n->parent == NULL)
- {
- /* If N has become the root node, deletion resulted in removing
- one black node (prior root) from every path, so all properties
- still hold.
- */
- return;
- }
- else
- {
- /* If N has a red sibling, change the colors of the parent and
- sibling and rotate about the parent. Thus, the sibling becomes
- grandparent and we can proceed to the next case.
- */
- if (node_color (sibling (n)) == RED)
- {
- n->parent->color = RED;
- sibling (n)->color = BLACK;
- if (n == n->parent->left)
- rotate_left (tree, n->parent);
- else
- rotate_right (tree, n->parent);
- }
-
- /* If the parent, sibling and nephews are all black, paint the
- sibling red. This means one black node was removed from all
- paths passing through the parent, so we recurse to the beginning
- of the loop with parent as the argument to restore the properties.
- This is the only branch that loops.
- */
- if (node_color (n->parent) == BLACK
- && node_color (sibling (n)) == BLACK
- && node_color (sibling (n)->left) == BLACK
- && node_color (sibling (n)->right) == BLACK)
- {
- sibling (n)->color = RED;
- n = n->parent;
- continue;
- }
- else
- {
- /* If the sibling and nephews are black but the parent is red,
- swap the colors of the sibling and parent. The properties
- are then restored.
- */
- if (node_color (n->parent) == RED
- && node_color (sibling (n)) == BLACK
- && node_color (sibling (n)->left) == BLACK
- && node_color (sibling (n)->right) == BLACK)
- {
- sibling (n)->color = RED;
- n->parent->color = BLACK;
- }
- else
- {
- /* N is the left child of its parent, its sibling is black,
- and the sibling's right child is black. Swap the colors
- of the sibling and its left sibling and rotate right
- over the sibling.
- */
- if (n == n->parent->left
- && node_color (sibling (n)) == BLACK
- && node_color (sibling (n)->left) == RED
- && node_color (sibling (n)->right) == BLACK)
- {
- sibling (n)->color = RED;
- sibling (n)->left->color = BLACK;
- rotate_right (tree, sibling (n));
- }
- else if (n == n->parent->right
- && node_color (sibling (n)) == BLACK
- && node_color (sibling (n)->right) == RED
- && node_color (sibling (n)->left) == BLACK)
- {
- /* The mirror case is handled similarly. */
- sibling (n)->color = RED;
- sibling (n)->right->color = BLACK;
- rotate_left (tree, sibling (n));
- }
- /* N is the left child of its parent, its sibling is black
- and the sibling's right child is red. Swap the colors
- of the parent and sibling, paint the sibling's right
- child black and rotate left at the parent. Similarly
- for the mirror case. This achieves the following:
-
- . A black node is added to all paths passing through N;
- . A black node is removed from all paths through the
- sibling's red child.
- . The latter is painted black which restores missing
- black node in all paths through the sibling's red child.
-
- Another sibling's child becomes a child of N's parent
- during the rotation and is therefore not affected.
- */
- sibling (n)->color = node_color (n->parent);
- n->parent->color = BLACK;
- if (n == n->parent->left)
- {
- sibling (n)->right->color = BLACK;
- rotate_left (tree, n->parent);
- }
- else
- {
- sibling (n)->left->color = BLACK;
- rotate_right (tree, n->parent);
- }
- }
- }
- }
- break;
- }
-}
-
-/* Remove N from the TREE. */
-void
-_gdbm_cache_tree_delete (cache_tree *tree, cache_node *n)
-{
- cache_node *child;
-
- /* If N has both left and right children, reduce the problem to
- the node with only one child. To do so, find the in-order
- predecessor of N, copy its value (elem) to N and then delete
- the predecessor. */
- if (n->left != NULL && n->right != NULL)
- {
- cache_node *p;
- for (p = n->left; p->right; p = p->right)
- ;
- n->adr = p->adr;
- n->elem = p->elem;
- n->elem->ca_node = n;
- n = p;
- }
-
- /* N has only one child. Select it. */
- child = n->left ? n->left : n->right;
- if (node_color (n) == BLACK)
- {
- n->color = node_color (child);
- rbt_delete_fixup (tree, n);
- }
- replace_node (tree, n, child);
- if (n->parent == NULL && child != NULL) /* root should be black */
- child->color = BLACK;
-
- /* Return N to the avail pool */
- rbt_node_dealloc (tree, n);
-}
-
-/* Insertion */
-static inline void
-rbt_insert_fixup (cache_tree *tree, cache_node *n)
-{
- while (1)
- {
- if (n->parent == NULL)
- {
- /* Node was inserted at the root of the tree.
- The root node must be black (property 2). Changing its color
- to black would add one black node to every path, which means
- the property 5 would remain satisfied. So we simply paint the
- node black.
- */
- n->color = BLACK;
- }
- else if (node_color (n->parent) == BLACK)
- {
- /* The node has black parent.
- All properties are satisfied. There's no need to change anything.
- */
- return;
- }
- else if (node_color (uncle (n)) == RED)
- {
- /* The uncle node is red.
- Repaint the parent and uncle black and the grandparent red. This
- would satisfy 4. However, if the grandparent is root, this would
- violate the property 2. So we repaint the grandparent by
- re-entering the fixup loop with grandparent as the node.
- This is the only branch that loops.
- */
- n->parent->color = BLACK;
- uncle (n)->color = BLACK;
- n = grandparent (n);
- n->color = RED;
- continue;
- }
- else
- {
- /* The new node is the right child of its parent and the parent is
- the left child of the grandparent. Rotate left about the parent.
- Mirror case: The new node is the left child of its parent and the
- parent is the right child of the grandparent. Rotate right about
- the parent. This fixes the properties for the rbt_insert_5.
- */
- if (n == n->parent->right && n->parent == grandparent (n)->left)
- {
- rotate_left (tree, n->parent);
- n = n->left;
- }
- else if (n == n->parent->left && n->parent == grandparent (n)->right)
- {
- rotate_right (tree, n->parent);
- n = n->right;
- }
-
- /* The new node is the left child of its parent and the parent is the
- left child of the grandparent. Rotate right about the grandparent.
- Mirror case: The new node is the right child of its parent and the
- parent
- is the right child of the grandparent. Rotate left.
- */
- n->parent->color = BLACK;
- grandparent (n)->color = RED;
- if (n == n->parent->left && n->parent == grandparent (n)->left)
- {
- rotate_right (tree, grandparent (n));
- }
- else
- {
- rotate_left (tree, grandparent (n));
- }
- }
- break;
- }
-}
-
-/* Look up the node with the given ADR.
- If found, put it in *RETVAL and return node_found.
-
- Otherwise, create a new node and insert it at the appropriate place in
- the tree. Store the address of the newly created node in *RETVAL and
- return node_new.
-
- If a new node cannot be created (memory exhausted), return node_failure.
-*/
-int
-_gdbm_cache_tree_lookup (cache_tree *tree, off_t adr, cache_node **retval)
-{
- int res;
- cache_node *node, *parent = NULL;
-
- node = tree->root;
- while (node)
- {
- if (adr == node->adr)
- break;
- parent = node;
- if (adr < node->adr)
- node = node->left;
- else
- node = node->right;
- }
-
- if (node)
- {
- res = node_found;
- }
- else
- {
- node = rbt_node_alloc (tree);
- if (!node)
- return node_failure;
- if (!parent)
- tree->root = node;
- else if (adr < parent->adr)
- parent->left = node;
- else
- parent->right = node;
- node->adr = adr;
- node->parent = parent;
- rbt_insert_fixup (tree, node);
- res = node_new;
- }
- *retval = node;
- return res;
-}
-
-/* Interface functions */
-
-/* Create a cache tree structure for the database file DBF. */
-cache_tree *
-_gdbm_cache_tree_alloc (void)
-{
- cache_tree *t = malloc (sizeof (*t));
- if (t)
- {
- t->root = NULL;
- t->avail = NULL;
- }
- return t;
-}
-
-/* Free the memory used by the TREE. */
-void
-_gdbm_cache_tree_destroy (cache_tree *tree)
-{
- cache_node *n;
-
- /* Free the allocated tree nodes. Traverse the tree as if it were
- a simple binary tree: there's no use preserving RBT properties now.
- */
- while ((n = tree->root) != NULL)
- {
- if (!n->left)
- tree->root = n->right;
- else if (!n->right)
- tree->root = n->left;
- else
- {
- cache_node *p;
- for (p = n->left; p->right; p = p->right)
- ;
- p->right = n->right;
- tree->root = n->left;
- }
- free (n);
- }
-
- /* Free the avail list */
- while ((n = tree->avail) != NULL)
- {
- tree->avail = n->parent;
- free (n);
- }
- free (tree);
-}

Return to:

Send suggestions and report system problems to the System administrator.