Version the tree's root node, and when changing any pointer in the tree, make sure...
authorDana Jansens <danakj@orodu.net>
Thu, 29 Oct 2009 20:32:40 +0000 (16:32 -0400)
committerDana Jansens <danakj@orodu.net>
Thu, 12 Nov 2009 21:54:04 +0000 (16:54 -0500)
glib/gtree.c

index a92ec676b32f673f5c9891604d811c16f885c803..894ac6972db5e9a4a8f9acd9751d9507704a782e 100644 (file)
 #define MAX_OUT_DEGREE 3
 #define MAX_IN_DEGREE 5
 
-typedef struct _GTreeNode          GTreeNode;
-typedef struct _GTreeNodeData      GTreeNodeData;
+typedef struct _GTreeRoot          GTreeRoot;
 typedef struct _GTreeNodeVersion   GTreeNodeVersion;
+typedef struct _GTreeNodeData      GTreeNodeData;
+typedef struct _GTreeNode          GTreeNode;
+
+struct _GTreeRoot
+{
+  GTreeNode *root;
+  guint      version;
+};
 
 struct _GTree
 {
-  GTreeNode        *root;
+  GTreeRoot        *roots;
+  guint             nroots;
   GCompareDataFunc  key_compare;
   GDestroyNotify    key_destroy_func;
   GDestroyNotify    value_destroy_func;
@@ -54,6 +62,9 @@ struct _GTree
   guint             version;
 };
 
