{
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
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
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
* 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
node->v);
g_slice_free (GTreeNode, node);
}
+ else
+ node->data->stolen = TRUE;
return key_removed;
}
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++) {
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);