#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,
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);
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.
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;
}
if (g_atomic_int_dec_and_test (&tree->ref_count))
{
g_tree_remove_all (tree);
+ g_free (tree->r);
g_slice_free (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.
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
}
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
}
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
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,
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
{
}
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++;
}
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++;
/* 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;
}
}
* 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,
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;
* @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,
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;
{
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;
}
/**
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);
}
/**
* @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.
*
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;
}
/**
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
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);
}
}
{
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:
{
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
{
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);
}
{
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)
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;
}
}
}
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;
}
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;
}
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;
g_tree_node_search (GTreeNode *node,
GCompareFunc search_func,
gconstpointer data,
- GTreeSearchType search_type)
+ GTreeSearchType search_type,
+ guint version)
{
gint dir;
GTreeNode *remember;
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