Make a GTree persistent.
[dana/cg-glib.git] / glib / gtree.c
index 8870a8f..34946f8 100644 (file)
 
 #undef G_TREE_DEBUG
 
-typedef struct _GTreeNode  GTreeNode;
+#define MAX_IN_DEGREE 3
+#define TABLE_SIZE (MAX_IN_DEGREE+1) /* This is the minimum size required so
+                                        that you only have a constant number of
+                                        nodes fill up their tables and
+                                        require a new node to be created at
+                                        a time. */
+
+typedef struct _GTreeRootVersion   GTreeRootVersion;
+typedef struct _GTreeNodeVersion   GTreeNodeVersion;
+typedef struct _GTreeNodeData      GTreeNodeData;
+typedef struct _GTreeNode          GTreeNode;
+
+struct _GTreeRootVersion
+{
+  GTreeNode *root;
+  guint      version;
+};
 
 struct _GTree
 {
-  GTreeNode        *root;
+  GTreeRootVersion *r; /* versioned root nodes of the tree.  r[0] is
+                          the highest (latest) version.  then r[1]..r[nr-1] are
+                          older versions in ascending order.  Use first_v(),
+                          next_v() and prev_v() to traverse the list. */
+  guint             nr;
   GCompareDataFunc  key_compare;
   GDestroyNotify    key_destroy_func;
   GDestroyNotify    value_destroy_func;
   gpointer          key_compare_data;
   guint             nnodes;
   gint              ref_count;
+  guint             version;
 };
 
-struct _GTreeNode
+struct _GTreeNodeVersion
 {
-  gpointer   key;         /* key for this node */
-  gpointer   value;       /* value stored at this node */
+  guint      version;
   GTreeNode *left;        /* left subtree */
   GTreeNode *right;       /* right subtree */
   GTreeNode *parent;      /* parent node */
-  guint8     left_child;  /* the node has a left child (else left points to the
-                             previous node in the tree) */
-  guint8     right_child; /* the node has a right child (else right points to
-                             the next node in the tree) */
 };
 
+struct _GTreeNodeData
+{
+  gpointer   key;         /* key for this node */
+  gpointer   value;       /* value stored at this node */
+  gint       ref_count;
+  guint8     stolen;      /* true if the node is stolen instead of removed */
+};
+
+struct _GTreeNode
+{
+  GTreeNodeData    *data; /* the node's permanent data */
+  GTreeNodeVersion *v;    /* versions of pointers for the node.  v[0] is the
+                             highest (latest) version.  then v[1]..v[nv-1] are
+                             older versions in ascending order.  Use first_v(),
+                             next_v() and prev_v() to traverse the list. */
+  guint             nv;   /* number of versions stored in this node */
+};
 
-static GTreeNode* g_tree_node_new                   (gpointer         key,
+static GTreeNode* g_tree_node_new                   (GTree           *tree,
+                                                     gpointer         key,
                                                     gpointer         value);
 static guint      g_tree_priority                   (GTreeNode       *node);
 static void       g_tree_insert_internal            (GTree           *tree,
@@ -74,7 +108,8 @@ static gboolean   g_tree_remove_internal            (GTree           *tree,
                                                     gboolean         steal);
 static GTreeNode *g_tree_find_node                  (GTree           *tree,
                                                     gconstpointer    key,
-                                                     GTreeSearchType  search_type);
+                                                     GTreeSearchType  search_type,
+                                                     guint            version);
 static gint       g_tree_node_pre_order             (GTreeNode       *node,
                                                     GTraverseFunc    traverse_func,
                                                     gpointer         data);
@@ -87,31 +122,89 @@ static gint       g_tree_node_post_order            (GTreeNode       *node,
 static gpointer   g_tree_node_search                (GTreeNode       *node,
                                                     GCompareFunc     search_func,
                                                     gconstpointer    data,
-                                                     GTreeSearchType  search_type);
+                                                     GTreeSearchType  search_type,
+                                                     guint            version);
 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);
+static void       g_tree_node_check                 (GTreeNode       *node,
+                                                     guint            version);
 #endif
 
 static GTreeNode*
