#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;
guint version;
};
+#define root(i) roots[i].root
+#define rootversion(i) roots[i].version
+
struct _GTreeNodeVersion
{
guint version;
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
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
+ }
}
/**
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;
tree->nnodes = 0;
tree->ref_count = 1;
tree->version = 0;
+
+ tree->roots[0].root = NULL;
+ tree->roots[0].version = 0;
return 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);
g_return_if_fail (tree != NULL);
+ // XXX worry about removing all versions, not just the latest
+
node = g_tree_first_node (tree);
while (node)
node = next;
}
- tree->root = NULL;
+ tree->roots[0].root = NULL;
tree->nnodes = 0;
}
if (g_atomic_int_dec_and_test (&tree->ref_count))
{
g_tree_remove_all (tree);
+ g_free (tree->roots);
g_slice_free (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)
{
{
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;
{
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;
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
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;
{
/* 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)
g_return_if_fail (tree != NULL);
- if (!tree->root)
+ if (!tree->root(NOW))
return;
node = g_tree_first_node (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:
{
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
{
g_return_val_if_fail (tree != NULL, 0);
- return g_tree_node_height (tree->root);
+ return g_tree_node_height (tree->root(NOW));
}
/**
GTreeNode *node, *remember;
gint cmp;
- node = tree->root;
+ node = tree->root(NOW);
if (!node)
return NULL;
}
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
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