GTreeRootVersion *const v = tree->roots;
const guint nv = tree->nroots;
GTreeRootVersion *l, *r;
-
- /* note: we never search for a version smaller than the smallest
- version in the array */
+ static GTreeRootVersion none = {
+ .root = NULL,
+ .version = 0
+ };
if (v[0].version <= version)
return v;
else
l = m+1;
}
- g_assert (r->version < version);
+ g_assert (r == v || r->version < version);
g_assert (l == v+nv || l->version > version);
- return r;
+ if (r == v)
+ return &none;
+ else
+ return r;
}
static inline GTreeNodeVersion *
return ++tree->version;
}
-/* return TRUE if the node no stores the pointers that were in the version */
-static gboolean
+static inline guint
+next_version(guint i, guint n)
+{
+ if (i == 0)
+ return n; /* it was the last version so return something larger */
+ else if (i == n-1)
+ return 0; /* it was the last in the array */
+ else
+ return i+1; /* it was someone else in the array */
+}
+
+static inline guint
+previous_version(guint i, guint n)
+{
+ if (i == 0)
+ return n-1; /* it was the last version */
+ else if (i == 1)
+ return n; /* it was the first so return something larger */
+ else
+ return i-1; /* it was someone else in the array */
+}
+
+/* returns the lowest version > @version in the subtree rooted at @node */
+static guint
g_tree_node_delete_version (GTree *tree,
GTreeNode *node,
+ guint pnext,
guint version)
{
GTreeNodeVersion *nv;
- guint next, i;
- gboolean l, r, removed = FALSE;
+ guint next, i, lnext, rnext, to;
if (!node)
- return TRUE;
+ return 0;
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);
+ /* next is the index of the next versioned-pointers in the node */
+ i = nv - node->v;
+ next = next_version (nv - node->v, node->nv);
+
+ /* if the next version of pointers uses the child still then include that
+ version in pnext, otherwise we won't be left with a pointer to it anyways
+ so tell it 0. */
+ if (next < node->nv && node->v[next].left == nv->left)
+ lnext = MIN ((pnext ? pnext : node->v[next].version),
+ node->v[next].version ? node->v[next].version : pnext);
+ else
+ lnext = 0;
+ lnext = g_tree_node_delete_version (tree, nv->left, lnext, version);
+
+ if (next < node->nv && node->v[next].right == nv->right)
+ rnext = MIN ((pnext ? pnext : node->v[next].version),
+ node->v[next].version ? node->v[next].version : pnext);
+ else
+ rnext = 0;
+ rnext = g_tree_node_delete_version (tree, nv->right, rnext, version);
+
+ /* now next is the next version in this node */
+ if (next < node->nv)
+ next = node->v[next].version;
+ else
+ next = 0;
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 */
+ /* change this pointer's version to this value.
+ this removes the pointer for the current
+ version, but keeps it for versions >= 'to'.
+ if there was nothing seen >= 'version' in any subtrees or the path
+ from the root, then change to version+1 (and stick around)
+ else, change to the smallest version seen so far, because nothing in
+ between exists in this subtree, or in the path from root
+ */
+ to = MAX (nv->version+1,
+ MIN ((pnext ? pnext : MAX (lnext, rnext)),
+ MIN ((lnext ? lnext : MAX (pnext, rnext)),
+ (rnext ? rnext : MAX (pnext, lnext)))));
+
+ if (to < next)
+ {
+ g_assert (to <= tree->version); /* next should be 0 otherwise */
+ next = nv->version = to;
+ }
else
{
- /* already have a different set of pointers for version+1, so we can
- remove this set entirely */
+ /* already have a different set of pointers for that version, so we
+ can remove this set entirely */
if (i == 0)
{
node->v[0] = node->v[node->nv-1];
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 we removed the last version inside the node, then we can free it */
+ if (node->nv == 0)
{
- if (!node->data->stolen)
+ if (--node->data->ref_count == 0)
{
- if (tree->key_destroy_func)
- tree->key_destroy_func (node->data->key);
- if (tree->value_destroy_func)
- tree->value_destroy_func (node->data->value);
+ 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_free (GTreeNodeData, node->data);
+
+ g_slice_free1 (sizeof(GTreeNodeVersion) *
+ (node->version(NOW) == 0 ? 1 : TABLE_SIZE),
+ node->v);
+ node->v = NULL;
+ node->data = NULL;
+ g_slice_free (GTreeNode, node);
}
+ }
- g_slice_free1 (sizeof(GTreeNodeVersion) *
- (node->version(NOW) == 0 ? 1 : TABLE_SIZE),
- node->v);
- g_slice_free (GTreeNode, node);
- return TRUE;
- }
+ /* return the smallest version > the one being deleted that was seen
+ in this node or its children, or 0 if there was none */
- return FALSE;
+ return MIN ((next ? next : MAX (lnext, rnext)),
+ MIN ((lnext ? lnext : MAX (next, rnext)),
+ (rnext ? rnext : MAX (next, lnext))));
}
void
guint version)
{
GTreeRootVersion *rv;
+ guint next, rnext;
rv = g_tree_root_find_version (tree, version);
- if (g_tree_node_delete_version (tree, rv->root, version))
+ rnext = next_version (rv - tree->roots, tree->nroots);
+ if (rnext == tree->nroots)
+ rnext = rv->version < tree->version ? rv->version+1 : 0;
+ else if (tree->roots[rnext].root == rv->root)
+ rnext = tree->roots[rnext].version;
+ else
+ rnext = 0;
+
+ next = g_tree_node_delete_version (tree, rv->root, rnext, version);
+ if (rv->version == version)
+ {
+ if (next > 0 && next < rnext)
+ {
+ /* the smallest version in the tree is now 'next' */
+ rv->version = next;
+ }
+ else
+ {
+ rv->root = NULL;
+ }
+ }
+
+ guint i = rv - tree->roots;
+
+ /* we're going to insert a NULL root before 'i'. in this case it should
+ be after 'i'. */
+ if (rv->version < version)
+ i = next_version (i, tree->nroots);
+
+ /* find the furthest consecutive NULL root node strictly before 'i' */
+ guint j;
+ guint numnulls = 0;
+ guint pi = tree->nroots;
+ for (j = previous_version (i, tree->nroots);
+ j != tree->nroots && tree->roots[j].root == NULL;
+ j = previous_version (j, tree->nroots))
{
- guint i;
+ pi = j;
+ ++numnulls;
+ }
+
+ /* find the first non-NULL root node at or after 'i' */
+ guint ni = i;
+ for (j = next_version (i, tree->nroots);
+ j != tree->nroots && tree->roots[ni].root == NULL;
+ j = next_version (j, tree->nroots))
+ {
+ ni = j;
+ ++numnulls;
+ }
+
+ if (!numnulls)
+ {
+ /* gotta insert before 'i' */
+ guint j;
+
+ tree->nroots++;
+ tree->roots = g_renew(GTreeRootVersion, tree->roots, tree->nroots);
- i = rv - tree->roots;
if (i == 0)
+ tree->roots[tree->nroots-1] = tree->roots[i];
+ else
{
- tree->roots[0] = tree->roots[tree->nroots-1];
- tree->nroots--;
+ for (j = tree->nroots-1; j > i; --j)
+ tree->roots[j] = tree->roots[j-1];
}
- else
+ tree->roots[i].version = version;
+ tree->roots[i].root = NULL;
+ }
+ else
+ {
+ /* there are some NULL roots we can use already */
+ guint j;
+
+ /* there was no NULL roots before 'rv', so 'rv' must be a NULL root */
+ if (pi == tree->nroots)
+ pi = i;
+
+ /* now 'rv' must be NULL so we'll leave it, and kill the following
+ consecutive NULL roots */
+ j = next_version (pi, tree->nroots);
+ while (ni != tree->nroots)
{
- for (; i < tree->nroots-1; ++i)
- tree->roots[i] = tree->roots[i+1];
- tree->nroots--;
+ tree->roots[j] = tree->roots[ni];
+ j = next_version (j, tree->nroots);
+ ni = next_version (ni, tree->nroots);
}
+
+ tree->nroots -= numnulls-1;
+ tree->roots = g_renew(GTreeRootVersion, tree->roots, tree->nroots);
}
#ifdef G_TREE_DEBUG