From: Dana Jansens Date: Tue, 3 Nov 2009 23:19:38 +0000 (-0500) Subject: trying to make you delete versions. instead it'd be nice to insert/delete in any... X-Git-Url: http://git.openbox.org/?a=commitdiff_plain;h=d53af228d6b7a3d84b82f632c3b64e0109e5ac9d;p=dana%2Fcg-glib.git trying to make you delete versions. instead it'd be nice to insert/delete in any version you want --- diff --git a/glib/gtree.c b/glib/gtree.c index 65e00fc0..67f4671e 100644 --- a/glib/gtree.c +++ b/glib/gtree.c @@ -84,7 +84,8 @@ struct _GTreeNodeData { gpointer key; /* key for this node */ gpointer value; /* value stored at this node */ - guint ref_count; + guint16 ref_count; + guint8 stolen; /* true if the node is stolen instead of removed */ }; struct _GTreeNode @@ -153,6 +154,7 @@ g_tree_node_new (GTree *tree, 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 @@ -527,9 +529,125 @@ g_tree_current_version (GTree *tree) guint g_tree_next_version (GTree *tree) { + g_assert (tree->version+1 != 0); return ++tree->version; } +/* return TRUE if the node no stores the pointers that were in the version */ +static gboolean +g_tree_node_delete_version (GTree *tree, + GTreeNode *node, + guint version) +{ + GTreeNodeVersion *nv; + guint next, i; + gboolean l, r, removed = FALSE; + + if (!node) + return TRUE; + + nv = g_tree_node_find_version (node, version); + + l = g_tree_node_delete_version (tree, nv->left, version); + r = g_tree_node_delete_version (tree, nv->right, version); + + if (nv->version == version) + { + i = nv - node->v; + if (i == 0) /* latest version */ + next = tree->version; + else if (i == node->nv-1) /* last in the node's list */ + next = node->v[0].version; + else /* somewhere else in the node's list (has something after it) */ + next = (nv+1)->version; + + if (nv->version+1 < next) + nv->version++; /* remove the version, but keep the pointer for versions + larger than it */ + else + { + /* already have a different set of pointers for version+1, so we can + remove this set entirely */ + if (i == 0) + { + node->v[0] = node->v[node->nv-1]; + node->nv--; + } + else + { + for (; i < node->nv-1; ++i) + node->v[i] = node->v[i+1]; + node->nv--; + } + removed = TRUE; + } + } + + /* if the child is not pointing to us anymore then we'd better not be + pointing to it, and if it is, then we'd better still be too */ + g_assert (l == removed && r == removed); + + /* if we removed the last version inside the node, then we can free it */ + if (node->nv == 0) + { + if (--node->data->ref_count == 0) + { + 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); + } + + g_slice_free1 (sizeof(GTreeNodeVersion) * + (node->version(NOW) == 0 ? 1 : TABLE_SIZE), + node->v); + g_slice_free (GTreeNode, node); + + return TRUE; + } + + return FALSE; +} + +void +g_tree_delete_version (GTree *tree, + guint version) +{ + GTreeRootVersion *rv; + + rv = g_tree_root_find_version (tree, version); + + if (g_tree_node_delete_version (tree, rv->root, version)) + { + guint i; + + i = rv - tree->roots; + if (i == 0) + { + tree->roots[0] = tree->roots[tree->nroots-1]; + tree->nroots--; + } + else + { + for (; i < tree->nroots-1; ++i) + tree->roots[i] = tree->roots[i+1]; + tree->nroots--; + } + } + +#ifdef G_TREE_DEBUG + { + guint i; + for (i = 0; i < tree->roots[0].version; ++i) + g_tree_node_check (g_tree_root_find_version (tree, i)->root, i); + } +#endif +} + /* Make a new root pointer if the current one is not for the current version of the tree */ static void @@ -929,7 +1047,8 @@ g_tree_remove (GTree *tree, * Removes a key and its associated value from a #GTree without calling * 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. * + * it was inserted), then it cannot be removed from the tree, until all + * versions containing the node are removed from the tree. * If the key does not exist in the #GTree, the function does nothing. * * Returns: %TRUE if the key was found and able to be removed @@ -1099,6 +1218,8 @@ g_tree_remove_internal (GTree *tree, node->v); g_slice_free (GTreeNode, node); } + else + node->data->stolen = TRUE; return key_removed; } diff --git a/glib/gtree.h b/glib/gtree.h index 9665d70b..b723aebb 100644 --- a/glib/gtree.h +++ b/glib/gtree.h @@ -63,6 +63,8 @@ void g_tree_unref (GTree *tree); void g_tree_destroy (GTree *tree); guint g_tree_current_version (GTree *tree); guint g_tree_next_version (GTree *tree); +void g_tree_delete_version (GTree *tree, + guint version); void g_tree_insert (GTree *tree, gpointer key, gpointer value); diff --git a/tests/tree-test.c b/tests/tree-test.c index e4c57cd1..4ed704ce 100644 --- a/tests/tree-test.c +++ b/tests/tree-test.c @@ -257,6 +257,8 @@ main (int argc, g_assert (p == NULL); g_tree_unref (tree); + + /* test adding and removing items in a versioned tree */ tree = g_tree_new (my_compare); for (i = 0; chars[i]; i++) { @@ -363,6 +365,58 @@ main (int argc, g_tree_destroy (tree); + /* test removing versions from a tree */ + tree = g_tree_new (my_compare); + + for (i = 0; chars[i]; i++) { + g_tree_insert (tree, &chars[i], &chars[i]); + g_tree_next_version (tree); + } + g_assert(i == g_tree_nnodes(tree)); + + /* remove everything from the last version */ + for (i = 0; chars[i]; i++) + g_tree_remove (tree, &chars[i]); + g_assert(g_tree_nnodes(tree) == 0); + + g_assert (g_tree_current_version (tree) == i); + + for (i = 0; i < g_tree_current_version (tree); ++i) + { + char *r; + int j; + + for (j = 0; chars[j]; j++) + { + r = g_tree_lookup_related (tree, &chars[j], + G_TREE_SEARCH_PREDECESSOR, i); + g_assert (r && *r == chars[MIN(j,i)]); + } + } + + for (i = 0; chars[i]; i++) + if (i%2==0) + g_tree_delete_version (tree, i); + + for (i = 0; chars[i]; i++) + if (i%2!=0) + g_tree_delete_version (tree, i); + + for (i = 0; i < g_tree_current_version (tree); ++i) + { + char *r; + int j; + + for (j = 0; chars[j]; j++) + { + r = g_tree_lookup_related (tree, &chars[j], + G_TREE_SEARCH_PREDECESSOR, i); + g_assert (r == NULL); + } + } + + g_tree_unref (tree); + tree = g_tree_new_full ((GCompareDataFunc)my_compare, NULL, my_key_destroy, my_value_destroy);