-g_tree_node_new (gpointer key,
+g_tree_node_new (GTree   *tree,
+                 gpointer key,
                 gpointer value)
 {
   GTreeNode *node = g_slice_new (GTreeNode);
 
-  node->left = NULL;
-  node->right = NULL;
-  node->parent = NULL;
-  node->left_child = FALSE;
-  node->right_child = FALSE;
-  node->key = key;
-  node->value = value;
+  node->data = g_slice_new(GTreeNodeData);
+  node->data->key = key;
+  node->data->value = value;
+  node->data->ref_count = 1;
+  node->data->stolen = FALSE;
+
+  /* for the version 0, only allocate one set of pointers for a node,
+     so that we don't use a lot more memory when persistence isn't even
+     being used */
+  node->v = g_slice_alloc(sizeof(GTreeNodeVersion) *
+                          (tree->version == 0 ? 1 : TABLE_SIZE));
+  node->nv = 1;
+
+  node->v[0].version = tree->version;
+  node->v[0].left = NULL;
+  node->v[0].right = NULL;
+  node->v[0].parent = NULL;
 
   return node;
 }
 
+static gboolean
+g_tree_node_data_unref (GTree *tree,
+                        GTreeNode *node)
+{
+  /* free the node's data if it's the last node which is using it. just
+     because the version of the node was created in the latest version
+     of the tree, doesn't mean there aren't older versions around still */
+  if (g_atomic_int_dec_and_test (&node->data->ref_count))
+    {
+      if (!node->data->stolen)
+        {
+          if (tree->key_destroy_func)
+            tree->key_destroy_func (node->data->key);
+          if (tree->value_destroy_func)
+            tree->value_destroy_func (node->data->value);
+        }
+      g_slice_free (GTreeNodeData, node->data);
+      return TRUE;
+    }
+  return FALSE;
+}
+
+static gboolean
+g_tree_node_free (GTree     *tree,
+                  GTreeNode *node)
+{
+  gboolean data_free;
+
+  if (node->data)
+    data_free = g_tree_node_data_unref (tree, node);
+  else
+    data_free = FALSE;
+
+  g_slice_free1 (sizeof(GTreeNodeVersion) *
+                 (node->v[0].version == 0 ? 1 : TABLE_SIZE),
+                 node->v);
+  node->v = NULL;
+  node->data = NULL;
+  g_slice_free (GTreeNode, node);
+
+  return data_free;
+}
+
 /**
  * g_tree_new:
  * @key_compare_func: the function used to order the nodes in the #GTree.
@@ -181,85 +274,218 @@ 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->r                  = g_new(GTreeRootVersion, 1);
+  tree->nr                 = 1;
   tree->key_compare        = key_compare_func;
   tree->key_destroy_func   = key_destroy_func;
   tree->value_destroy_func = value_destroy_func;
   tree->key_compare_data   = key_compare_data;
   tree->nnodes             = 0;
   tree->ref_count          = 1;
+  tree->version            = 0;
+
+  tree->r[0].root          = NULL;
+  tree->r[0].version       = 0;
   
   return tree;
 }
 
+/* finds the largest version in the array, that is <= the query version */
+static inline GTreeRootVersion *
+g_tree_root_find_version (GTree *tree,
+                          guint  version)
+{
+  GTreeRootVersion *const v = tree->r;
+  const guint nv = tree->nr;
+  GTreeRootVersion *l, *r;
+
+  /* note: we never search for a version smaller than the smallest
+     version in the array */
+
+  if (v[0].version <= version)
+    return v;
+
+  l = v+1;
+  r = v+(nv-1);
+  while (l <= r) /* binary search */
+    {
+      GTreeRootVersion *const m = l + (r - l) / 2;
+      if (version == m->version)
+        return m;
+      else if (version < m->version)
+        r = m-1;
+      else
+        l = m+1;
+    }
+  g_assert (r->version < version);
+  /* When searching for the second last root (the last position in the array),
+     l will be off the end of the list */
+  g_assert (l == v+nv || l->version > version);
+  return r;
+}
+
+static inline GTreeNodeVersion *
+g_tree_node_find_version(GTreeNode *node,
+                         guint      version)
+{
+  GTreeNodeVersion *const v = node->v;
+  const guint nv = node->nv;
+  GTreeNodeVersion *n;
+
+  /* note: we never search for a version smaller than the smallest
+     version in the array */
+
+  if (v[0].version <= version)
+    return v;
+
+  /* there are at most TABLE_SIZE things to look through, which is small,
+     so just scan through them from largest version to smallest */
+  for (n = v+(nv-1); n != v; --n)
+    if (n->version <= version)
+      break;
+  g_assert (n != v);
+  return n;
+}
+
 static inline GTreeNode *
-g_tree_first_node (GTree *tree)
+g_tree_first_node (GTree *tree,
+                   guint  version)
 {
   GTreeNode *tmp;
+  GTreeNodeVersion *tmpv;
+  GTreeRootVersion *rv;
 
-  if (!tree->root)
+  rv = g_tree_root_find_version (tree, version);
+  if (!rv->root)
     return NULL;
 
-  tmp = tree->root;
+  tmpv = g_tree_node_find_version (tmp = rv->root, version);
 
-  while (tmp->left_child)
-    tmp = tmp->left;
+  while (tmpv->left)
+    tmpv = g_tree_node_find_version (tmp = tmpv->left, version);
 
   return tmp;
 } 
 
 static inline GTreeNode *
-g_tree_node_previous (GTreeNode *node)
+g_tree_node_previous (GTreeNode *node,
+                      guint      version)
 {
   GTreeNode *tmp;
+  GTreeNodeVersion *nodev, *tmpv;
 
-  tmp = node->left;
-
-  if (node->left_child)
-    while (tmp->right_child)
-      tmp = tmp->right;
+  nodev = g_tree_node_find_version (node, version);
 
+  if (nodev->left)
+    {
+      tmpv = g_tree_node_find_version (tmp = nodev->left, version);
+      while (tmpv->right)
+        tmpv = g_tree_node_find_version (tmp = tmpv->right, version);
+    }
+  else
+    {
+      tmp = nodev->parent;
+      while (tmp)
+        {
+          tmpv = g_tree_node_find_version (tmp, version);
+          if (tmpv->right == node)
+              break;
+          node = tmp;
+          tmp = tmpv->parent;
+        }
+    }
   return tmp;
 }
                  
 static inline GTreeNode *
-g_tree_node_next (GTreeNode *node)
+g_tree_node_next (GTreeNode *node,
+                  guint      version)
 {
   GTreeNode *tmp;
+  GTreeNodeVersion *nodev, *tmpv;
 
-  tmp = node->right;
-
-  if (node->right_child)
-    while (tmp->left_child)
-      tmp = tmp->left;
+  nodev = g_tree_node_find_version (node, version);
 
+  if (nodev->right)
+    {
+      tmpv = g_tree_node_find_version (tmp = nodev->right, version);
+      while (tmpv->left)
+        tmpv = g_tree_node_find_version (tmp = tmpv->left, version);
+    }
+  else
+    {
+      tmp = nodev->parent;
+      while (tmp)
+        {
+          tmpv = g_tree_node_find_version (tmp, version);
+          if (tmpv->left == node)
+              break;
+          node = tmp;
+          tmp = tmpv->parent;
+        }
+    }
   return tmp;
 }
 
-static void
-g_tree_remove_all (GTree *tree)
+static inline void
+g_tree_node_free_all (GTree *tree, GTreeNode *node)
 {
-  GTreeNode *node;
-  GTreeNode *next;
-
-  g_return_if_fail (tree != NULL);
+  guint i;
 
-  node = g_tree_first_node (tree);
+  if (!node)
+    return;
 
-  while (node)
+  for (i = 0; i < node->nv; ++i)
     {
-      next = g_tree_node_next (node);
+      g_tree_node_free_all (tree, node->v[i].parent);
+      g_tree_node_free_all (tree, node->v[i].left);
+      g_tree_node_free_all (tree, node->v[i].right);
+    }
+
+  g_tree_node_free (tree, node);
+}
 
-      if (tree->key_destroy_func)
-       tree->key_destroy_func (node->key);
-      if (tree->value_destroy_func)
-       tree->value_destroy_func (node->value);
-      g_slice_free (GTreeNode, node);
+static inline GTreeNode *
+g_tree_node_decompose (GTree     *tree,
+                       GTreeNode *node)
+{
+  guint i;
 
-      node = next;
+  if (!node || !node->data)
+    return NULL;
+
+  g_tree_node_data_unref (tree, node);
+  node->data = NULL;
+
+  for (i = 0; i < node->nv; ++i)
+    {
+      node->v[i].parent = g_tree_node_decompose (tree, node->v[i].parent);
+      node->v[i].left = g_tree_node_decompose (tree, node->v[i].left);
+      node->v[i].right = g_tree_node_decompose (tree, node->v[i].right);
     }
 
-  tree->root = NULL;
+  return node;
+}
+
+static void
+g_tree_remove_all (GTree *tree)
+{
+  guint i;
+
+  g_return_if_fail (tree != NULL);
+
+  /* decompose the graph into individual trees rooted at the various
+     root pointers in the graph, so that there are no cycles while we are
+     freeing them */
+  for (i = 0; i < tree->nr; ++i)
+    tree->r[i].root = g_tree_node_decompose (tree, tree->r[i].root);
+
+  /* free everything */
+  for (i = 0; i < tree->nr; ++i)
+    g_tree_node_free_all (tree, tree->r[i].root);
+
+  tree->r[0].root = NULL;
+  tree->r[0].version = tree->version = 0;
   tree->nnodes = 0;
 }
 
@@ -305,6 +531,7 @@ g_tree_unref (GTree *tree)
   if (g_atomic_int_dec_and_test (&tree->ref_count))
     {
       g_tree_remove_all (tree);
+      g_free (tree->r);
       g_slice_free (GTree, tree);
     }
 }
