#undef G_TREE_DEBUG
-typedef struct _GTreeNode GTreeNode;
+#define MAX_OUT_DEGREE 3
+#define MAX_IN_DEGREE 5
+
+typedef struct _GTreeNode GTreeNode;
+typedef struct _GTreeNodeData GTreeNodeData;
+typedef struct _GTreeNodeVersion GTreeNodeVersion;
struct _GTree
{
gpointer key_compare_data;
guint nnodes;
gint ref_count;
+ guint version;
};
-struct _GTreeNode
+struct _GTreeNodeVersion
{
- gpointer key; /* key for this node */
- gpointer value; /* value stored at this node */
+ guint version;
GTreeNode *left; /* left subtree */
GTreeNode *right; /* right subtree */
GTreeNode *parent; /* parent node */
the next node in the tree) */
};
+struct _GTreeNodeData
+{
+ gpointer key; /* key for this node */
+ gpointer value; /* value stored at this node */
+};
+
+struct _GTreeNode
+{
+ GTreeNodeData *data; /* the node's permanent data */
+ GTreeNodeVersion *v; /* versions of pointers for the node, new versions
+ * are prepended onto the array so v[0] is the newest
+ */
+ guint nv; /* number of versions stored in this node */
+};
+
+#define NOW (0)
+
+#define version(i) v[i].version
+#define left(i) v[i].left
+#define right(i) v[i].right
+#define parent(i) v[i].parent
+#define left_child(i) v[i].left_child
+#define right_child(i) v[i].right_child
static GTreeNode* g_tree_node_new (gpointer key,
- gpointer value);
+ gpointer value,
+ guint version);
static guint g_tree_priority (GTreeNode *node);
static void g_tree_insert_internal (GTree *tree,
gpointer key,
static GTreeNode*
g_tree_node_new (gpointer key,
- gpointer value)
+ gpointer value,
+ guint version)
{
GTreeNode *node = g_slice_new (GTreeNode);
- node->left = NULL;
- node->right = NULL;
- node->parent = NULL;
- node->left_child = FALSE;
- node->right_child = FALSE;
- node->key = key;
- node->value = value;
+ node->data = g_slice_new(GTreeNodeData);
+ node->data->key = key;
+ node->data->value = value;
+
+ /* 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
+ being used */
+ node->v = g_slice_alloc(sizeof(GTreeNodeVersion) *
+ (version == 0 ? 1 : MAX_IN_DEGREE + 1));
+ node->nv = 1;
+
+ node->v[0].version = version;
+ node->v[0].left = NULL;
+ node->v[0].right = NULL;
+ node->v[0].parent = NULL;
+ node->v[0].left_child = FALSE;
+ node->v[0].right_child = FALSE;
return node;
}
tree->key_compare_data = key_compare_data;
tree->nnodes = 0;
tree->ref_count = 1;
+ tree->version = 0;
return tree;
}
tmp = tree->root;
- while (tmp->left_child)
- tmp = tmp->left;
+ while (tmp->left_child(NOW))
+ tmp = tmp->left(NOW);
return tmp;
}
{
GTreeNode *tmp;
- tmp = node->left;
+ tmp = node->left(NOW);
- if (node->left_child)
- while (tmp->right_child)
- tmp = tmp->right;
+ if (node->left_child(NOW))
+ while (tmp->right_child(NOW))
+ tmp = tmp->right(NOW);
return tmp;
}
{
GTreeNode *tmp;
- tmp = node->right;
+ tmp = node->right(NOW);
- if (node->right_child)
- while (tmp->left_child)
- tmp = tmp->left;
+ if (node->right_child(NOW))
+ while (tmp->left_child(NOW))
+ tmp = tmp->left(NOW);
return tmp;
}
next = g_tree_node_next (node);
if (tree->key_destroy_func)
- tree->key_destroy_func (node->key);
+ tree->key_destroy_func (node->data->key);
if (tree->value_destroy_func)
- tree->value_destroy_func (node->value);
+ tree->value_destroy_func (node->data->value);
g_slice_free (GTreeNode, node);
node = next;
g_tree_unref (tree);
}
+/**
+ * g_tree_current_version:
+ * @tree: a #GTree
+ *
+ * Returns the current version number of the #GTree. Inserting or deleting
+ * keys will affect the current version, but not change earlier versions.
+ *
+ * Returns: the current version number of the #GTree.
+ **/
+guint
+g_tree_current_version (GTree *tree)
+{
+ return tree->version;
+}
+
+/**
+ * g_tree_next_version:
+ * @tree: a #GTree
+ *
+ * Increments the version number of the tree. Inserting or deleting keys will
+ * affect the new version of the tree, but not change earlier versions.
+ *
+ * Returns: the new current version number of the #GTree.
+ **/
+guint
+g_tree_next_version (GTree *tree)
+{
+ return ++tree->version;
+}
+
/**
* g_tree_insert:
* @tree: a #GTree.
static guint
g_tree_priority (GTreeNode *node)
{
- guint key = GPOINTER_TO_UINT (node);
+ guint key = GPOINTER_TO_UINT (node->data);
/* This hash function is based on one found on Thomas Wang's
* web page at
if (!tree->root)
{
- tree->root = g_tree_node_new (key, value);
+ tree->root = g_tree_node_new (key, value, tree->version);
tree->nnodes++;
return;
}
while (1)
{
- gint cmp = tree->key_compare (key, node->key, tree->key_compare_data);
+ gint cmp = tree->key_compare (key, node->data->key,
+ tree->key_compare_data);
if (cmp == 0)
{
if (tree->value_destroy_func)
- tree->value_destroy_func (node->value);
+ tree->value_destroy_func (node->data->value);
- node->value = value;
+ node->data->value = value;
if (replace)
{
if (tree->key_destroy_func)
- tree->key_destroy_func (node->key);
+ tree->key_destroy_func (node->data->key);
- node->key = key;
+ node->data->key = key;
}
else
{
}
else if (cmp < 0)
{
- if (node->left_child)
- node = node->left;
+ if (node->left_child(NOW))
+ node = node->left(NOW);
else
{
- child = g_tree_node_new (key, value);
+ child = g_tree_node_new (key, value, tree->version);
- child->left = node->left;
- child->right = node;
- child->parent = node;
+ child->v[0].left = node->v[0].left;
+ child->v[0].right = node;
+ child->v[0].parent = node;
- node->left = child;
- node->left_child = TRUE;
+ node->v[0].left = child;
+ node->v[0].left_child = TRUE;
tree->nnodes++;
}
else
{
- if (node->right_child)
- node = node->right;
+ if (node->right_child(NOW))
+ node = node->right(NOW);
else
{
- child = g_tree_node_new (key, value);
+ child = g_tree_node_new (key, value, tree->version);
- child->right = node->right;
- child->left = node;
- child->parent = node;
+ child->v[0].right = node->v[0].right;
+ child->v[0].left = node;
+ child->v[0].parent = node;
- node->right = child;
- node->right_child = TRUE;
+ node->v[0].right = child;
+ node->v[0].right_child = TRUE;
tree->nnodes++;
/* rotate the new node up until the heap property is restored */
node = child;
- while (node->parent &&
- g_tree_priority (node) < g_tree_priority (node->parent))
+ while (node->parent(NOW) &&
+ g_tree_priority (node) < g_tree_priority (node->parent(NOW)))
{
- GTreeNode *const sp = node->parent->parent;
- GTreeNode *const p = node->parent;
- if (p->left == node)
+ 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);
else
g_tree_node_rotate_left (p);
if (!sp)
tree->root = node;
- else if (sp->left == p)
- sp->left = node;
+ else if (sp->left(NOW) == p)
+ sp->left(NOW) = node;
else
- sp->right = node;
+ sp->right(NOW) = node;
}
}
while (1)
{
- gint cmp = tree->key_compare (key, node->key, tree->key_compare_data);
+ gint cmp = tree->key_compare (key, node->data->key,
+ tree->key_compare_data);
if (cmp == 0)
break;
else if (cmp < 0)
{
- if (!node->left_child)
+ if (!node->left_child(NOW))
return FALSE;
parent = node;
is_leftchild = TRUE;
- node = node->left;
+ node = node->left(NOW);
}
else
{
- if (!node->right_child)
+ if (!node->right_child(NOW))
return FALSE;
parent = node;
is_leftchild = FALSE;
- node = node->right;
+ node = node->right(NOW);
}
}
/* rotate the node down the tree, maintaining the heap property */
- while (node->left_child || node->right_child)
+ while (node->left_child(NOW) || node->right_child(NOW))
{
- if (!node->left_child ||
- (node->right_child &&
- g_tree_priority (node->left) > g_tree_priority (node->right)))
+ if (!node->left_child(NOW) ||
+ (node->right_child(NOW) &&
+ g_tree_priority (node->left(NOW)) >
+ g_tree_priority (node->right(NOW))))
{
/* rotate the right child up */
if (!parent)
parent = tree->root = g_tree_node_rotate_left (node);
else if (is_leftchild)
- parent = parent->left = g_tree_node_rotate_left (node);
+ parent = parent->v[0].left = g_tree_node_rotate_left (node);
else
- parent = parent->right = g_tree_node_rotate_left (node);
+ parent = parent->v[0].right = g_tree_node_rotate_left (node);
is_leftchild = TRUE;
}
else
if (!parent)
parent = tree->root = g_tree_node_rotate_right (node);
else if (is_leftchild)
- parent = parent->left = g_tree_node_rotate_right (node);
+ parent = parent->v[0].left = g_tree_node_rotate_right (node);
else
- parent = parent->right = g_tree_node_rotate_right (node);
+ parent = parent->v[0].right = g_tree_node_rotate_right (node);
is_leftchild = FALSE;
}
}
tree->root = NULL;
else if (is_leftchild)
{
- parent->left_child = FALSE;
- parent->left = node->left;
+ parent->v[0].left_child = FALSE;
+ parent->v[0].left = node->v[0].left;
}
else
{
- parent->right_child = FALSE;
- parent->right = node->right;
+ parent->v[0].right_child = FALSE;
+ parent->v[0].right = node->v[0].right;
}
if (!steal)
{
if (tree->key_destroy_func)
- tree->key_destroy_func (node->key);
+ tree->key_destroy_func (node->data->key);
if (tree->value_destroy_func)
- tree->value_destroy_func (node->value);
+ tree->value_destroy_func (node->data->value);
}
g_slice_free (GTreeNode, node);
node = g_tree_find_node (tree, key, search_type);
- return node ? node->value : NULL;
+ return node ? node->data->value : NULL;
}
/**
if (node)
{
if (orig_key)
- *orig_key = node->key;
+ *orig_key = node->data->key;
if (value)
- *value = node->value;
+ *value = node->data->value;
return TRUE;
}
else
while (node)
{
- if ((*func) (node->key, node->value, user_data))
+ if ((*func) (node->data->key, node->data->value, user_data))
break;
node = g_tree_node_next (node);
{
gint l = 0, r = 0;
if (node == NULL) return 0;
- if (node->left_child) l = g_tree_node_height (node->left);
- if (node->right_child) r = g_tree_node_height (node->right);
+ if (node->left_child(NOW)) l = g_tree_node_height (node->left(NOW));
+ if (node->right_child(NOW)) r = g_tree_node_height (node->right(NOW));
return 1 + MAX(l, r);
}
remember = NULL;
while (1)
{
- cmp = tree->key_compare (key, node->key, tree->key_compare_data);
+ cmp = tree->key_compare (key, node->data->key, tree->key_compare_data);
if (cmp == 0)
return node;
else if (cmp < 0)
{
if (search_type == G_TREE_SEARCH_SUCCESSOR)
remember = node;
- if (!node->left_child)
+ if (!node->left_child(NOW))
return remember;
- node = node->left;
+ node = node->left(NOW);
}
else
{
if (search_type == G_TREE_SEARCH_PREDECESSOR)
remember = node;
- if (!node->right_child)
+ if (!node->right_child(NOW))
return remember;
- node = node->right;
+ node = node->right(NOW);
}
}
}
GTraverseFunc traverse_func,
gpointer data)
{
- if ((*traverse_func) (node->key, node->value, data))
+ if ((*traverse_func) (node->data->key, node->data->value, data))
return TRUE;
- if (node->left_child)
+ if (node->left_child(NOW))
{
- if (g_tree_node_pre_order (node->left, traverse_func, data))
+ if (g_tree_node_pre_order (node->left(NOW), traverse_func, data))
return TRUE;
}
- if (node->right_child)
+ if (node->right_child(NOW))
{
- if (g_tree_node_pre_order (node->right, traverse_func, data))
+ if (g_tree_node_pre_order (node->right(NOW), traverse_func, data))
return TRUE;
}
GTraverseFunc traverse_func,
gpointer data)
{
- if (node->left_child)
+ if (node->left_child(NOW))
{
- if (g_tree_node_in_order (node->left, traverse_func, data))
+ if (g_tree_node_in_order (node->left(NOW), traverse_func, data))
return TRUE;
}
- if ((*traverse_func) (node->key, node->value, data))
+ if ((*traverse_func) (node->data->key, node->data->value, data))
return TRUE;
- if (node->right_child)
+ if (node->right_child(NOW))
{
- if (g_tree_node_in_order (node->right, traverse_func, data))
+ if (g_tree_node_in_order (node->right(NOW), traverse_func, data))
return TRUE;
}
GTraverseFunc traverse_func,
gpointer data)
{
- if (node->left_child)
+ if (node->left_child(NOW))
{
- if (g_tree_node_post_order (node->left, traverse_func, data))
+ if (g_tree_node_post_order (node->left(NOW), traverse_func, data))
return TRUE;
}
- if (node->right_child)
+ if (node->right_child(NOW))
{
- if (g_tree_node_post_order (node->right, traverse_func, data))
+ if (g_tree_node_post_order (node->right(NOW), traverse_func, data))
return TRUE;
}
- if ((*traverse_func) (node->key, node->value, data))
+ if ((*traverse_func) (node->data->key, node->data->value, data))
return TRUE;
return FALSE;
remember = NULL;
while (1)
{
- dir = (* search_func) (node->key, data);
+ dir = (* search_func) (node->data->key, data);
if (dir == 0)
- return node->value;
+ return node->data->value;
else if (dir < 0)
{
if (search_type == G_TREE_SEARCH_SUCCESSOR)
remember = node;
- if (!node->left_child)
+ if (!node->left_child(NOW))
return remember;
- node = node->left;
+ node = node->left(NOW);
}
else
{
if (search_type == G_TREE_SEARCH_PREDECESSOR)
remember = node;
- if (!node->right_child)
+ if (!node->right_child(NOW))
return remember;
- node = node->right;
+ node = node->right(NOW);
}
}
}
{
GTreeNode *right;
- right = node->right;
+ right = node->v[0].right;
- if (right->left_child)
+ if (right->v[0].left_child)
{
- node->right = right->left;
- node->right->parent = node;
+ node->v[0].right = right->v[0].left;
+ node->v[0].right->v[0].parent = node;
}
else
{
- node->right_child = FALSE;
- node->right = right;
- right->left_child = TRUE;
+ node->v[0].right_child = FALSE;
+ node->v[0].right = right;
+ right->v[0].left_child = TRUE;
}
- right->left = node;
- right->parent = node->parent;
- node->parent = right;
+ right->v[0].left = node;
+ right->v[0].parent = node->v[0].parent;
+ node->v[0].parent = right;
return right;
}
{
GTreeNode *left;
- left = node->left;
+ left = node->v[0].left;
- if (left->right_child)
+ if (left->v[0].right_child)
{
- node->left = left->right;
- node->left->parent = node;
+ node->v[0].left = left->v[0].right;
+ node->v[0].left->v[0].parent = node;
}
else
{
- node->left_child = FALSE;
- node->left = left;
- left->right_child = TRUE;
+ node->v[0].left_child = FALSE;
+ node->v[0].left = left;
+ left->v[0].right_child = TRUE;
}
- left->right = node;
- left->parent = node->parent;
- node->parent = left;
+ left->v[0].right = node;
+ left->v[0].parent = node->v[0].parent;
+ node->v[0].parent = left;
return left;
}
g_tree_node_dump (GTreeNode *node,
gint indent)
{
- g_print ("%*s%c\n", indent, "", *(char *)node->key);
+ g_print ("%*s%c\n", indent, "", *(char *)node->data->key);
if (node->left_child)
g_tree_node_dump (node->left, indent + 2);
else if (node->left)
- g_print ("%*s<%c\n", indent + 2, "", *(char *)node->left->key);
+ g_print ("%*s<%c\n", indent + 2, "", *(char *)node->left->data->key);
if (node->right_child)
g_tree_node_dump (node->right, indent + 2);
else if (node->right)
- g_print ("%*s>%c\n", indent + 2, "", *(char *)node->right->key);
+ g_print ("%*s>%c\n", indent + 2, "", *(char *)node->right->data->key);
}