+#define root(i)         roots[i].root
+#define rootversion(i)  roots[i].version
+
 struct _GTreeNodeVersion
 {
   guint      version;
@@ -118,8 +129,10 @@ static gpointer   g_tree_node_search                (GTreeNode       *node,
                                                     gconstpointer    data,
                                                      GTreeSearchType  search_type);
 static gint       g_tree_node_height                (GTreeNode       *node);
-static GTreeNode* g_tree_node_rotate_left           (GTreeNode       *node);
-static GTreeNode* g_tree_node_rotate_right          (GTreeNode       *node);
+static GTreeNode* g_tree_node_rotate_left           (GTree           *tree,
+                                                     GTreeNode       *node);
+static GTreeNode* g_tree_node_rotate_right          (GTree           *tree,
+                                                     GTreeNode       *node);
 #ifdef G_TREE_DEBUG
 static void       g_tree_node_check                 (GTreeNode       *node);
 #endif
@@ -152,46 +165,67 @@ g_tree_node_new (GTree   *tree,
   return node;
 }
 
-static GTreeNode*
-g_tree_root_modify (GTree     *tree,
-                    GTreeNode *root)
+static void
+g_tree_root_next_version (GTree *tree)
 {
-  tree->root = root;
-  return root;
+  g_assert(tree->rootversion(NOW) <= tree->version);
+
+  if (tree->rootversion(NOW) < tree->version)
+    {
+      int i;
+
+      /* prepend a new version to the root */
+      tree->nroots++;
+      tree->roots = g_renew(GTreeRoot, tree->roots, tree->nroots);
+      for (i = 1; i < tree->nroots; ++i)
+        tree->roots[i] = tree->roots[i-1];
+      /* roots[0] will be a copy of the latest version */
+      tree->roots[0].version = tree->version;
+
+      // XXX worry about incoming pointers
+    }
 }
 
+/* You must call this on a node before you start changing its pointers,
+   or you might be changing an old (permanent) version of the node */
 static GTreeNode*
-g_tree_node_modify (GTree     *tree,
-                    GTreeNode *node,
-                    GTreeNode *left,
-                    GTreeNode *right,
-                    GTreeNode *parent,
-                    gboolean   left_child,
-                    gboolean   right_child)
+g_tree_node_next_version (GTree     *tree,
+                          GTreeNode *node)
 {
   int i;
 
-  for (i = 0; i < node->nv; ++i)
-    if (node->version(i) == tree->version)
-      break;
-  if (i == node->nv)
+  g_assert(node->version(NOW) <= tree->version);
+
+  if (node->version(NOW) == tree->version)
+    return node; /* node already has the current version */
+
+  /* if the current version is 0, then it's the first time we're making a new
+     pointer version for this node, and so its pointer table only has size
+     one (and thus is full) */
+  if (node->version(NOW) == 0 || node->nv == MAX_IN_DEGREE+1)
     {
       /* we filled the node's pointer table and need to make a new GTreeNode */
       GTreeNode *n = g_slice_new(GTreeNode);
       n->data = node->data;
-      i = 0;
-      n->v[i].version = tree->version;
+      n->v[0] = node->v[0]; /* copy the latest version to here */
+      n->v[0].version = tree->version;
       n->nv = 1;
-      node = n;
-    }
-
-  node->v[i].left = left;
-  node->v[i].right = right;
-  node->v[i].parent = parent;
-  node->v[i].left_child = left_child;
-  node->v[i].right_child = right_child;
+      return n;
 
-  return node;
+      // XXX worry about incoming pointers
+    }
+  else
+    {
+      /* prepend a version to the node's table */
+      node->nv++;
+      for (i = 1; i < node->nv; ++i)
+        node->v[i] = node->v[i-1];
+      /* v[0] will be a copy of the latest version */
+      node->v[0].version = tree->version;
+      return node;
+
+      // XXX worry about incoming pointers
+    }
 }
 
 /**
@@ -263,7 +297,8 @@ g_tree_new_full (GCompareDataFunc key_compare_func,
   g_return_val_if_fail (key_compare_func != NULL, NULL);
   
   tree = g_slice_new (GTree);
-  tree->root               = NULL;
+  tree->roots              = g_new(GTreeRoot, 1);
+  tree->nroots             = 1;
   tree->key_compare        = key_compare_func;
   tree->key_destroy_func   = key_destroy_func;
   tree->value_destroy_func = value_destroy_func;
@@ -271,6 +306,9 @@ g_tree_new_full (GCompareDataFunc key_compare_func,
   tree->nnodes             = 0;
   tree->ref_count          = 1;
   tree->version            = 0;
+
+  tree->roots[0].root      = NULL;
+  tree->roots[0].version   = 0;
   
   return tree;
 }
@@ -280,10 +318,10 @@ g_tree_first_node (GTree *tree)
 {
   GTreeNode *tmp;
 
-  if (!tree->root)
+  if (!tree->root(NOW))
     return NULL;
 
-  tmp = tree->root;
+  tmp = tree->root(NOW);
 
   while (tmp->left_child(NOW))
     tmp = tmp->left(NOW);
@@ -327,6 +365,8 @@ g_tree_remove_all (GTree *tree)
 
   g_return_if_fail (tree != NULL);
 
+  // XXX worry about removing all versions, not just the latest
+
   node = g_tree_first_node (tree);
 
   while (node)
@@ -342,7 +382,7 @@ g_tree_remove_all (GTree *tree)
       node = next;
     }
 
-  tree->root = NULL;
+  tree->roots[0].root = NULL;
   tree->nnodes = 0;
 }
 
@@ -388,6 +428,7 @@ g_tree_unref (GTree *tree)
   if (g_atomic_int_dec_and_test (&tree->ref_count))
     {
       g_tree_remove_all (tree);
+      g_free (tree->roots);
       g_slice_free (GTree, tree);
     }
 }
@@ -539,14 +580,15 @@ g_tree_insert_internal (GTree    *tree,
 
   g_return_if_fail (tree != NULL);
 
-  if (!tree->root)
+  if (!tree->root(NOW))
     {
-      g_tree_root_modify (tree, g_tree_node_new (tree, key, value));
+      g_tree_root_next_version (tree);
+      tree->roots[0].root = g_tree_node_new (tree, key, value);
       tree->nnodes++;
       return;
     }
 
-  node = tree->root;
+  node = tree->root(NOW);
 
   while (1)
     {
@@ -584,10 +626,12 @@ g_tree_insert_internal (GTree    *tree,
             {
               child = g_tree_node_new (tree, key, value);
 
-              child->v[0].left = node->v[0].left;
+              /* child is created at the current version */
+              child->v[0].left = node->left(NOW);
               child->v[0].right = node;
               child->v[0].parent = node;
 