@@ -330,6 +557,180 @@ g_tree_destroy (GTree *tree)
 }
 
 /**
+ * g_tree_current_version:
+ * @tree: a #GTree
+ *
+ * Returns the current version number of the #GTree.  Inserting or deleting
+ * keys will affect the current version, but not change earlier versions.
+ *
+ * Returns: the current version number of the #GTree.
+ **/
+guint
+g_tree_current_version (GTree *tree)
+{
+  return tree->version;
+}
+
+/**
+ * g_tree_next_version:
+ * @tree: a #GTree
+ *
+ * Increments the version number of the tree.  Inserting or deleting keys will
+ * affect the new version of the tree, but not change earlier versions.
+ *
+ * Returns: the new current version number of the #GTree.
+ **/
+guint
+g_tree_next_version (GTree *tree)
+{
+  return ++tree->version;
+}
+
+/* Make a new root pointer if the current one is not for the current version
+   of the tree */
+static void
+g_tree_root_next_version (GTree *tree)
+{
+  g_assert(tree->r[0].version <= tree->version);
+
+  if (tree->r[0].version < tree->version)
+    {
+      /* add a new version of the root */
+      tree->nr++;
+      tree->r = g_renew(GTreeRootVersion, tree->r, tree->nr);
+      /* copy the latest version from r[0] */
+      tree->r[tree->nr-1] = tree->r[0];
+      tree->r[0].version = tree->version;
+    }
+}
+
+/* add a new version of pointers to a node.  return TRUE if ok, and FALSE
+   if there is no room to do so. */
+static inline GTreeNode *
+g_tree_node_add_version (GTree     *tree,
+                         GTreeNode *node)
+{
+  if (!node)
+    return NULL;
+
+  /* node already has the current version */
+  if (node->v[0].version == tree->version)
+    return node;
+  /* if we filled the node's pointer table and need to make a new GTreeNode */
+  else if (node->v[0].version == 0 || node->nv >= TABLE_SIZE)
+    {
+      GTreeNode *newnode = g_slice_new(GTreeNode);
+      newnode->v = g_slice_alloc(sizeof(GTreeNodeVersion) * TABLE_SIZE);
+      newnode->data = node->data;
+      g_atomic_int_inc (&newnode->data->ref_count);
+      newnode->v[0] = node->v[0]; /* copy the latest version to here */
+      newnode->v[0].version = tree->version;
+      newnode->nv = 1;
+      return newnode;
+    }
+  /* there is space left in the node's pointer table for a new version */
+  else
+    {
+      node->nv++;
+      /* copy the latest version from v[0] */
+      node->v[node->nv-1] = node->v[0];
+      node->v[0].version = tree->version;
+      return node;
+    }
+}
+
+static inline void
+g_tree_node_fix_incoming (GTree     *tree,
+                          GTreeNode *oldnode,
+                          GTreeNode *newnode)
+{
+  GTreeNode *oldparent, *oldleft, *oldright;
+  GTreeNode *newparent, *newleft, *newright;
+
+  if (oldnode == newnode) return;
+
+  g_assert (oldnode != NULL);
+  g_assert (newnode != NULL);
+
+  /* find incoming edges to the old version of the node */
+  oldparent = newnode->v[0].parent;
+  if (oldparent && oldparent->v[0].left != oldnode &&
+      oldparent->v[0].right != oldnode)
+    oldparent = NULL;
+  oldleft = newnode->v[0].left;
+  if (oldleft && oldleft->v[0].parent != oldnode)
+    oldleft = NULL;
+  oldright = newnode->v[0].right;
+  if (oldright && oldright->v[0].parent != oldnode)
+    oldright = NULL;
+
+  /* add new pointers for nodes that point to me, possibly creating new
+     versions of the nodes */
+  newparent = g_tree_node_add_version (tree, oldparent);
+  newleft = g_tree_node_add_version (tree, oldleft);
+  newright = g_tree_node_add_version (tree, oldright);
+
+  /* 1. update my pointers to point to the new versions (if a prev node
+     points to me, then i'm its next node, and its my parent or
+     it is the rightmost node in my left subtree. case a, i update my
+     pointer to it already.  case b, if it's my left child i update
+     my pointer to it already, otherwise i don't have a pointer to it,
+     because i do have a left subtree.
+  */
+  if (newparent != oldparent)
+    newnode->v[0].parent = newparent;
+  if (newleft != oldleft)
+    newnode->v[0].left = newleft;
+  if (newright != oldright)
+    newnode->v[0].right = newright;
+
+  /* 2. update my incoming nodes to point back to the new version of me */
+  if (newparent)
+    {
+      if (newparent->v[0].left == oldnode)
+        newparent->v[0].left = newnode;
+      else
+        newparent->v[0].right = newnode;
+    }
+  if (newleft)
+    newleft->v[0].parent = newnode;
+  if (newright)
+    newright->v[0].parent = newnode;
+
+  /* 3. recurse on each of the incoming nodes if they had to create a new
+     version */
+  g_tree_node_fix_incoming (tree, oldparent, newparent);
+  g_tree_node_fix_incoming (tree, oldleft, newleft);
+  g_tree_node_fix_incoming (tree, oldright, newright);
+
+  /* 4. if this was the root node, then the root pointer into the tree needs
+     be updated */
+  if (tree->r[0].root == oldnode)
+    {
+      g_tree_root_next_version (tree);
+      tree->r[0].root = newnode;
+    }
+}
+
+/* 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_next_version (GTree     *tree,
+                          GTreeNode *oldnode)
+{
+  GTreeNode *newnode;
+
+  if (!oldnode)
+    return NULL;
+
+  g_assert(oldnode->v[0].version <= tree->version);
+
+  newnode = g_tree_node_add_version (tree, oldnode);
+  g_tree_node_fix_incoming (tree, oldnode, newnode);
+  return newnode;
+}
+
+/**
  * g_tree_insert:
  * @tree: a #GTree.
  * @key: the key to insert.
@@ -354,7 +755,11 @@ g_tree_insert (GTree    *tree,
   g_tree_insert_internal (tree, key, value, FALSE);
 
 #ifdef G_TREE_DEBUG
-  g_tree_node_check (tree->root);
+  {
+    guint i;
+    for (i = 0; i <= tree->version; ++i)
+      g_tree_node_check (g_tree_root_find_version (tree, i)->root, i);
+  }
 #endif
 }
 
@@ -384,7 +789,11 @@ g_tree_replace (GTree    *tree,
   g_tree_insert_internal (tree, key, value, TRUE);
 
 #ifdef G_TREE_DEBUG
-  g_tree_node_check (tree->root);
+  {
+    guint i;
+    for (i = 0; i <= tree->version; ++i)
+      g_tree_node_check (g_tree_root_find_version (tree, i)->root, i);
+  }
 #endif
 }
 
@@ -396,7 +805,7 @@ g_tree_replace (GTree    *tree,
 static guint
 g_tree_priority (GTreeNode *node)
 {
-  guint key = GPOINTER_TO_UINT (node);
+  guint key = GPOINTER_TO_UINT (node->data);
 
   /* This hash function is based on one found on Thomas Wang's
    * web page at
@@ -415,7 +824,8 @@ g_tree_priority (GTreeNode *node)
   return key? key : 1;
 }
 
-/* internal insert routine */
+/* Internal insert routine.  Always inserts into the current version of the
+   tree. */
 static void
 g_tree_insert_internal (GTree    *tree,
                         gpointer  key,
@@ -426,32 +836,34 @@ g_tree_insert_internal (GTree    *tree,
 
   g_return_if_fail (tree != NULL);
 
-  if (!tree->root)
+  if (!tree->r[0].root)
     {
-      tree->root = g_tree_node_new (key, value);
+      g_tree_root_next_version (tree);
+      tree->r[0].root = g_tree_node_new (tree, key, value);
       tree->nnodes++;
       return;
     }
 
-  node = tree->root;
+  node = tree->r[0].root;
 
   while (1)
     {
-      gint cmp = tree->key_compare (key, node->key, tree->key_compare_data);
+      gint cmp = tree->key_compare (key, node->data->key,
+                                    tree->key_compare_data);
       
       if (cmp == 0)
         {
           if (tree->value_destroy_func)
-            tree->value_destroy_func (node->value);
+            tree->value_destroy_func (node->data->value);
 
-          node->value = value;
+          node->data->value = value;
 
           if (replace)
             {
               if (tree->key_destroy_func)
-                tree->key_destroy_func (node->key);
+                tree->key_destroy_func (node->data->key);
 
-              node->key = key;
+              node->data->key = key;
             }
           else
             {
@@ -464,18 +876,15 @@ g_tree_insert_internal (GTree    *tree,
         }
       else if (cmp < 0)
         {
-          if (node->left_child)
-              node = node->left;
+          if (node->v[0].left)
+              node = node->v[0].left;
           else
             {
-              child = g_tree_node_new (key, value);
+              child = g_tree_node_new (tree, key, value);
+              node = g_tree_node_next_version (tree, node);
 
-              child->left = node->left;
-              child->right = node;
-              child->parent = node;
-
-              node->left = child;
-              node->left_child = TRUE;
+              child->v[0].parent = node;
+              node->v[0].left = child;
 
              tree->nnodes++;
 
@@ -484,18 +893,15 @@ g_tree_insert_internal (GTree    *tree,
         }
       else
         {
-          if (node->right_child)
-              node = node->right;
+          if (node->v[0].right)
+              node = node->v[0].right;
           else
             {
-              child = g_tree_node_new (key, value);
-
-              child->right = node->right;
-              child->left = node;
-              child->parent = node;
+              child = g_tree_node_new (tree, key, value);
+              node = g_tree_node_next_version (tree, node);
 
-              node->right = child;
-              node->right_child = TRUE;
+              child->v[0].parent = node;
+              node->v[0].right = child;
 
              tree->nnodes++;
 
@@ -506,22 +912,29 @@ g_tree_insert_internal (GTree    *tree,
 
   /* rotate the new node up until the heap property is restored */
   node = child;
-  while (node->parent &&
-         g_tree_priority (node) < g_tree_priority (node->parent))
+  while (node->v[0].parent &&
+         g_tree_priority (node) < g_tree_priority (node->v[0].parent))
     {
-      GTreeNode *const sp = node->parent->parent;
-      GTreeNode *const p = node->parent;
-      if (p->left == node)
-        g_tree_node_rotate_right (p);
+      GTreeNode *sp, *p;
+
+      /* we'll be changing both of these nodes */
+      p = g_tree_node_next_version (tree, node->v[0].parent);
+      sp = g_tree_node_next_version (tree, p->v[0].parent);
+
+      if (p->v[0].left == node)
+        node = g_tree_node_rotate_right (tree, p);
       else
-        g_tree_node_rotate_left (p);
+        node = g_tree_node_rotate_left (tree, p);
 
       if (!sp)
-        tree->root = node;
-      else if (sp->left == p)
-        sp->left = node;
+        {
+          g_tree_root_next_version (tree);
+          tree->r[0].root = node;
+        }
+      else if (sp->v[0].left == p)
+        sp->v[0].left = node;
       else
-        sp->right = node;
+        sp->v[0].right = node;
     }
 }
 
@@ -535,10 +948,13 @@ g_tree_insert_internal (GTree    *tree,
  * If the #GTree was created using g_tree_new_full(), the key and value 
  * are freed using the supplied destroy functions, otherwise you have to 
  * make sure that any dynamically allocated values are freed yourself.
+ * Note that if the key existed in
+ * earlier versions of the tree (g_tree_next_version() has been called since
+ * it was inserted), then it cannot be removed from the tree. *
  * If the key does not exist in the #GTree, the function does nothing.
  *
- * Returns: %TRUE if the key was found (prior to 2.8, this function returned 
- *   nothing)
+ * Returns: %TRUE if the key was found and able to be removed
+ *   (prior to 2.8, this function returned nothing)
  **/
 gboolean
 g_tree_remove (GTree         *tree,
@@ -551,7 +967,11 @@ g_tree_remove (GTree         *tree,
   removed = g_tree_remove_internal (tree, key, FALSE);
 
 #ifdef G_TREE_DEBUG
-  g_tree_node_check (tree->root);
+  {
+    guint i;
+    for (i = 0; i <= tree->version; ++i)
+      g_tree_node_check (g_tree_root_find_version (tree, i)->root, i);
+  }
 #endif
 
   return removed;
@@ -563,12 +983,13 @@ g_tree_remove (GTree         *tree,
  * @key: the key to remove.
  * 
  * Removes a key and its associated value from a #GTree without calling 
- * the key and value destroy functions.
- *
+ * the key and value destroy functions.  Note that if the key existed in
+ * earlier versions of the tree (g_tree_next_version() has been called since
+ * it was inserted), then it cannot be removed from the tree. *
  * If the key does not exist in the #GTree, the function does nothing.
  *
- * Returns: %TRUE if the key was found (prior to 2.8, this function returned 
- *    nothing)
+ * Returns: %TRUE if the key was found and able to be removed
+ *   (prior to 2.8, this function returned nothing)
  **/
 gboolean
 g_tree_steal (GTree         *tree,
@@ -581,7 +1002,11 @@ g_tree_steal (GTree         *tree,
   removed = g_tree_remove_internal (tree, key, TRUE);
 
 #ifdef G_TREE_DEBUG
-  g_tree_node_check (tree->root);
+  {
+    guint i;
+    for (i = 0; i <= tree->version; ++i)
+      g_tree_node_check (g_tree_root_find_version (tree, i)->root, i);
+  }
 #endif
 
   return removed;
@@ -595,98 +1020,144 @@ g_tree_remove_internal (GTree         *tree,
 {
   GTreeNode *node, *parent;
   gboolean is_leftchild;
+  gboolean data_free;
 
   g_return_val_if_fail (tree != NULL, FALSE);
 
-  if (!tree->root)
+  if (!tree->r[0].root)
     return FALSE;
 
-  node = tree->root;
+  node = tree->r[0].root;
   parent = NULL;
   is_leftchild = FALSE;
 
   while (1)
     {
-      gint cmp = tree->key_compare (key, node->key, tree->key_compare_data);
+      gint cmp = tree->key_compare (key, node->data->key,
+                                    tree->key_compare_data);
       
       if (cmp == 0)
         break;
       else if (cmp < 0)
         {
-          if (!node->left_child)
+          if (!node->v[0].left)
             return FALSE;
 
          parent = node;
           is_leftchild = TRUE;
-         node = node->left;
+         node = node->v[0].left;
         }
       else
         {
-          if (!node->right_child)
+          if (!node->v[0].right)
             return FALSE;
 
          parent = node;
           is_leftchild = FALSE;
-         node = node->right;
+         node = node->v[0].right;
         }
     }
 
   /* rotate the node down the tree, maintaining the heap property */
-  while (node->left_child || node->right_child)
+  while (node->v[0].left || node->v[0].right)
     {
-      if (!node->left_child ||
-          (node->right_child &&
-           g_tree_priority (node->left) > g_tree_priority (node->right)))
+      /* we're changing this node, make sure our pointer will stay valid
+         when we rotate it */
+      node = g_tree_node_next_version (tree, node);
+      /* getting the next version for the node may change where its parent
+         lies, so get a new pointer for it, from the node */
+      parent = node->v[0].parent;
+
+      if (!node->v[0].left ||
+          (node->v[0].right &&
+           g_tree_priority (node->v[0].left) >
+           g_tree_priority (node->v[0].right)))
         {
           /* rotate the right child up */
           if (!parent)
-            parent = tree->root = g_tree_node_rotate_left (node);
-          else if (is_leftchild)
-            parent = parent->left = g_tree_node_rotate_left (node);
+            {
+              g_tree_root_next_version (tree);
+              parent = tree->r[0].root =
+                g_tree_node_rotate_left (tree, node);
+            }
           else
-            parent = parent->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 = tree->root = g_tree_node_rotate_right (node);
-          else if (is_leftchild)
-            parent = parent->left = g_tree_node_rotate_right (node);
+            {
+              g_tree_root_next_version (tree);
+              parent = tree->r[0].root =
+                g_tree_node_rotate_right (tree, node);
+            }
           else
-            parent = parent->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)
-    tree->root = NULL;
-  else if (is_leftchild)
     {
-      parent->left_child = FALSE;
-      parent->left = node->left;
+      g_tree_root_next_version (tree);
+      tree->r[0].root = NULL;
     }
   else
     {
-      parent->right_child = FALSE;
-      parent->right = node->right;
+      /* make a new version of the parent to cut the child off.
+         making a new version of the parent may make a new version of
+         the node being deleted, which is the one we'd want to free then
+         instead, so update the node pointer */
+      parent = g_tree_node_next_version (tree, parent);
+      if (is_leftchild)
+        {
+          node = parent->v[0].left;
+          parent->v[0].left = NULL;
+        }
+      else
+        {
+          node = parent->v[0].right;
+          parent->v[0].right = NULL;
+        }
     }
-  
-  if (!steal)
+
+  tree->nnodes--;
+
+  if (node->v[0].version == tree->version)
     {
-      if (tree->key_destroy_func)
-        tree->key_destroy_func (node->key);
-      if (tree->value_destroy_func)
-        tree->value_destroy_func (node->value);
+      /* remove the entry from the node's pointer table */
+      node->v[0] = node->v[node->nv-1];
+      --node->nv;
     }
 
-  g_slice_free (GTreeNode, node);
+  node->data->stolen = steal;
+  data_free = FALSE;
 
-  tree->nnodes--;
+  /* only really delete the node if it was only in the current version,
+     otherwise it needs to be remembered */
+  if (node->nv == 0)
+    data_free = g_tree_node_free (tree, node);
 
-  return TRUE;
+  return data_free;
 }
 
 /**
@@ -705,7 +1176,7 @@ gpointer
 g_tree_lookup (GTree         *tree,
               gconstpointer  key)
 {
-  return g_tree_lookup_related (tree, key, G_TREE_SEARCH_EXACT);
+  return g_tree_lookup_related (tree, key, G_TREE_SEARCH_EXACT, tree->version);
 }
 
 /**
@@ -715,6 +1186,8 @@ g_tree_lookup (GTree         *tree,
  * @search_type: the search behavior if the @key is not present in the #GTree,
  *   one of %G_TREE_SEARCH_EXACT, %G_TREE_SEARCH_SUCCESSOR, and
  *   %G_TREE_SEARCH_PREDECESSOR.
+ * @version: the version of the tree within which to search.  If
+ *   g_tree_next_version() has not been used, then this is 0.
  *
  * Gets a value corresponding to the given key.
  *
@@ -738,15 +1211,17 @@ g_tree_lookup (GTree         *tree,
 gpointer
 g_tree_lookup_related  (GTree          *tree,
                         gconstpointer   key,
-                        GTreeSearchType search_type)
+                        GTreeSearchType search_type,
+                        guint           version)
 {
   GTreeNode *node;
 
   g_return_val_if_fail (tree != NULL, NULL);
+  g_return_val_if_fail (version <= tree->version, NULL);
 
-  node = g_tree_find_node (tree, key, search_type);
+  node = g_tree_find_node (tree, key, search_type, version);
   
-  return node ? node->value : NULL;
+  return node ? node->data->value : NULL;
 }
 
 /**
@@ -773,14 +1248,14 @@ g_tree_lookup_extended (GTree         *tree,
   
   g_return_val_if_fail (tree != NULL, FALSE);
   
-  node = g_tree_find_node (tree, lookup_key, G_TREE_SEARCH_EXACT);
+  node = g_tree_find_node (tree, lookup_key, G_TREE_SEARCH_EXACT, tree->version);
   
   if (node)
     {
       if (orig_key)
-        *orig_key = node->key;
+        *orig_key = node->data->key;
       if (value)
-        *value = node->value;
+        *value = node->data->value;
       return TRUE;
     }
   else
@@ -812,17 +1287,17 @@ g_tree_foreach (GTree         *tree,
 
   g_return_if_fail (tree != NULL);
   
-  if (!tree->root)
+  if (!tree->r[0].root)
     return;
 
-  node = g_tree_first_node (tree);
+  node = g_tree_first_node (tree, tree->version);
   
   while (node)
     {
-      if ((*func) (node->key, node->value, user_data))
+      if ((*func) (node->data->key, node->data->value, user_data))
        break;
       
-      node = g_tree_node_next (node);
+      node = g_tree_node_next (node, tree->version);
     }
 }
 
@@ -850,21 +1325,21 @@ g_tree_traverse (GTree         *tree,
 {
   g_return_if_fail (tree != NULL);
 
-  if (!tree->root)
+  if (!tree->r[0].root)
     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->r[0].root, 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->r[0].root, 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->r[0].root, traverse_func, user_data);
       break;
     
     case G_LEVEL_ORDER:
@@ -940,7 +1415,9 @@ 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->r[0].root,
+                             search_func, user_data, search_type,
+                             tree->version);
 }
 
 static gint
@@ -948,8 +1425,8 @@ g_tree_node_height(GTreeNode *node)
 {
   gint l = 0, r = 0;
   if (node == NULL) return 0;
-  if (node->left_child) l = g_tree_node_height (node->left);
-  if (node->right_child) r = g_tree_node_height (node->right);
+  if (node->v[0].left) l = g_tree_node_height (node->v[0].left);
+  if (node->v[0].right) r = g_tree_node_height (node->v[0].right);
   return 1 + MAX(l, r);
 }
 
@@ -970,16 +1447,16 @@ 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->r[0].root);
 }
 
 /**
  * g_tree_nnodes:
  * @tree: a #GTree.
  * 
- * Gets the number of nodes in a #GTree.
+ * Gets the number of nodes in the current version of a #GTree.
  * 
- * Return value: the number of nodes in the #GTree.
+ * Return value: the number of nodes in the latest version of the #GTree.
  **/
 gint
 g_tree_nnodes (GTree *tree)
@@ -992,38 +1469,43 @@ g_tree_nnodes (GTree *tree)
 static GTreeNode *
 g_tree_find_node (GTree           *tree,
                   gconstpointer    key,
-                  GTreeSearchType  search_type)
+                  GTreeSearchType  search_type,
+                  guint            version)
 {
   GTreeNode *node, *remember;
+  GTreeRootVersion *rv;
   gint cmp;
 
-  node = tree->root;
+  rv = g_tree_root_find_version (tree, version);
+  node = rv->root;
   if (!node)
     return NULL;
 
   remember = NULL;
   while (1)
     {
-      cmp = tree->key_compare (key, node->key, tree->key_compare_data);
+      cmp = tree->key_compare (key, node->data->key, tree->key_compare_data);
       if (cmp == 0)
        return node;
       else if (cmp < 0)
        {
+          GTreeNodeVersion *nodev = g_tree_node_find_version (node, version);
           if (search_type == G_TREE_SEARCH_SUCCESSOR)
             remember = node;
-         if (!node->left_child)
+         if (!nodev->left)
            return remember;
 
-         node = node->left;
+         node = nodev->left;
        }
       else
        {
+          GTreeNodeVersion *nodev = g_tree_node_find_version (node, version);
           if (search_type == G_TREE_SEARCH_PREDECESSOR)
             remember = node;
-         if (!node->right_child)
+         if (!nodev->right)
            return remember;
 
-         node = node->right;
+         node = nodev->right;
        }
     }
 }
@@ -1033,18 +1515,18 @@ g_tree_node_pre_order (GTreeNode     *node,
                       GTraverseFunc  traverse_func,
                       gpointer       data)
 {
-  if ((*traverse_func) (node->key, node->value, data))
+  if ((*traverse_func) (node->data->key, node->data->value, data))
     return TRUE;
 
-  if (node->left_child)
+  if (node->v[0].left)
     {
-      if (g_tree_node_pre_order (node->left, traverse_func, data))
+      if (g_tree_node_pre_order (node->v[0].left, traverse_func, data))
        return TRUE;
     }
 
-  if (node->right_child)
+  if (node->v[0].right)
     {
-      if (g_tree_node_pre_order (node->right, traverse_func, data))
+      if (g_tree_node_pre_order (node->v[0].right, traverse_func, data))
        return TRUE;
     }
 
@@ -1056,18 +1538,18 @@ g_tree_node_in_order (GTreeNode     *node,
                      GTraverseFunc  traverse_func,
                      gpointer       data)
 {
-  if (node->left_child)
+  if (node->v[0].left)
     {
-      if (g_tree_node_in_order (node->left, traverse_func, data))
+      if (g_tree_node_in_order (node->v[0].left, traverse_func, data))
        return TRUE;
     }
 
-  if ((*traverse_func) (node->key, node->value, data))
+  if ((*traverse_func) (node->data->key, node->data->value, data))
     return TRUE;
 
-  if (node->right_child)
+  if (node->v[0].right)
     {
-      if (g_tree_node_in_order (node->right, traverse_func, data))
+      if (g_tree_node_in_order (node->v[0].right, traverse_func, data))
        return TRUE;
     }
   
@@ -1079,19 +1561,19 @@ g_tree_node_post_order (GTreeNode     *node,
                        GTraverseFunc  traverse_func,
                        gpointer       data)
 {
-  if (node->left_child)
+  if (node->v[0].left)
     {
-      if (g_tree_node_post_order (node->left, traverse_func, data))
+      if (g_tree_node_post_order (node->v[0].left, traverse_func, data))
        return TRUE;
     }
 
-  if (node->right_child)
+  if (node->v[0].right)
     {
-      if (g_tree_node_post_order (node->right, traverse_func, data))
+      if (g_tree_node_post_order (node->v[0].right, traverse_func, data))
        return TRUE;
     }
 
-  if ((*traverse_func) (node->key, node->value, data))
+  if ((*traverse_func) (node->data->key, node->data->value, data))
     return TRUE;
 
   return FALSE;
@@ -1101,7 +1583,8 @@ static gpointer
 g_tree_node_search (GTreeNode       *node,
                    GCompareFunc     search_func,
                    gconstpointer    data,
-                    GTreeSearchType  search_type)
+                    GTreeSearchType  search_type,
+                    guint            version)
 {
   gint dir;
   GTreeNode *remember;
@@ -1112,140 +1595,146 @@ g_tree_node_search (GTreeNode       *node,
   remember = NULL;
   while (1) 
     {
-      dir = (* search_func) (node->key, data);
+      dir = (* search_func) (node->data->key, data);
       if (dir == 0)
-       return node->value;
+       return node->data->value;
       else if (dir < 0) 
        { 
+          GTreeNodeVersion *nodev = g_tree_node_find_version (node, version);
           if (search_type == G_TREE_SEARCH_SUCCESSOR)
             remember = node;
-         if (!node->left_child)
+         if (!nodev->left)
            return remember;
 
-         node = node->left;
+         node = nodev->left;
        }
       else
        {
+          GTreeNodeVersion *nodev = g_tree_node_find_version (node, version);
           if (search_type == G_TREE_SEARCH_PREDECESSOR)
             remember = node;
-         if (!node->right_child)
+         if (!nodev->right)
            return remember;
          
-         node = node->right;
+         node = nodev->right;
        }
     }
 }
 
 static GTreeNode*
-g_tree_node_rotate_left (GTreeNode *node)
+g_tree_node_rotate_left (GTree     *tree,
+                         GTreeNode *node)
 {
   GTreeNode *right;
 
-  right = node->right;
+  g_assert (node->v[0].version == tree->version);
+
+  right = g_tree_node_next_version (tree, node->v[0].right);
 
-  if (right->left_child)
+  if (right->v[0].left)
     {
-      node->right = right->left;
-      node->right->parent = node;
+      node->v[0].right = g_tree_node_next_version (tree, right->v[0].left);
+      node->v[0].right->v[0].parent = node;
     }
   else
-    {
-      node->right_child = FALSE;
-      node->right = right;
-      right->left_child = TRUE;
-    }
-  right->left = node;
-  right->parent = node->parent;
-  node->parent = right;
+      node->v[0].right = NULL;
+  right->v[0].left = node;
+  right->v[0].parent = node->v[0].parent;
+  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->left;
+  g_assert (node->v[0].version == tree->version);
+
+  left = g_tree_node_next_version (tree, node->v[0].left);
 
-  if (left->right_child)
+  if (left->v[0].right)
     {
-      node->left = left->right;
-      node->left->parent = node;
+      node->v[0].left = g_tree_node_next_version (tree, left->v[0].right);
+      node->v[0].left->v[0].parent = node;
     }
   else
-    {
-      node->left_child = FALSE;
-      node->left = left;
-      left->right_child = TRUE;
-    }
-  left->right = node;
-  left->parent = node->parent;
-  node->parent = left;
+    node->v[0].left = NULL;
+  left->v[0].right = node;
+  left->v[0].parent = node->v[0].parent;
+  node->v[0].parent = left;
 
   return left;
 }
 
 #ifdef G_TREE_DEBUG
 static void
-g_tree_node_check (GTreeNode *node)
+g_tree_node_check (GTreeNode *node,
+                   guint      version)
 {
-  gint left_height;
-  gint right_height;
-  GTreeNode *tmp;
+  GTreeNodeVersion *nv, *tmpv;
 
   if (node)
     {
-      if (node->left_child)
+      nv = g_tree_node_find_version (node, version);
+
+      g_assert (nv->left == NULL || nv->left != nv->right);
+
+      if (nv->left)
        {
-         tmp = g_tree_node_previous (node);
-         g_assert (tmp->right == node);
+          tmpv = g_tree_node_find_version (nv->left, version);
+          g_assert (tmpv->parent == node);
        }
 
-      if (node->right_child)
+      if (nv->right)
        {
-         tmp = g_tree_node_next (node);
-         g_assert (tmp->left == node);
+          tmpv = g_tree_node_find_version (nv->right, version);
+          g_assert (tmpv->parent == node);
        }
 
-      left_height = 0;
-      right_height = 0;
-      
-      if (node->left_child)
-       left_height = g_tree_node_height (node->left);
-      if (node->right_child)
-       right_height = g_tree_node_height (node->right);
-      
-      if (node->left_child)
-       g_tree_node_check (node->left);
-      if (node->right_child)
-       g_tree_node_check (node->right);
+      if (nv->parent)
+        {
+          tmpv = g_tree_node_find_version (nv->parent, version);
+          g_assert (tmpv->left == node || tmpv->right == node);
+        }
+
+      if (nv->left)
+       g_tree_node_check (nv->left, version);
+      if (nv->right)
+       g_tree_node_check (nv->right, version);
     }
 }
 
 static void
-g_tree_node_dump (GTreeNode *node, 
+g_tree_node_dump (GTreeNode *node,
+                  guint      version,
                  gint       indent)
 {
-  g_print ("%*s%c\n", indent, "", *(char *)node->key);
+  GTreeNodeVersion *nv = g_tree_node_find_version (node, version);
+
+  g_print ("%*s%c\n", indent, "", *(char *)node->data->key);
 
-  if (node->left_child)
-    g_tree_node_dump (node->left, indent + 2);
-  else if (node->left)
-    g_print ("%*s<%c\n", indent + 2, "", *(char *)node->left->key);
+  if (nv->left)
+    g_tree_node_dump (nv->left, version, indent + 2);
+  else if ((node = g_tree_node_previous (node, version)))
+    g_print ("%*s<%c\n", indent + 2, "", *(char *)node->data->key);
 
-  if (node->right_child)
-    g_tree_node_dump (node->right, indent + 2);
-  else if (node->right)
-    g_print ("%*s>%c\n", indent + 2, "", *(char *)node->right->key);
+  if (nv->right)
+    g_tree_node_dump (nv->right, version, indent + 2);
+  else if ((node = g_tree_node_next (node, version)))
+    g_print ("%*s>%c\n", indent + 2, "", *(char *)node->data->key);
 }
 
 
 void
-g_tree_dump (GTree *tree)
+g_tree_dump (GTree *tree,
+             guint  version)
 {
-  if (tree->root)
-    g_tree_node_dump (tree->root, 0);
+  GTreeRootVersion *rv = g_tree_root_find_version (tree, version);
+  if (rv->root)
+    g_tree_node_dump (rv->root, version, 0);
 }
 #endif