--- /dev/null
+/*
+ * AA-Tree - Binary tree with embeddable nodes.
+ *
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Self-balancing binary tree.
+ *
+ * Here is an implementation of AA-tree (Arne Andersson tree)
+ * which is simplification of Red-Black tree.
+ *
+ * Red-black tree has following properties that must be kept:
+ * 1. A node is either red or black.
+ * 2. The root is black.
+ * 3. All leaves (NIL nodes) are black.
+ * 4. Both childen of red node are black.
+ * 5. Every path from root to leaf contains same number of black nodes.
+ *
+ * AA-tree adds additional property:
+ * 6. Red node can exist only as a right node.
+ *
+ * Red-black tree properties quarantee that the longest path is max 2x longer
+ * than shortest path (B-R-B-R-B-R-B vs. B-B-B-B) thus the tree will be roughly
+ * balanced. Also it has good worst-case guarantees for insertion and deletion,
+ * which makes it good tool for real-time applications.
+ *
+ * AA-tree removes most special cases from RB-tree, thus making resulting
+ * code lot simpler. It requires slightly more rotations when inserting
+ * and deleting but also keeps the tree more balanced.
+ */
+
+
+#include "aatree.h"
+
+typedef struct AATree Tree;
+typedef struct AANode Node;
+
+/*
+ * NIL node
+ */
+#define NIL ((struct AANode *)&_nil)
+static const struct AANode _nil = { NIL, NIL, 0 };
+
+/*
+ * Rebalancing. AA-tree needs only 2 operations
+ * to keep the tree balanced.
+ */
+
+/*
+ * Fix red on left.
+ *
+ * X Y
+ * / --> \
+ * Y X
+ * \ /
+ * a a
+ */
+static inline Node * skew(Node *x)
+{
+ Node *y = x->left;
+ if (x->level == y->level && x != NIL) {
+ x->left = y->right;
+ y->right = x;
+ return y;
+ }
+ return x;
+}
+
+/*
+ * Fix 2 reds on right.
+ *
+ * X Y
+ * \ / \
+ * Y --> X Z
+ * / \ \
+ * a Z a
+ */
+static inline Node * split(Node *x)
+{
+ Node *y = x->right;
+ if (x->level == y->right->level && x != NIL) {
+ x->right = y->left;
+ y->left = x;
+ y->level++;
+ return y;
+ }
+ return x;
+}
+
+/* insert is easy */
+static Node *rebalance_on_insert(Node *current)
+{
+ return split(skew(current));
+}
+
+/* remove is bit more tricky */
+static Node *rebalance_on_remove(Node *current)
+{
+ /*
+ * Removal can create a gap in levels,
+ * fix it by lowering current->level.
+ */
+ if (current->left->level < current->level - 1
+ || current->right->level < current->level - 1)
+ {
+ current->level--;
+
+ /* if ->right is red, change it's level too */
+ if (current->right->level > current->level)
+ current->right->level = current->level;
+
+ /* reshape, ask Arne about those */
+ current = skew(current);
+ current->right = skew(current->right);
+ current->right->right = skew(current->right->right);
+ current = split(current);
+ current->right = split(current->right);
+ }
+ return current;
+}
+
+/*
+ * Recursive insertion
+ */
+
+static Node * insert_sub(Tree *tree, Node *current, uintptr_t value, Node *node)
+{
+ int cmp;
+
+ if (current == NIL) {
+ /*
+ * Init node as late as possible, to avoid corrupting
+ * the tree in case it is already added.
+ */
+ node->left = node->right = NIL;
+ node->level = 1;
+
+ tree->count++;
+ return node;
+ }
+
+ /* recursive insert */
+ cmp = tree->node_cmp(value, current);
+ if (cmp > 0)
+ current->right = insert_sub(tree, current->right, value, node);
+ else if (cmp < 0)
+ current->left = insert_sub(tree, current->left, value, node);
+ else
+ /* already exists? */
+ return current;
+
+ return rebalance_on_insert(current);
+}
+
+void aatree_insert(Tree *tree, uintptr_t value, Node *node)
+{
+ tree->root = insert_sub(tree, tree->root, value, node);
+}
+
+/*
+ * Recursive removal
+ */
+
+/* remove_sub could be used for that, but want to avoid comparisions */
+static Node *steal_leftmost(Tree *tree, Node *current, Node **save_p)
+{
+ if (current->left == NIL) {
+ *save_p = current;
+ return current->right;
+ }
+
+ current->left = steal_leftmost(tree, current->left, save_p);
+ return rebalance_on_remove(current);
+}
+
+/* drop this node from tree */
+static Node *drop_this_node(Tree *tree, Node *old)
+{
+ Node *new = NIL;
+
+ if (old->left == NIL)
+ new = old->right;
+ else if (old->right == NIL)
+ new = old->left;
+ else {
+ /*
+ * Picking nearest from right is better than from left,
+ * due to asymmetry of the AA-tree. It will result in
+ * less tree operations in the long run,
+ */
+ old->right = steal_leftmost(tree, old->right, &new);
+
+ /* take old node's place */
+ *new = *old;
+ }
+
+ /* cleanup for old node */
+ if (tree->release_cb)
+ tree->release_cb(old, tree);
+ tree->count--;
+
+ return new;
+}
+
+static Node *remove_sub(Tree *tree, Node *current, uintptr_t value)
+{
+ int cmp;
+
+ /* not found? */
+ if (current == NIL)
+ return current;
+
+ cmp = tree->node_cmp(value, current);
+ if (cmp > 0)
+ current->right = remove_sub(tree, current->right, value);
+ else if (cmp < 0)
+ current->left = remove_sub(tree, current->left, value);
+ else
+ current = drop_this_node(tree, current);
+
+ return rebalance_on_remove(current);
+}
+
+void aatree_remove(Tree *tree, uintptr_t value)
+{
+ tree->root = remove_sub(tree, tree->root, value);
+}
+
+/*
+ * Walking all nodes
+ */
+
+static void walk_sub(Node *current, enum AATreeWalkType wtype,
+ aatree_walker_f walker, void *arg)
+{
+ if (current == NIL)
+ return;
+
+ switch (wtype) {
+ case AA_WALK_IN_ORDER:
+ walk_sub(current->left, wtype, walker, arg);
+ walker(current, arg);
+ walk_sub(current->right, wtype, walker, arg);
+ break;
+ case AA_WALK_POST_ORDER:
+ walk_sub(current->left, wtype, walker, arg);
+ walk_sub(current->right, wtype, walker, arg);
+ walker(current, arg);
+ break;
+ case AA_WALK_PRE_ORDER:
+ walker(current, arg);
+ walk_sub(current->left, wtype, walker, arg);
+ walk_sub(current->right, wtype, walker, arg);
+ break;
+ }
+}
+
+/* walk tree in correct order */
+void aatree_walk(Tree *tree, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg)
+{
+ walk_sub(tree->root, wtype, walker, arg);
+}
+
+/* walk tree in bottom-up order, so that walker can destroy the nodes */
+void aatree_destroy(Tree *tree)
+{
+ walk_sub(tree->root, AA_WALK_POST_ORDER, tree->release_cb, tree);
+
+ /* reset tree */
+ tree->root = NIL;
+ tree->count = 0;
+}
+
+/* prepare tree */
+void aatree_init(Tree *tree, aatree_cmp_f cmpfn, aatree_walker_f release_cb)
+{
+ tree->root = NIL;
+ tree->count = 0;
+ tree->node_cmp = cmpfn;
+ tree->release_cb = release_cb;
+}
+
+/*
+ * search function
+ */
+Node *aatree_search(Tree *tree, uintptr_t value)
+{
+ Node *current = tree->root;
+ while (current != NIL) {
+ int cmp = tree->node_cmp(value, current);
+ if (cmp > 0)
+ current = current->right;
+ else if (cmp < 0)
+ current = current->left;
+ else
+ return current;
+ }
+ return NULL;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/** @file
+ *
+ * AA-Tree - Binary tree with embeddable nodes.
+ *
+ * AA-Tree (Arne Andersson tree) is a simplified Red-Black tree.
+ */
+
+#ifndef _USUAL_AATREE_H_
+#define _USUAL_AATREE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+struct AATree;
+struct AANode;
+
+/** Callback for node comparision against value */
+typedef int (*aatree_cmp_f)(uintptr_t, struct AANode *node);
+
+/** Callback for walking the tree */
+typedef void (*aatree_walker_f)(struct AANode *n, void *arg);
+
+/**
+ * Tree header, for storing helper functions.
+ */
+struct AATree {
+ struct AANode *root;
+ int count;
+ aatree_cmp_f node_cmp;
+ aatree_walker_f release_cb;
+};
+
+/**
+ * Tree node. Embeddable, parent structure should be taken
+ * with container_of().
+ *
+ * Techinally, the full level is not needed and 2-lowest
+ * bits of either ->left or ->right would be enough
+ * to keep track of structure. Currently this is not
+ * done to keep code simple.
+ */
+struct AANode {
+ struct AANode *left; /**< smaller values */
+ struct AANode *right; /**< larger values */
+ int level; /**< number of black nodes to leaf */
+};
+
+/**
+ * Walk order types.
+ */
+enum AATreeWalkType {
+ AA_WALK_IN_ORDER = 0, /* left->self->right */
+ AA_WALK_PRE_ORDER = 1, /* self->left->right */
+ AA_WALK_POST_ORDER = 2, /* left->right->self */
+};
+
+/** Initialize structure */
+void aatree_init(struct AATree *tree, aatree_cmp_f cmpfn, aatree_walker_f release_cb);
+
+/** Search for node */
+struct AANode *aatree_search(struct AATree *tree, uintptr_t value);
+
+/** Insert new node */
+void aatree_insert(struct AATree *tree, uintptr_t value, struct AANode *node);
+
+/** Remote node */
+void aatree_remove(struct AATree *tree, uintptr_t value);
+
+/** Walk over all nodes */
+void aatree_walk(struct AATree *tree, enum AATreeWalkType wtype, aatree_walker_f walker, void *arg);
+
+/** Free */
+void aatree_destroy(struct AATree *tree);
+
+/** Check if terminal node. */
+static inline int aatree_is_nil_node(const struct AANode *node)
+{
+ return (node->left == node);
+}
+
+#endif
*
* Cluster name will be actual connect string.
*/
-static ProxyCluster *fake_cluster_list = NULL;
+static struct AATree fake_cluster_tree;
/* plan for fetching cluster version */
static void *version_plan;
return (n > 0) && !(n & (n - 1));
}
+static int cluster_name_cmp(uintptr_t val, struct AANode *node)
+{
+ const char *name = (const char *)val;
+ const ProxyCluster *cluster = (ProxyCluster *)node;
+
+ return strcmp(name, cluster->name);
+}
+
/*
* Create cache memory area and prepare plans
*/
ALLOCSET_SMALL_MINSIZE,
ALLOCSET_SMALL_INITSIZE,
ALLOCSET_SMALL_MAXSIZE);
+ aatree_init(&fake_cluster_tree, cluster_name_cmp, NULL);
}
/* initialize plans on demand */
ProxyCluster *cluster;
ProxyConnection *conn;
MemoryContext old_ctx;
+ struct AANode *n;
/* search if cached */
- for (cluster = fake_cluster_list; cluster; cluster = cluster->next)
- {
- if (strcmp(cluster->name, connect_str) == 0)
- break;
- }
-
- if (cluster)
- return cluster;
+ n = aatree_search(&fake_cluster_tree, (uintptr_t)connect_str);
+ if (n)
+ return (ProxyCluster *)n;
/* create if not */
MemoryContextSwitchTo(old_ctx);
- cluster->next = fake_cluster_list;
- fake_cluster_list = cluster;
+ aatree_insert(&fake_cluster_tree, (uintptr_t)connect_str, &cluster->node);
return cluster;
}
/*
* Clean old connections and results from all clusters.
*/
+
+static void w_clean_cluster(struct AANode *n, void *arg)
+{
+ ProxyCluster *c = (ProxyCluster *)n;
+ struct timeval *now = arg;
+ clean_cluster(c, now);
+}
+
void
plproxy_cluster_maint(struct timeval * now)
{
for (cluster = cluster_list; cluster; cluster = cluster->next)
clean_cluster(cluster, now);
- for (cluster = fake_cluster_list; cluster; cluster = cluster->next)
- clean_cluster(cluster, now);
+ aatree_walk(&fake_cluster_tree, AA_WALK_IN_ORDER, w_clean_cluster, now);
}