--- /dev/null
+/*
+ * PgBouncer - Lightweight connection pooler for PostgreSQL.
+ *
+ * Copyright (c) 2007 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and 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 <stddef.h> /* for NULL */
+
+#include "aatree.h"
+
+/*
+ * NIL node
+ */
+#define NIL (&_nil)
+static struct Node _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, long 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 tree_insert(Tree *tree, long 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 */
+ tree->release_cb(old, tree);
+ tree->count--;
+
+ return new;
+}
+
+static Node *remove_sub(Tree *tree, Node *current, long 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 tree_remove(Tree *tree, long value)
+{
+ tree->root = remove_sub(tree, tree->root, value);
+}
+
+/*
+ * Walking all nodes
+ */
+
+static void walk_sub(Node *current, enum TreeWalkType wtype,
+ tree_walker_f walker, void *arg)
+{
+ if (current == NIL)
+ return;
+
+ switch (wtype) {
+ case WALK_IN_ORDER:
+ walk_sub(current->left, wtype, walker, arg);
+ walker(current, arg);
+ walk_sub(current->right, wtype, walker, arg);
+ break;
+ case WALK_POST_ORDER:
+ walk_sub(current->left, wtype, walker, arg);
+ walk_sub(current->right, wtype, walker, arg);
+ walker(current, arg);
+ break;
+ case 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 tree_walk(Tree *tree, enum TreeWalkType wtype, tree_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 tree_destroy(Tree *tree)
+{
+ walk_sub(tree->root, WALK_POST_ORDER, tree->release_cb, tree);
+
+ /* reset tree */
+ tree->root = NIL;
+ tree->count = 0;
+}
+
+/* prepare tree */
+void tree_init(Tree *tree, tree_cmp_f cmpfn, tree_walker_f release_cb)
+{
+ tree->root = NIL;
+ tree->count = 0;
+ tree->node_cmp = cmpfn;
+ tree->release_cb = release_cb;
+}
+
+/*
+ * search function
+ */
+Node *tree_search(Tree *tree, long 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
+/*
+ * PgBouncer - Lightweight connection pooler for PostgreSQL.
+ *
+ * Copyright (c) 2007 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+typedef struct Node Node;
+typedef struct Tree Tree;
+
+typedef int (*tree_cmp_f)(long, Node *node);
+typedef void (*tree_walker_f)(Node *n, void *arg);
+
+/*
+ * Tree header, for storing helper functions.
+ */
+struct Tree {
+ Node *root;
+ int count;
+ tree_cmp_f node_cmp;
+ tree_walker_f release_cb;
+};
+
+/*
+ * Tree node.
+ */
+struct Node {
+ Node *left; /* smaller values */
+ Node *right; /* larger values */
+ int level; /* number of black nodes to leaf */
+};
+
+/*
+ * walk order
+ */
+enum TreeWalkType {
+ WALK_IN_ORDER = 0, /* left->self->right */
+ WALK_PRE_ORDER = 1, /* self->left->right */
+ WALK_POST_ORDER = 2, /* left->right->self */
+};
+
+void tree_init(Tree *tree, tree_cmp_f cmpfn, tree_walker_f release_cb);
+Node *tree_search(Tree *tree, long value);
+void tree_insert(Tree *tree, long value, Node *node);
+void tree_remove(Tree *tree, long value);
+void tree_walk(Tree *tree, enum TreeWalkType wtype, tree_walker_f walker, void *arg);
+void tree_destroy(Tree *tree);
+
--- /dev/null
+/*
+ * PgBouncer - Lightweight connection pooler for PostgreSQL.
+ *
+ * Copyright (c) 2007 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and 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.
+ */
+
+/*
+ * A version of Bob Jenkins' lookup3.c hash.
+ *
+ * It is supposed to give same results as hashlittle() on little-endian
+ * and hashbig() on big-endian machines.
+ */
+
+#include <sys/types.h>
+
+#include "hash.h"
+
+/* rotate uint32 */
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/*
+ * Disallow going over given data length.
+ * It is safe, if word boundary is not crossed,
+ * should be bit faster, although I have not noticed yet.
+ */
+#define STRICT_LENGTH 1
+
+/*
+ * Bob Jenkins hash mixing functions for 3 32bit integers.
+ */
+
+#define main_mix(a, b, c) do { \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+} while (0)
+
+#define final_mix(a, b, c) do { \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c, 4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+} while (0)
+
+/*
+ * Macros for fetching uint32_t from memory.
+ *
+ * Depending on alignment, it can be done with
+ * uint32, uint16 or uint8.
+ */
+
+/* load uint from proper pointer and shift t oposition */
+#define GET(ptr, pos, sft) (((uint32_t)((ptr)[pos])) << sft)
+
+#ifdef WORDS_BIGENDIAN
+
+#define LOAD_BYTES(v, p8, n) do { \
+ switch (n) { \
+ case 4: v += GET(p8, 0, 24) | GET(p8, 1, 16) | GET(p8, 2, 8) | GET(p8, 3, 0); break; \
+ case 3: v += GET(p8, 0, 24) | GET(p8, 1, 16) | GET(p8, 2, 8); break; \
+ case 2: v += GET(p8, 0, 24) | GET(p8, 1, 16); break; \
+ case 1: v += GET(p8, 0, 24); break; \
+ } \
+} while (0)
+
+#define LOAD_SHORTS(v, p16, n) do { \
+ switch (n) { \
+ case 4: v += GET(p16, 0, 16) | GET(p16, 1, 0); break; \
+ case 3: v += GET(p16, 0, 16) |(GET(p16, 1, 0) & 0xFF00); break; \
+ case 2: v += GET(p16, 0, 16); break; \
+ case 1: v += GET(p16, 0, 16) & 0xFF00; break; \
+ } \
+} while (0)
+
+#define LOAD_INTS(v, p32, n) do { \
+ switch (n) { \
+ case 4: v += GET(p32, 0, 0); break; \
+ case 3: v += GET(p32, 0, 0) & 0xFFFFFF00; break; \
+ case 2: v += GET(p32, 0, 0) & 0xFFFF0000; break; \
+ case 1: v += GET(p32, 0, 0) & 0xFF000000; break; \
+ } \
+} while (0)
+
+#else /* LITTLE-ENDIAN */
+
+#define LOAD_BYTES(v, p8, n) do { \
+ switch (n) { \
+ case 4: v += GET(p8, 0, 0) | GET(p8, 1, 8) | GET(p8, 2, 16) | GET(p8, 3, 24); break; \
+ case 3: v += GET(p8, 0, 0) | GET(p8, 1, 8) | GET(p8, 2, 16); break; \
+ case 2: v += GET(p8, 0, 0) | GET(p8, 1, 8); break; \
+ case 1: v += GET(p8, 0, 0); break; \
+ } \
+} while (0)
+
+#define LOAD_SHORTS(v, p16, n) do { \
+ switch (n) { \
+ case 4: v += GET(p16, 0, 0) | GET(p16, 1, 16); break; \
+ case 3: v += GET(p16, 0, 0) |(GET(p16, 1, 16) & 0x00FF); break; \
+ case 2: v += GET(p16, 0, 0); break; \
+ case 1: v += GET(p16, 0, 0) & 0x00FF; break; \
+ } \
+} while (0)
+
+#define LOAD_INTS(v, p32, n) do { \
+ switch (n) { \
+ case 4: v += GET(p32, 0, 0); break; \
+ case 3: v += GET(p32, 0, 0) & 0x00FFFFFF; break; \
+ case 2: v += GET(p32, 0, 0) & 0x0000FFFF; break; \
+ case 1: v += GET(p32, 0, 0) & 0x000000FF; break; \
+ } \
+} while (0)
+
+#endif /* LITTLE ENDIAN */
+
+/*
+ * combined fetching, also increases data pointer.
+ */
+#define LOAD(v, data, n, unit) do { \
+ if (n < 4 && unit > 1 && STRICT_LENGTH) { \
+ LOAD_BYTES(v, ((uint8_t *)data), n); \
+ } else { \
+ switch (unit) { \
+ case 4: LOAD_INTS(v, data, n); break; \
+ case 2: LOAD_SHORTS(v, data, n); break; \
+ case 1: LOAD_BYTES(v, data, n); break; \
+ } \
+ } \
+ data += n/unit; \
+} while (0)
+
+/*
+ * common main loop
+ */
+#define main_loop(data, len, a, b, c, unit) do { \
+ while (len > 12) { \
+ LOAD(a, data, 4, unit); \
+ LOAD(b, data, 4, unit); \
+ LOAD(c, data, 4, unit); \
+ main_mix(a, b, c); \
+ len -= 12; \
+ } \
+} while (0)
+
+/*
+ * fetch last 12 bytes into variables and mix them
+ */
+#define final_loop(data, len, a, b, c, unit) do { \
+ switch (len) { \
+ case 12: LOAD(a, data, 4, unit); LOAD(b, data, 4, unit); LOAD(c, data, 4, unit); break; \
+ case 11: LOAD(a, data, 4, unit); LOAD(b, data, 4, unit); LOAD(c, data, 3, unit); break; \
+ case 10: LOAD(a, data, 4, unit); LOAD(b, data, 4, unit); LOAD(c, data, 2, unit); break; \
+ case 9: LOAD(a, data, 4, unit); LOAD(b, data, 4, unit); LOAD(c, data, 1, unit); break; \
+ case 8: LOAD(a, data, 4, unit); LOAD(b, data, 4, unit); break; \
+ case 7: LOAD(a, data, 4, unit); LOAD(b, data, 3, unit); break; \
+ case 6: LOAD(a, data, 4, unit); LOAD(b, data, 2, unit); break; \
+ case 5: LOAD(a, data, 4, unit); LOAD(b, data, 1, unit); break; \
+ case 4: LOAD(a, data, 4, unit); break; \
+ case 3: LOAD(a, data, 3, unit); break; \
+ case 2: LOAD(a, data, 2, unit); break; \
+ case 1: LOAD(a, data, 1, unit); break; \
+ case 0: return c; \
+ } \
+ final_mix(a, b, c); \
+} while (0)
+
+/*
+ * common function body
+ */
+#define body(data, len, unit_type, unit) do { \
+ uint32_t a, b, c; \
+ const unit_type *ptr = data; \
+ a = b = c = 0xdeadbeef + len; \
+ main_loop(ptr, len, a, b, c, unit); \
+ final_loop(ptr, len, a, b, c, unit); \
+ return c; \
+} while (0)
+
+/*
+ * actual function
+ */
+unsigned lookup_hash(const void *data, unsigned len)
+{
+ if (((long)data & 3) == 0)
+ body(data, len, uint32_t, 4);
+ else if (((long)data & 1) == 0)
+ body(data, len, uint16_t, 2);
+ else
+ body(data, len, uint8_t, 1);
+}
+