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