/* gtkrbtree.c * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library. If not, see . */ #include "config.h" #include "gtkrbtreeprivate.h" #include "gtkdebug.h" /* Define the following to print adds and removals to stdout. * The format of the printout will be suitable for addition as a new test to * testsuite/gtk/rbtree-crash.c * by just grepping the printouts from the relevant rbtree. * * This is meant to be a trivial way to add rbtree tests to the testsuite. */ #undef DUMP_MODIFICATION typedef struct _GtkRbNode GtkRbNode; struct _GtkRbTree { guint ref_count; gsize element_size; gsize augment_size; GtkRbTreeAugmentFunc augment_func; GDestroyNotify clear_func; GDestroyNotify clear_augment_func; GtkRbNode *root; }; struct _GtkRbNode { guint red :1; guint dirty :1; GtkRbNode *left; GtkRbNode *right; /* The difference between tree and parent here is that we OR the tree with 1 and because * pointers are always multiples of 4, we can know if we've stored a parent or the tree here */ union { gpointer parent_or_tree; GtkRbNode *parent; GtkRbTree *tree; }; }; #define NODE_FROM_POINTER(ptr) ((GtkRbNode *) (((guchar *) (ptr)) - sizeof (GtkRbNode))) #define NODE_TO_POINTER(node) ((node) ? ((gpointer) (((guchar *) (node)) + sizeof (GtkRbNode))) : NULL) #define NODE_TO_AUG_POINTER(tree, node) ((gpointer) ((node) ? (((guchar *) (node)) + sizeof (GtkRbNode) + (tree)->element_size) : NULL)) static inline gboolean is_root (GtkRbNode *node) { return GPOINTER_TO_SIZE (node->parent_or_tree) & 1 ? TRUE : FALSE; } static inline GtkRbNode * parent (GtkRbNode *node) { if (is_root (node)) return NULL; else return node->parent; } static GtkRbTree * tree (GtkRbNode *node) { while (!is_root (node)) node = parent (node); return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (node->tree) & ~1); } static void set_parent (GtkRbTree *tree, GtkRbNode *node, GtkRbNode *new_parent) { if (new_parent != NULL) { node->parent = new_parent; } else { node->tree = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (tree) | 1); tree->root = node; } } static inline gsize gtk_rb_node_get_size (GtkRbTree *tree) { return sizeof (GtkRbNode) + tree->element_size + tree->augment_size; } static GtkRbNode * gtk_rb_node_new (GtkRbTree *tree) { GtkRbNode *result; result = g_malloc0 (gtk_rb_node_get_size (tree)); result->red = TRUE; result->dirty = TRUE; return result; } static void gtk_rb_node_free (GtkRbTree *tree, GtkRbNode *node) { if (tree->clear_func) tree->clear_func (NODE_TO_POINTER (node)); if (tree->clear_augment_func) tree->clear_augment_func (NODE_TO_AUG_POINTER (tree, node)); g_free (node); } static void gtk_rb_node_free_deep (GtkRbTree *tree, GtkRbNode *node) { GtkRbNode *right = node->right; if (node->left) gtk_rb_node_free_deep (tree, node->left); gtk_rb_node_free (tree, node); if (right) gtk_rb_node_free_deep (tree, right); } static void gtk_rb_node_mark_dirty (GtkRbNode *node, gboolean mark_parent) { if (node->dirty) return; node->dirty = TRUE; if (mark_parent && parent (node)) gtk_rb_node_mark_dirty (parent (node), TRUE); } static void gtk_rb_node_clean (GtkRbTree *tree, GtkRbNode *node) { if (!node->dirty) return; node->dirty = FALSE; if (tree->augment_func) tree->augment_func (tree, NODE_TO_AUG_POINTER (tree, node), NODE_TO_POINTER (node), NODE_TO_POINTER (node->left), NODE_TO_POINTER (node->right)); } static GtkRbNode * gtk_rb_node_get_first (GtkRbNode *node) { while (node->left) node = node->left; return node; } static GtkRbNode * gtk_rb_node_get_last (GtkRbNode *node) { while (node->right) node = node->right; return node; } static GtkRbNode * gtk_rb_node_get_previous (GtkRbNode *node) { GtkRbNode *p; if (node->left) return gtk_rb_node_get_last (node->left); for (p = parent (node); p != NULL; p = parent (node)) { if (p->right == node) return p; node = p; } return NULL; } static GtkRbNode * gtk_rb_node_get_next (GtkRbNode *node) { GtkRbNode *p; if (node->right) return gtk_rb_node_get_first (node->right); for (p = parent (node); p != NULL; p = parent (node)) { if (p->left == node) return p; node = p; } return NULL; } #ifdef DUMP_MODIFICATION static guint position (GtkRbTree *tree, GtkRbNode *node) { GtkRbNode *n; guint i; i = 0; for (n = gtk_rb_node_get_first (tree->root); n != node; n = gtk_rb_node_get_next (n)) i++; return i; } #endif static void gtk_rb_node_rotate_left (GtkRbTree *tree, GtkRbNode *node) { GtkRbNode *right, *p; right = node->right; p = parent (node); node->right = right->left; if (right->left) set_parent (tree, right->left, node); set_parent (tree, right, p); if (p) { if (node == p->left) p->left = right; else p->right = right; } right->left = node; set_parent (tree, node, right); gtk_rb_node_mark_dirty (node, FALSE); gtk_rb_node_mark_dirty (right, FALSE); } static void gtk_rb_node_rotate_right (GtkRbTree *tree, GtkRbNode *node) { GtkRbNode *left, *p; left = node->left; p = parent (node); node->left = left->right; if (left->right) set_parent (tree, left->right, node); set_parent (tree, left, p); if (p) { if (node == p->right) p->right = left; else p->left = left; } /* link node and left */ left->right = node; set_parent (tree, node, left); gtk_rb_node_mark_dirty (node, FALSE); gtk_rb_node_mark_dirty (left, FALSE); } static gboolean is_red (GtkRbNode *node_or_null) { if (node_or_null == NULL) return FALSE; else return node_or_null->red; } static inline gboolean is_black (GtkRbNode *node_or_null) { return !is_red (node_or_null); } static void set_black (GtkRbNode *node_or_null) { if (node_or_null == NULL) return; node_or_null->red = FALSE; } static void set_red (GtkRbNode *node_or_null) { if (node_or_null == NULL) return; node_or_null->red = TRUE; } static void gtk_rb_tree_insert_fixup (GtkRbTree *tree, GtkRbNode *node) { GtkRbNode *p; /* check Red-Black properties */ for (p = parent (node); p && is_red (p); p = parent (node)) { GtkRbNode *pp = parent (p); /* we have a violation */ g_assert (pp); if (p == pp->left) { GtkRbNode *uncle = pp->right; if (is_red (uncle)) { /* uncle is red */ set_black (p); set_black (uncle); set_red (pp); node = pp; } else { /* uncle is black */ if (node == p->right) { /* make node a left child */ gtk_rb_node_rotate_left (tree, p); p = node; node = p->left; } /* recolor and rotate */ set_black (p); set_red (pp); gtk_rb_node_rotate_right (tree, pp); } } else { /* mirror image of above code */ GtkRbNode *uncle = pp->left; if (is_red (uncle)) { /* uncle is red */ set_black (p); set_black (uncle); set_red (pp); node = pp; } else { /* uncle is black */ if (node == p->left) { gtk_rb_node_rotate_right (tree, p); p = node; node = p->right; } set_black (p); set_red (pp); gtk_rb_node_rotate_left (tree, pp); } } } set_black (tree->root); } static void gtk_rb_tree_remove_node_fixup (GtkRbTree *tree, GtkRbNode *node, GtkRbNode *p) { while (node != tree->root && is_black (node)) { if (node == p->left) { GtkRbNode *w = p->right; if (is_red (w)) { set_black (w); set_red (p); gtk_rb_node_rotate_left (tree, p); w = p->right; } g_assert (w); if (is_black (w->left) && is_black (w->right)) { set_red (w); node = p; } else { if (is_black (w->right)) { set_black (w->left); set_red (w); gtk_rb_node_rotate_right (tree, w); w = p->right; } w->red = p->red; set_black (p); set_black (w->right); gtk_rb_node_rotate_left (tree, p); node = tree->root; } } else { GtkRbNode *w = p->left; if (is_red (w)) { set_black (w); set_red (p); gtk_rb_node_rotate_right (tree, p); w = p->left; } g_assert (w); if (is_black (w->right) && is_black (w->left)) { set_red (w); node = p; } else { if (is_black (w->left)) { set_black (w->right); set_red (w); gtk_rb_node_rotate_left (tree, w); w = p->left; } w->red = p->red; set_black (p); set_black (w->left); gtk_rb_node_rotate_right (tree, p); node = tree->root; } } p = parent (node); } set_black (node); } GtkRbTree * gtk_rb_tree_new_for_size (gsize element_size, gsize augment_size, GtkRbTreeAugmentFunc augment_func, GDestroyNotify clear_func, GDestroyNotify clear_augment_func) { GtkRbTree *tree; tree = g_new0 (GtkRbTree, 1); tree->ref_count = 1; tree->element_size = element_size; tree->augment_size = augment_size; tree->augment_func = augment_func; tree->clear_func = clear_func; tree->clear_augment_func = clear_augment_func; return tree; } GtkRbTree * gtk_rb_tree_ref (GtkRbTree *tree) { tree->ref_count++; return tree; } void gtk_rb_tree_unref (GtkRbTree *tree) { tree->ref_count--; if (tree->ref_count > 0) return; if (tree->root) gtk_rb_node_free_deep (tree, tree->root); g_free (tree); } gpointer gtk_rb_tree_get_first (GtkRbTree *tree) { if (tree->root == NULL) return NULL; return NODE_TO_POINTER (gtk_rb_node_get_first (tree->root)); } gpointer gtk_rb_tree_get_last (GtkRbTree *tree) { if (tree->root == NULL) return NULL; return NODE_TO_POINTER (gtk_rb_node_get_last (tree->root)); } gpointer gtk_rb_tree_node_get_previous (gpointer node) { return NODE_TO_POINTER (gtk_rb_node_get_previous (NODE_FROM_POINTER (node))); } gpointer gtk_rb_tree_node_get_next (gpointer node) { return NODE_TO_POINTER (gtk_rb_node_get_next (NODE_FROM_POINTER (node))); } gpointer gtk_rb_tree_get_root (GtkRbTree *tree) { return NODE_TO_POINTER (tree->root); } gpointer gtk_rb_tree_node_get_parent (gpointer node) { return NODE_TO_POINTER (parent (NODE_FROM_POINTER (node))); } gpointer gtk_rb_tree_node_get_left (gpointer node) { return NODE_TO_POINTER (NODE_FROM_POINTER (node)->left); } gpointer gtk_rb_tree_node_get_right (gpointer node) { return NODE_TO_POINTER (NODE_FROM_POINTER (node)->right); } gpointer gtk_rb_tree_get_augment (GtkRbTree *tree, gpointer node) { GtkRbNode *rbnode = NODE_FROM_POINTER (node); gtk_rb_node_clean (tree, rbnode); return NODE_TO_AUG_POINTER (tree, rbnode); } GtkRbTree * gtk_rb_tree_node_get_tree (gpointer node) { return tree (NODE_FROM_POINTER (node)); } void gtk_rb_tree_node_mark_dirty (gpointer node) { gtk_rb_node_mark_dirty (NODE_FROM_POINTER (node), TRUE); } gpointer gtk_rb_tree_insert_before (GtkRbTree *tree, gpointer node) { GtkRbNode *result; if (tree->root == NULL) { #ifdef DUMP_MODIFICATION g_print ("add (tree, 0); /* 0x%p */\n", tree); #endif /* DUMP_MODIFICATION */ g_assert (node == NULL); result = gtk_rb_node_new (tree); tree->root = result; } else if (node == NULL) { return gtk_rb_tree_insert_after (tree, gtk_rb_tree_get_last (tree)); } else { GtkRbNode *current = NODE_FROM_POINTER (node); #ifdef DUMP_MODIFICATION g_print ("add (tree, %u); /* 0x%p */\n", position (tree, current), tree); #endif /* DUMP_MODIFICATION */ /* setup new node */ result = gtk_rb_node_new (tree); if (current->left) { current = gtk_rb_node_get_last (current->left); current->right = result; } else { current->left = result; } set_parent (tree, result, current); gtk_rb_node_mark_dirty (current, TRUE); } gtk_rb_tree_insert_fixup (tree, result); return NODE_TO_POINTER (result); } gpointer gtk_rb_tree_insert_after (GtkRbTree *tree, gpointer node) { GtkRbNode *current, *result; if (node == NULL) return gtk_rb_tree_insert_before (tree, gtk_rb_tree_get_first (tree)); current = NODE_FROM_POINTER (node); #ifdef DUMP_MODIFICATION g_print ("add (tree, %u); /* 0x%p */\n", position (tree, current) + 1, tree); #endif /* DUMP_MODIFICATION */ /* setup new node */ result = gtk_rb_node_new (tree); if (current->right) { current = gtk_rb_node_get_first (current->right); current->left = result; } else { current->right = result; } set_parent (tree, result, current); gtk_rb_node_mark_dirty (current, TRUE); gtk_rb_tree_insert_fixup (tree, result); return NODE_TO_POINTER (result); } void gtk_rb_tree_remove (GtkRbTree *tree, gpointer node) { GtkRbNode *x, *y, *p, *real_node; real_node = NODE_FROM_POINTER (node); #ifdef DUMP_MODIFICATION g_print ("delete (tree, %u); /* 0x%p */\n", position (tree, real_node), tree); #endif /* DUMP_MODIFICATION */ y = real_node; if (y->left && y->right) { y = y->right; while (y->left) y = y->left; } /* x is y's only child, or nil */ if (y->left) x = y->left; else x = y->right; /* remove y from the parent chain */ p = parent (y); if (x != NULL) set_parent (tree, x, p); if (p) { if (y == p->left) p->left = x; else p->right = x; gtk_rb_node_mark_dirty (p, TRUE); } else { if (x == NULL) tree->root = NULL; } /* We need to clean up the validity of the tree. */ if (is_black (y)) gtk_rb_tree_remove_node_fixup (tree, x, p); if (y != real_node) { /* Move the node over */ if (is_red (real_node) != is_red (y)) y->red = !y->red; y->left = real_node->left; if (y->left) set_parent (tree, y->left, y); y->right = real_node->right; if (y->right) set_parent (tree, y->right, y); p = parent (real_node); set_parent (tree, y, p); if (p) { if (p->left == real_node) p->left = y; else p->right = y; gtk_rb_node_mark_dirty (p, TRUE); } gtk_rb_node_mark_dirty (y, TRUE); } gtk_rb_node_free (tree, real_node); } void gtk_rb_tree_remove_all (GtkRbTree *tree) { #ifdef DUMP_MODIFICATION g_print ("delete_all (tree); /* 0x%p */\n", tree); #endif /* DUMP_MODIFICATION */ if (tree->root) gtk_rb_node_free_deep (tree, tree->root); tree->root = NULL; }