[gtk+] rbtree: Rewrite to not lose node order



commit 6d0499a5002a46600cfe95a7feb4d69d4f6dfb51
Author: Benjamin Otte <otte redhat com>
Date:   Tue Nov 22 23:15:53 2011 +0100

    rbtree: Rewrite to not lose node order
    
    _gtk_rbtree_reorder() was moving the node's data while reordering. As we
    use the node pointer in the a11y code as a hash key, this didn't work.
    
    So this rewrite changes that. As a bonus, it is less code and faster.
    Woohoo!

 gtk/gtkrbtree.c |  162 +++++++++++++++++++++++++------------------------------
 1 files changed, 74 insertions(+), 88 deletions(-)
---
diff --git a/gtk/gtkrbtree.c b/gtk/gtkrbtree.c
index ef2d735..d6dc938 100644
--- a/gtk/gtkrbtree.c
+++ b/gtk/gtkrbtree.c
@@ -767,65 +767,48 @@ _gtk_rbtree_set_fixed_height (GtkRBTree *tree,
   while ((node = _gtk_rbtree_next (tree, node)) != NULL);
 }
 
-typedef struct _GtkRBReorder
-{
-  GtkRBTree *children;
-  gint height;
-  gint flags;
-  gint order;
-  gint invert_order;
-  guint total_count;
-} GtkRBReorder;
-
-static int
-gtk_rbtree_reorder_sort_func (gconstpointer a,
-			      gconstpointer b)
+static void
+reorder_prepare (GtkRBTree *tree,
+                 GtkRBNode *node,
+                 gpointer   data)
 {
-  return ((GtkRBReorder *) a)->order - ((GtkRBReorder *) b)->order;
+  node->offset -= node->left->offset + node->right->offset;
+  GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_DESCENDANTS_INVALID);
 }
 
-static int
-gtk_rbtree_reorder_invert_func (gconstpointer a,
-				gconstpointer b)
+static void
+reorder_fixup (GtkRBTree *tree,
+               GtkRBNode *node,
+               gpointer   data)
 {
-  return ((GtkRBReorder *) a)->invert_order - ((GtkRBReorder *) b)->invert_order;
+  node->offset += node->left->offset + node->right->offset;
+  node->count = 1 + node->left->count + node->right->count;
+  _fixup_validation (tree, node);
+  _fixup_total_count (tree, node);
 }
 
 static void
-gtk_rbtree_reorder_fixup (GtkRBTree *tree,
-			  GtkRBNode *node)
+reorder_copy_node (GtkRBTree *tree,
+                   GtkRBNode *to,
+                   GtkRBNode *from)
 {
-  if (_gtk_rbtree_is_nil (node))
-    return;
-
-  node->total_count = 1;
-
-  if (!_gtk_rbtree_is_nil (node->left))
-    {
-      gtk_rbtree_reorder_fixup (tree, node->left);
-      node->offset += node->left->offset;
-      node->total_count += node->left->total_count;
-    }
-  if (!_gtk_rbtree_is_nil (node->right))
-    {
-      gtk_rbtree_reorder_fixup (tree, node->right);
-      node->offset += node->right->offset;
-      node->total_count += node->right->total_count;
-    }
-      
-  if (node->children)
-    {
-      node->offset += node->children->root->offset;
-      node->total_count += node->children->root->total_count;
-    }
-  
-  if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
-      GTK_RBNODE_FLAG_SET (node->right, GTK_RBNODE_DESCENDANTS_INVALID) ||
-      GTK_RBNODE_FLAG_SET (node->left, GTK_RBNODE_DESCENDANTS_INVALID) ||
-      (node->children && GTK_RBNODE_FLAG_SET (node->children->root, GTK_RBNODE_DESCENDANTS_INVALID)))
-    GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_DESCENDANTS_INVALID);
-  else
-    GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_DESCENDANTS_INVALID);
+  to->flags = (to->flags & GTK_RBNODE_NON_COLORS) | GTK_RBNODE_GET_COLOR (from);
+
+  to->left = from->left;
+  if (!_gtk_rbtree_is_nil (to->left))
+    to->left->parent = to;
+
+  to->right = from->right;
+  if (!_gtk_rbtree_is_nil (to->right))
+    to->right->parent = to;
+
+  to->parent = from->parent;
+  if (_gtk_rbtree_is_nil (to->parent))
+    tree->root = to;
+  else if (to->parent->left == from)
+    to->parent->left = to;
+  else if (to->parent->right == from)
+    to->parent->right = to;
 }
 
 /* It basically pulls everything out of the tree, rearranges it, and puts it
@@ -839,59 +822,62 @@ _gtk_rbtree_reorder (GtkRBTree *tree,
 		     gint      *new_order,
 		     gint       length)
 {
-  GtkRBReorder reorder = { NULL };
-  GArray *array;
+  GtkRBNode **nodes;
   GtkRBNode *node;
-  gint i;
+  gint i, j;
   
   g_return_if_fail (tree != NULL);
   g_return_if_fail (length > 0);
   g_return_if_fail (tree->root->count == length);
   
-  /* Sort the trees values in the new tree. */
-  array = g_array_sized_new (FALSE, FALSE, sizeof (GtkRBReorder), length);
-  for (i = 0; i < length; i++)
-    {
-      reorder.order = new_order[i];
-      reorder.invert_order = i;
-      g_array_append_val (array, reorder);
-    }
+  nodes = g_new (GtkRBNode *, length);
 