+              node = g_tree_node_next_version (tree, node);
               node->v[0].left = child;
               node->v[0].left_child = TRUE;
 
@@ -604,10 +648,12 @@ g_tree_insert_internal (GTree    *tree,
             {
               child = g_tree_node_new (tree, key, value);
 
-              child->v[0].right = node->v[0].right;
+              /* child is created at the current version */
+              child->v[0].right = node->right(NOW);
               child->v[0].left = node;
               child->v[0].parent = node;
 
+              node = g_tree_node_next_version (tree, node);
               node->v[0].right = child;
               node->v[0].right_child = TRUE;
 
@@ -626,12 +672,15 @@ g_tree_insert_internal (GTree    *tree,
       GTreeNode *const sp = node->parent(NOW)->parent(NOW);
       GTreeNode *const p = node->parent(NOW);
       if (p->left(NOW) == node)
-        g_tree_node_rotate_right (p);
+        g_tree_node_rotate_right (tree, p);
       else
-        g_tree_node_rotate_left (p);
+        g_tree_node_rotate_left (tree, p);
 
       if (!sp)
-        g_tree_root_modify (tree, node);
+        {
+          g_tree_root_next_version (tree);
+          tree->roots[0].root = node;
+        }
       else if (sp->left(NOW) == p)
         sp->left(NOW) = node;
       else
@@ -712,10 +761,10 @@ g_tree_remove_internal (GTree         *tree,
 
   g_return_val_if_fail (tree != NULL, FALSE);
 
-  if (!tree->root)
+  if (!tree->root(NOW))
     return FALSE;
 
-  node = tree->root;
+  node = tree->root(NOW);
   parent = NULL;
   is_leftchild = FALSE;
 
@@ -756,38 +805,65 @@ g_tree_remove_internal (GTree         *tree,
         {
           /* rotate the right child up */
           if (!parent)
-            parent = g_tree_root_modify (tree, g_tree_node_rotate_left (node));
-          else if (is_leftchild)
-            parent = parent->v[0].left = g_tree_node_rotate_left (node);
+            {
+              g_tree_root_next_version(tree);
+              parent = tree->roots[0].root =
+                g_tree_node_rotate_left (tree, node);
+            }
           else
-            parent = parent->v[0].right = g_tree_node_rotate_left (node);
+            {
+              parent = g_tree_node_next_version (tree, parent);
+              if (is_leftchild)
+                parent = parent->v[0].left =
+                  g_tree_node_rotate_left (tree, node);
+              else
+                parent = parent->v[0].right =
+                  g_tree_node_rotate_left (tree, node);
+            }
           is_leftchild = TRUE;
         }
       else
         {
           /* rotate the left child up */
           if (!parent)
-            parent = g_tree_root_modify(tree, g_tree_node_rotate_right (node));
-          else if (is_leftchild)
-            parent = parent->v[0].left = g_tree_node_rotate_right (node);
+            {
+              g_tree_root_next_version(tree);
+              parent = tree->roots[0].root =
+                g_tree_node_rotate_right (tree, node);
+            }
           else
-            parent = parent->v[0].right = g_tree_node_rotate_right (node);
+            {
+              parent = g_tree_node_next_version (tree, parent);
+              if (is_leftchild)
+                parent = parent->v[0].left =
+                  g_tree_node_rotate_right (tree, node);
+              else
+                parent = parent->v[0].right =
+                  g_tree_node_rotate_right (tree, node);
+            }
           is_leftchild = FALSE;
         }
     }
 
   /* remove any pointers to the node in the treap */
   if (!parent)
-    g_tree_root_modify(tree, NULL);
-  else if (is_leftchild)
     {
-      parent->v[0].left_child = FALSE;
-      parent->v[0].left = node->v[0].left;
+      g_tree_root_next_version (tree);
+      tree->roots[0].root = NULL;
     }
   else
     {
-      parent->v[0].right_child = FALSE;
-      parent->v[0].right = node->v[0].right;
+      parent = g_tree_node_next_version (tree, parent);
+      if (is_leftchild)
+        {
+          parent->v[0].left_child = FALSE;
+          parent->v[0].left = node->left(NOW);
+        }
+      else
+        {
+          parent->v[0].right_child = FALSE;
+          parent->v[0].right = node->right(NOW);
+        }
     }
   
   if (!steal)
@@ -928,7 +1004,7 @@ g_tree_foreach (GTree         *tree,
 
   g_return_if_fail (tree != NULL);
   
-  if (!tree->root)
+  if (!tree->root(NOW))
     return;
 
   node = g_tree_first_node (tree);
@@ -966,21 +1042,21 @@ g_tree_traverse (GTree         *tree,
 {
   g_return_if_fail (tree != NULL);
 
-  if (!tree->root)
+  if (!tree->root(NOW))
     return;
 
   switch (traverse_type)
     {
     case G_PRE_ORDER:
-      g_tree_node_pre_order (tree->root, traverse_func, user_data);
+      g_tree_node_pre_order (tree->root(NOW), traverse_func, user_data);
       break;
 
     case G_IN_ORDER:
-      g_tree_node_in_order (tree->root, traverse_func, user_data);
+      g_tree_node_in_order (tree->root(NOW), traverse_func, user_data);
       break;
 
     case G_POST_ORDER:
-      g_tree_node_post_order (tree->root, traverse_func, user_data);
+      g_tree_node_post_order (tree->root(NOW), traverse_func, user_data);
       break;
     
     case G_LEVEL_ORDER:
@@ -1056,7 +1132,8 @@ g_tree_search_related (GTree          *tree,
 {
   g_return_val_if_fail (tree != NULL, NULL);
 
-  return g_tree_node_search (tree->root, search_func, user_data, search_type);
+  return g_tree_node_search (tree->root(NOW),
+                             search_func, user_data, search_type);
 }
 
 static gint
@@ -1086,7 +1163,7 @@ g_tree_height (GTree *tree)
 {
   g_return_val_if_fail (tree != NULL, 0);
 
-  return g_tree_node_height (tree->root);
+  return g_tree_node_height (tree->root(NOW));
 }
 
 /**
@@ -1113,7 +1190,7 @@ g_tree_find_node (GTree           *tree,
   GTreeNode *node, *remember;
   gint cmp;
 
-  node = tree->root;
+  node = tree->root(NOW);
   if (!node)
     return NULL;
 
@@ -1253,15 +1330,19 @@ g_tree_node_search (GTreeNode       *node,
 }
 
 static GTreeNode*
-g_tree_node_rotate_left (GTreeNode *node)
+g_tree_node_rotate_left (GTree     *tree,
+                         GTreeNode *node)
 {
   GTreeNode *right;
 
-  right = node->v[0].right;
+  right = node->right(NOW);
 
-  if (right->v[0].left_child)
+  node = g_tree_node_next_version (tree, node);
+  right = g_tree_node_next_version (tree, right);
+
+  if (right->left_child(NOW))
     {
-      node->v[0].right = right->v[0].left;
+      node->v[0].right = g_tree_node_next_version (tree, right->left(NOW));
       node->v[0].right->v[0].parent = node;
     }
   else
@@ -1271,22 +1352,26 @@ g_tree_node_rotate_left (GTreeNode *node)
       right->v[0].left_child = TRUE;
     }
   right->v[0].left = node;
-  right->v[0].parent = node->v[0].parent;
+  right->v[0].parent = node->parent(NOW);
   node->v[0].parent = right;
 
   return right;
 }
 
 static GTreeNode*
-g_tree_node_rotate_right (GTreeNode *node)
+g_tree_node_rotate_right (GTree     *tree,
+                          GTreeNode *node)
 {
   GTreeNode *left;
 
-  left = node->v[0].left;
+  left = node->left(NOW);
+
+  node = g_tree_node_next_version (tree, node);
+  left = g_tree_node_next_version (tree, left);
 
-  if (left->v[0].right_child)
+  if (left->right_child(NOW))
     {
-      node->v[0].left = left->v[0].right;
+      node->v[0].left = g_tree_node_next_version(tree, left->right(NOW));
       node->v[0].left->v[0].parent = node;
     }
   else