-  g_array_sort(array, gtk_rbtree_reorder_sort_func);
+  _gtk_rbtree_traverse (tree, tree->root, G_PRE_ORDER, reorder_prepare, NULL);
 
-  /* rewind node*/
-  node = _gtk_rbtree_first (tree);
+  for (node = _gtk_rbtree_first (tree), i = 0;
+       node;
+       node = _gtk_rbtree_next (tree, node), i++)
+    {
+      nodes[i] = node;
+    }
 
   for (i = 0; i < length; i++)
     {
-      g_assert (!_gtk_rbtree_is_nil (node));
-      g_array_index (array, GtkRBReorder, i).children = node->children;
-      g_array_index (array, GtkRBReorder, i).flags = GTK_RBNODE_NON_COLORS & node->flags;
-      g_array_index (array, GtkRBReorder, i).height = GTK_RBNODE_GET_HEIGHT (node);
+      GtkRBNode tmp = { 0, };
+      GSList *l, *cycle = NULL;
 
-      node = _gtk_rbtree_next (tree, node);
-    }
+      tmp.offset = -1;
 
-  g_array_sort (array, gtk_rbtree_reorder_invert_func);
- 
-  /* rewind node*/
-  node = _gtk_rbtree_first (tree);
+      /* already swapped */
+      if (nodes[i] == NULL)
+        continue;
+      /* no need to swap */
+      if (new_order[i] == i)
+        continue;
 
-  /* Go through the tree and change the values to the new ones. */
-  for (i = 0; i < length; i++)
-    {
-      reorder = g_array_index (array, GtkRBReorder, i);
-      node->children = reorder.children;
-      if (node->children)
-	node->children->parent_node = node;
-      node->flags = GTK_RBNODE_GET_COLOR (node) | reorder.flags;
-      /* We temporarily set the height to this. */
-      node->offset = reorder.height;
-      node = _gtk_rbtree_next (tree, node);
+      /* make a list out of the pending nodes */
+      for (j = i; new_order[j] != i; j = new_order[j])
+        {
+          cycle = g_slist_prepend (cycle, nodes[j]);
+          nodes[j] = NULL;
+        }
+
+      node = nodes[j];
+      reorder_copy_node (tree, &tmp, node);
+      for (l = cycle; l; l = l->next)
+        {
+          reorder_copy_node (tree, node, l->data);
+          node = l->data;
+        }
+
+      reorder_copy_node (tree, node, &tmp);
+      nodes[j] = NULL;
+      g_slist_free (cycle);
     }
-  gtk_rbtree_reorder_fixup (tree, tree->root);
 
-  g_array_free (array, TRUE);
+  _gtk_rbtree_traverse (tree, tree->root, G_POST_ORDER, reorder_fixup, NULL);
+
+  g_free (nodes);
 }
 
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]