[asterisk-commits] murf: branch murf/sched-rbtree r104072 - in /team/murf/sched-rbtree: include/...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sat Feb 23 18:36:53 CST 2008


Author: murf
Date: Sat Feb 23 18:36:52 2008
New Revision: 104072

URL: http://svn.digium.com/view/asterisk?view=rev&rev=104072
Log:
OK, this introduces rbtree.c and rbtree.h, derived from public domain source. See files for proper credits. sched.c is modified to use red-black trees instead of a linear list. sched.c was taken from the bug11210 branch, so it uses a hashtab to speed up the id search for deletion. The rbtree stuff replaces the doubly-linked lists used previously. The rbtree code was tested extensively using the main (now commented out), with up to a million nodes, added in order, reverse order, and random order. All operations on the tree were tested. The rbtree code looks real good, and works also. The sched code doesn't seem worth a lot of work, but in testing for max calls/sec on short-duration calls, it steps to the front of the crowd as a cpu cycle guzzler. Using a binary tree to implement an ascending priority queue is a classic approach, and using red-black tree is practical because insertion order can tend to be in-order. It's a little looser than AVL trees, and thanks to Julienne's n
 on-recursive implementation, it is wicked-fast, and hoped to do well. In some future moment, I'll compare performance, and toss this branch if it doesn't do noticeably better.

Added:
    team/murf/sched-rbtree/include/asterisk/rbtree.h   (with props)
    team/murf/sched-rbtree/main/rbtree.c   (with props)
Modified:
    team/murf/sched-rbtree/include/asterisk/sched.h
    team/murf/sched-rbtree/main/Makefile
    team/murf/sched-rbtree/main/sched.c
    team/murf/sched-rbtree/res/res_config_pgsql.c

Added: team/murf/sched-rbtree/include/asterisk/rbtree.h
URL: http://svn.digium.com/view/asterisk/team/murf/sched-rbtree/include/asterisk/rbtree.h?view=auto&rev=104072
==============================================================================
--- team/murf/sched-rbtree/include/asterisk/rbtree.h (added)
+++ team/murf/sched-rbtree/include/asterisk/rbtree.h Sat Feb 23 18:36:52 2008
@@ -1,0 +1,139 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2008, Steve Murphy
+ *
+ * Steve Murphy <murf at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+/*! \file
+ * \page Red Black Binary Tree Implementation
+ *
+ *  \brief This is an implementation of Red-Black Trees
+ *  targeted for use as a priority queue mechanism for
+ *  the sched package. It includes a main() function 
+ *  (normally ifdef-d out) for those who like to do things
+ *  like regression tests, etc.
+ *
+ *  This code is derived from an implementation by
+ *  Julienne Walker, whose tutorial on the subject
+ *  of Red-Black trees was lucid, easily understood,
+ *  and utterly delightful. My favorite quotes
+ *   "Many brain cells have perished trying to come up
+ *    with an easy way to guarantee this optimum structure..."
+ *   "Deletion of a node from a red black tree is a pain in
+ *    the <expletive deleted>."
+ *
+ *  Many thanks to Julienne's hard work and genius for
+ *  an excellent non-recursive rendition of the red-black
+ *  tree insertion and deletion algorithms, and her placement
+ *  of the code in the public domain.
+ *  (See http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx)
+ *  I took this code, and renamed the functions slightly, modified
+ *  them to use generic pointers, added test code, and some search
+ *  routines, etc.
+ *
+ *  \author Steve Murphy <murf at digium.com>
+ */
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+
+
+#ifndef _ASTERISK_RBTREE_H
+#define _ASTERISK_RBTREE_H
+
+struct ast_rbt_node {
+	int red;
+	void *object;
+	struct ast_rbt_node *link[2];
+};
+
+typedef int (ast_rbt_compare_fn)(const void *obj1, const void *obj2); /* returns -1 for obj1 < obj2; 0 for obj1 == obj2; 1 for obj1 > obj2 */
+typedef void (ast_rbt_destroy_fn)(const void *object);
+
+struct ast_rbt_tree {
+	struct ast_rbt_node *root;
+	ast_rbt_compare_fn *object_compare_fn; /* returns -1 for obj1 < obj2; 0 for obj1 == obj2; 1 for obj1 > obj2 */
+	ast_rbt_destroy_fn *object_destroy_fn;  /* free up object */
+};
+
+/* Everything the average programmer needs to do his/her job... */
+
+/*! \brief
+ * Create an rbtree structure and return its pointer.
+ * \param comp A function pointer to compare objects
+ * \param dest A function pointer to destroy the objects stored in the tree nodes. 
+ *        If you do not want destruction, pass a NULL pointer.
+ * \return a pointer to an ast_rbt_tree.
+ */
+struct ast_rbt_tree *ast_rbt_make_tree(ast_rbt_compare_fn *comp, ast_rbt_destroy_fn *dest);
+
+/*! \brief
+ * Destroy (free) an ast_rbt_tree. All nodes will be zero'd and freed.
+ * All objects will be freed also, if the destroy function was set during the ast_rbt_make_tree call.
+ * 
+ * \param tree A pointer to the tree
+ * \return a NULL pointer you can use to zero a pointer to the tree.
+ */
+struct ast_rbt_tree *ast_rbt_destroy_tree(struct ast_rbt_tree *tree);
+
+/*! \brief
+ * Check that RB tree is well-formed.
+ * \param tree A tree pointer
+ * \param root A pointer to a root node; the tree will be recursively probed.
+ * \return 1 if the root is NULL, 0 otherwise.
+ */
+int ast_rbt_assert (struct ast_rbt_tree *tree, struct ast_rbt_node *root);
+
+
+/*! \brief
+ * Check to see if the tree is empty
+ * \param tree A tree pointer
+ * \return 1 if the tree is empty, 0 otherwise.
+ */
+int ast_rbt_tree_empty(struct ast_rbt_tree *tree);
+
+/*! \brief
+ * Insert a node into the rbtree.
+ * \param tree a pointer to an ast_rbt_tree
+ * \param object The object you want associated with the node. An opaque pointer.
+ * \return 0 if there was a problem; 1 otherwise
+ */
+int ast_rbt_insert(struct ast_rbt_tree *tree, void *object);
+
+/*! \brief
+ * Remove a node from the ast_rbt_tree
+ * \param tree a pointer to an ast_rbt_tree
+ * \param object A pointer to an object. If the compare function returns 0 for a
+          match with a tree object, the matching object in the tree will be
+          removed.
+ * \return Always returns 1.
+ */
+int ast_rbt_remove(struct ast_rbt_tree *tree, void *object);
+
+/*! \brief
+ * Find a matching object in the ast_rbt_tree.
+ * \param tree an ast_rbt_tree pointer.
+ * \param object - an object pointer; look for a match in the tree.
+ * \return a pointer to the matching object..
+ */
+void *ast_rbt_find_object(struct ast_rbt_tree *tree, const void *object);
+
+/*! \brief
+ * return the first object in the tree
+ * \param tree an ast_rbt_tree pointer.
+ * \return an opaque pointer to the object that is the lowest valued in the tree.
+ */
+void *ast_rbt_get_first_object(struct ast_rbt_tree *tree);
+
+#endif

Propchange: team/murf/sched-rbtree/include/asterisk/rbtree.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/murf/sched-rbtree/include/asterisk/rbtree.h
------------------------------------------------------------------------------
    svn:keywords = Author Id Date Revision

Propchange: team/murf/sched-rbtree/include/asterisk/rbtree.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/murf/sched-rbtree/include/asterisk/sched.h
URL: http://svn.digium.com/view/asterisk/team/murf/sched-rbtree/include/asterisk/sched.h?view=diff&rev=104072&r1=104071&r2=104072
==============================================================================
--- team/murf/sched-rbtree/include/asterisk/sched.h (original)
+++ team/murf/sched-rbtree/include/asterisk/sched.h Sat Feb 23 18:36:52 2008
@@ -27,6 +27,7 @@
 extern "C" {
 #endif
 
+
 /*! \brief Max num of schedule structs
  * \note The max number of schedule structs to keep around
  * for use.  Undefine to disable schedule structure
@@ -38,10 +39,22 @@
 #define AST_SCHED_DEL(sched, id) \
 	do { \
 		int _count = 0; \
-		while (id > -1 && ast_sched_del(sched, id) && _count++ < 10) \
-			usleep(1); \
-		if (_count == 10) \
-			ast_log(LOG_WARNING, "Unable to cancel schedule ID %d.  This is probably a bug (%s: %s, line %d).\n", id, __FILE__, __PRETTY_FUNCTION__, __LINE__); \
+		while (id > -1 && ast_sched_del(sched, id) && ++_count < 10) \
+			usleep(1); \
+		if (_count == 10) \
+			ast_log(LOG_WARNING, "Unable to cancel schedule ID %d.  This is probably a bug (%s: %s, line %d).\n", id, __FILE__, __PRETTY_FUNCTION__, __LINE__); \
+		id = -1; \
+	} while (0);
+
+#define AST_SCHED_DEL_UNREF(sched, id, refcall)			\
+	do { \
+		int _count = 0; \
+		while (id > -1 && ast_sched_del(sched, id) && ++_count < 10) \
+			usleep(1); \
+		if (_count == 10) \
+			ast_log(LOG_WARNING, "Unable to cancel schedule ID %d.  This is probably a bug (%s: %s, line %d).\n", id, __FILE__, __PRETTY_FUNCTION__, __LINE__); \
+		if (id > -1) \
+			refcall; \
 		id = -1; \
 	} while (0);
 
@@ -57,6 +70,26 @@
 
 #define AST_SCHED_REPLACE(id, sched, when, callback, data) \
 		AST_SCHED_REPLACE_VARIABLE(id, sched, when, callback, data, 0)
+
+#define AST_SCHED_REPLACE_VARIABLE_UNREF(id, sched, when, callback, data, variable, unrefcall, addfailcall, refcall) \
+	do { \
+		int _count = 0, _res=1;											 \
+		void *_data = (void *)ast_sched_find_data(sched, id);			\
+		while (id > -1 && (_res = ast_sched_del(sched, id) && _count++ < 10)) \
+			usleep(1); \
+		if (!_res && _data)							\
+			unrefcall;	/* should ref _data! */		\
+		if (_count == 10) \
+			ast_log(LOG_WARNING, "Unable to cancel schedule ID %d.  This is probably a bug (%s: %s, line %d).\n", id, __FILE__, __PRETTY_FUNCTION__, __LINE__); \
+		id = ast_sched_add_variable(sched, when, callback, data, variable); \
+		if (id == -1)  \
+			addfailcall;	\
+		else \
+			refcall; \
+	} while (0);
+
+#define AST_SCHED_REPLACE_UNREF(id, sched, when, callback, data, unrefcall, addfailcall, refcall) \
+	AST_SCHED_REPLACE_VARIABLE_UNREF(id, sched, when, callback, data, 0, unrefcall, addfailcall, refcall)
 
 struct sched_context;
 
@@ -81,6 +114,14 @@
 typedef int (*ast_sched_cb)(const void *data);
 #define AST_SCHED_CB(a) ((ast_sched_cb)(a))
 
+struct ast_cb_names
+{
+	int numassocs;
+	char *list[10];
+	ast_sched_cb cblist[10];
+};
+char *ast_sched_report(struct sched_context *con, char *buf, int bufsiz, struct ast_cb_names *cbnames);
+		
 /*! \brief Adds a scheduled event
  * Schedule an event to take place at some point in the future.  callback
  * will be called with data as the argument, when milliseconds into the
@@ -135,6 +176,15 @@
  */
 int ast_sched_replace_variable(int old_id, struct sched_context *con, int when, ast_sched_cb callback, const void *data, int variable);
 
+	
+/*! \brief Find a sched structure and return the data field associated with it. 
+ * \param con scheduling context in which to search fro the matching id
+ * \param id ID of the scheduled item to find
+ * \return the data field from the matching sched struct if found; else return NULL if not found.
+ */
+
+const void *ast_sched_find_data(struct sched_context *con, int id);
+	
 /*! \brief Deletes a scheduled event
  * Remove this event from being run.  A procedure should not remove its own
  * event, but return 0 instead.  In most cases, you should not call this
@@ -144,6 +194,7 @@
  * \param id ID of the scheduled item to delete
  * \return Returns 0 on success, -1 on failure
  */
+
 int ast_sched_del(struct sched_context *con, int id);
 
 /*! \brief Determines number of seconds until the next outstanding event to take place

Modified: team/murf/sched-rbtree/main/Makefile
URL: http://svn.digium.com/view/asterisk/team/murf/sched-rbtree/main/Makefile?view=diff&rev=104072&r1=104071&r2=104072
==============================================================================
--- team/murf/sched-rbtree/main/Makefile (original)
+++ team/murf/sched-rbtree/main/Makefile Sat Feb 23 18:36:52 2008
@@ -30,7 +30,7 @@
 	cryptostub.o sha1.o http.o fixedjitterbuf.o abstract_jb.o \
 	strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \
 	astobj2.o hashtab.o global_datastores.o $(RESAMPLE_OBJS) version.o \
-	features.o
+	features.o rbtree.o
 
 # we need to link in the objects statically, not as a library, because
 # otherwise modules will not have them available if none of the static

Added: team/murf/sched-rbtree/main/rbtree.c
URL: http://svn.digium.com/view/asterisk/team/murf/sched-rbtree/main/rbtree.c?view=auto&rev=104072
==============================================================================
--- team/murf/sched-rbtree/main/rbtree.c (added)
+++ team/murf/sched-rbtree/main/rbtree.c Sat Feb 23 18:36:52 2008
@@ -1,0 +1,1028 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2008, Steve Murphy
+ *
+ * Steve Murphy <murf at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+/*! \file
+ *
+ *  \brief This is an implementation of Red-Black Trees
+ *  targeted for use as an ascending priority queue mechanism for
+ *  the sched package. It includes a main() function 
+ *  (normally ifdef-d out) for those who like to do things
+ *  like regression tests, etc.
+ *
+ *  This code is derived from an implementation by
+ *  Julienne Walker, whose tutorial on the subject
+ *  of Red-Black trees was lucid, easily understood,
+ *  and utterly delightful. My favorite quotes
+ *   "Many brain cells have perished trying to come up
+ *    with an easy way to guarantee this optimum structure..."
+ *   "Deletion of a node from a red black tree is a pain in
+ *    the <expletive deleted>."
+ *
+ *  Many thanks to Julienne's hard work and genius for
+ *  an excellent non-recursive rendition of the red-black
+ *  tree insertion and deletion algorithms, and her placement
+ *  of the code in the public domain.
+ *
+ *  (See http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx)
+ *
+ *  Julienne's code is (according to the above page), in the public domain,
+ *  so, it would be the changes and additions that I made that would be
+ *  licensed...I took this code, and renamed the functions slightly, modified
+ *  them to use generic pointers, added test code, and some search
+ *  routines, etc.
+ *
+ *  \author Steve Murphy <murf at digium.com>
+ */
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision")
+
+#include "asterisk/utils.h"
+#include "asterisk/rbtree.h"
+
+struct ast_rbt_tree *ast_rbt_make_tree(ast_rbt_compare_fn *comp, ast_rbt_destroy_fn *dest)
+{
+	struct ast_rbt_tree *x = ast_calloc(sizeof(struct ast_rbt_tree),1);
+	x->object_compare_fn = comp;
+	x->object_destroy_fn = dest;
+	return x;
+}
+
+static struct ast_rbt_node *ast_rbt_destroy_node(struct ast_rbt_tree *tree, struct ast_rbt_node *node)
+{
+	if (node) {
+		if (node->link[0])
+			node->link[0] = ast_rbt_destroy_node(tree, node->link[0]);
+		if (node->link[1])
+			node->link[1] = ast_rbt_destroy_node(tree, node->link[1]);
+		if (tree->object_destroy_fn)
+			(*tree->object_destroy_fn)(node->object);
+		node->object = NULL;
+		free(node);
+	}
+	return NULL;
+}
+
+static struct ast_rbt_node *make_node (void *object)
+{
+	struct ast_rbt_node *rn = ast_malloc(sizeof *rn);
+	
+	if (rn != NULL) {
+		rn->object = object;
+		rn->red = 1; /* 1 is red, 0 is black */
+		rn->link[0] = NULL;
+		rn->link[1] = NULL;
+	}
+	
+	return rn;
+}
+
+
+struct ast_rbt_tree *ast_rbt_destroy_tree(struct ast_rbt_tree *tree)
+{
+	if (tree->root) {
+		ast_rbt_destroy_node(tree, tree->root);
+	}
+	tree->root = 0;
+	free(tree);
+	return 0;
+}
+
+int ast_rbt_tree_empty(struct ast_rbt_tree *tree)
+{
+	if (tree->root)
+		return 1;
+	else
+		return 0;
+}
+
+/* helper funcs */
+
+static inline int is_red(struct ast_rbt_node *root) 
+{ 
+	return root != NULL && root->red == 1; 
+}
+
+static struct ast_rbt_node *ast_rbt_single(struct ast_rbt_node *root, int dir) 
+{ 
+	struct ast_rbt_node *save = root->link[!dir]; 
+	
+	root->link[!dir] = save->link[dir]; 
+	save->link[dir] = root; 
+	
+	root->red = 1; 
+	save->red = 0; 
+	
+	return save; 
+} 
+
+static struct ast_rbt_node *ast_rbt_double(struct ast_rbt_node *root, int dir) 
+{ 
+	root->link[!dir] = ast_rbt_single(root->link[!dir], !dir); 
+	return ast_rbt_single(root, dir); 
+}
+
+int ast_rbt_assert (struct ast_rbt_tree *tree, struct ast_rbt_node *root)
+{
+	int lh, rh;
+	
+	if (root == NULL)
+		return 1;
+	else {
+		struct ast_rbt_node *ln = root->link[0];
+		struct ast_rbt_node *rn = root->link[1];
+		
+		/* Consecutive red links */
+		if (is_red(root)) {
+			if (is_red(ln) || is_red(rn)) {
+				puts ("Red violation");
+				return 0;
+			}
+		}
+		
+		lh = ast_rbt_assert(tree, ln);
+		rh = ast_rbt_assert(tree, rn);
+		
+		/* Invalid binary search tree */
+		if ((ln != NULL && (*tree->object_compare_fn)(ln->object, root->object) >= 0)
+			|| (rn != NULL && (*tree->object_compare_fn)(rn->object, root->object) <= 0)) {
+			puts ("Binary tree violation");
+			return 0;
+		}
+		
+		/* Black height mismatch */
+		if (lh != 0 && rh != 0 && lh != rh) {
+			puts("Black violation");
+			return 0;
+		}
+		
+		/* Only count black links */
+		if (lh != 0 && rh != 0)
+			return is_red(root) ? lh : lh + 1;
+		else
+			return 0;
+	}
+}
+
+#ifdef RECURSIVE_FUNCS
+
+int ast_rbt_insert(struct ast_rbt_tree *tree, void *object)
+{
+	tree->root = ast_rbt_insert_r(tree->root, object);
+	tree->root->red = 0;
+	return 1;
+}
+
+
+struct ast_rbt_node *ast_rbt_insert_r(struct ast_rbt_tree *tree, struct ast_rbt_node *root, void *object)
+{
+	int cmp;
+	if (root == NULL)
+		root = make_node(object);
+	else if ((cmp = tree->object_compare_func(root->object, object)) != 0) {
+		/* originally: int dir = root->object < object; 1 if robj < obj; 0 otherwise */
+		int dir = cmp == -1; /* 1 if robj < obj; 0 otherwise */
+		
+		root->link[dir] = ast_rbt_insert_r(root->link[dir], object);
+		
+		if (is_red(root->link[dir])) {
+			if (is_red(root->link[!dir])) {
+				/* Case 1 */
+				root->red = 1;
+				root->link[0]->red = 0;
+				root->link[1]->red = 0;
+			}
+			else {
+				/* Cases 2 & 3 */
+				if (is_red(root->link[dir]->link[dir]))
+					root = ast_rbt_single(root, !dir);
+				else if (is_red(root->link[dir]->link[!dir]))
+					root = ast_rbt_double(root, !dir);
+			}
+		}
+	}
+	
+	return root;
+}
+
+
+struct ast_rbt_node *ast_rbt_remove_r(struct ast_rbt_tree *tree, struct ast_rbt_node *root, void *object, int *done)
+{
+	int cmp;
+	
+	if (root == NULL)
+		*done = 1;
+	else {
+		int dir;
+		
+		if ((cmp = (*tree->object_compare_fn)(root->object, object)) == 0) {
+			if (root->link[0] == NULL || root->link[1] == NULL) {
+				struct ast_rbt_node *save =
+					root->link[root->link[0] == NULL];
+				
+				/* Case 0 */
+				if (is_red(root))
+					*done = 1;
+				else if (is_red(save)) {
+					save->red = 0;
+					*done = 1;
+				}
+				
+				if (tree->object_destroy_fn)
+					(*tree->object_destroy_fn)(root);
+				
+				return save;
+			}
+			else {
+				struct ast_rbt_node *heir = root->link[0];
+				
+				while (heir->link[1] != NULL)
+					heir = heir->link[1];
+				
+				root->object = heir->object;
+				object = heir->object;
+			}
+		}
+		
+		dir = cmp == -1; /* 1 if robj < obj */
+		root->link[dir] = ast_rbt_remove_r(tree, root->link[dir], object, done);
+		
+		if (!*done)
+			root = ast_rbt_remove_balance(root, dir, done);
+	}
+	
+	return root;
+}
+
+int ast_rbt_remove(struct ast_rbt_tree *tree, void *object)
+{
+	int done = 0;
+	
+	tree->root = ast_rbt_remove_r(tree, tree->root, object, &done);
+	if (tree->root != NULL)
+		tree->root->red = 0;
+	
+	return 1;
+}
+
+struct ast_rbt_node *ast_rbt_remove_balance(struct ast_rbt_node *root, int dir, int *done)
+{
+	struct ast_rbt_node *p = root;
+	struct ast_rbt_node *s = root->link[!dir];
+	
+	/* Case reduction, remove red sibling */
+	if (is_red(s)) {
+		root = ast_rbt_single(root, dir);
+		s = p->link[!dir];
+	}
+	
+	if (s != NULL) {
+		if (!is_red(s->link[0]) && !is_red(s->link[1])) {
+			if (is_red(p))
+				*done = 1;
+			p->red = 0;
+			s->red = 1;
+		}
+		else {
+			int save = root->red;
+			int new_root = (root == p);
+			
+			if (is_red(s->link[!dir]))
+				p = ast_rbt_single(p, dir);
+			else
+				p = ast_rbt_double(p, dir);
+			
+			p->red = save;
+			p->link[0]->red = 0;
+			p->link[1]->red = 0;
+			
+			if (new_root)
+				root = p;
+			
+			*done = 1;
+		}
+	}
+	
+	return root;
+}
+
+#endif /* recursive versions of the insert/delete funcs */
+
+
+int ast_rbt_insert(struct ast_rbt_tree *tree, void *object)
+{
+	if (tree->root == NULL) {
+		/* Empty tree case */
+		tree->root = make_node(object);
+		if (tree->root == NULL)
+			return 0;
+	}
+	else {
+		struct ast_rbt_node head = {0}; /* False tree root */
+		
+		struct ast_rbt_node *g, *t;     /* Grandparent & parent */
+		struct ast_rbt_node *p, *q;     /* Iterator & parent */
+		int cmp;
+		int dir = 0, last;
+		
+		/* Set up helpers */
+		t = &head;
+		g = p = NULL;
+		q = t->link[1] = tree->root;
+		
+		/* Search down the tree */
+		for (; ;) {
+			if (q == NULL) {
+				/* Insert new node at the bottom */
+				p->link[dir] = q = make_node(object);
+				if (q == NULL)
+					return 0;
+			}
+			else if (is_red(q->link[0]) && is_red(q->link[1])) {
+				/* Color flip */
+				q->red = 1;
+				q->link[0]->red = 0;
+				q->link[1]->red = 0;
+			}
+			
+			/* Fix red violation */
+			if (is_red(q) && is_red(p)) {
+				int dir2 = t->link[1] == g;
+				
+				if (q == p->link[last])
+					t->link[dir2] = ast_rbt_single(g, !last);
+				else
+					t->link[dir2] = ast_rbt_double(g, !last);
+			}
+			
+			/* Stop if found */
+			if ((cmp = (*tree->object_compare_fn)(q->object, object)) == 0)
+				break;
+			
+			last = dir;
+			dir = (cmp == -1); /* 1 if qobj < obj */
+			
+			/* Update helpers */
+			if (g != NULL)
+				t = g;
+			g = p, p = q;
+			q = q->link[dir];
+		}
+		
+		/* Update root */
+		tree->root = head.link[1];
+	}
+	
+	/* Make root black */
+	tree->root->red = 0;
+	
+	return 1;
+}
+
+
+
+int ast_rbt_remove(struct ast_rbt_tree *tree, void *object)
+{
+	if (tree->root != NULL) {
+		struct ast_rbt_node head = {0}; /* False tree root */
+		struct ast_rbt_node *q, *p, *g; /* Helpers */
+		struct ast_rbt_node *f = NULL;  /* Found item */
+		int dir = 1;
+		int cmp;
+		
+		
+		/* Set up helpers */
+		q = &head;
+		g = p = NULL;
+		q->link[1] = tree->root;
+		
+		/* Search and push a red down */
+		while (q->link[dir] != NULL) {
+			int last = dir;
+			
+			/* Update helpers */
+			g = p, p = q;
+			q = q->link[dir];
+			cmp = (*tree->object_compare_fn)(q->object,object);
+			dir = (cmp == -1);
+			
+			/* Save found node */
+			if (cmp == 0)
+				f = q;
+			
+			/* Push the red node down */
+			if (!is_red(q) && !is_red(q->link[dir])) {
+				if (is_red(q->link[!dir]))
+					p = p->link[last] = ast_rbt_single(q, dir);
+				else if (!is_red(q->link[!dir])) {
+					struct ast_rbt_node *s = p->link[!last];
+					
+					if (s != NULL) {
+						if (!is_red(s->link[!last]) && !is_red(s->link[last])) {
+							/* Color flip */
+							p->red = 0;
+							s->red = 1;
+							q->red = 1;
+						}
+						else {
+							int dir2 = g->link[1] == p;
+							
+							if (is_red (s->link[last]))
+								g->link[dir2] = ast_rbt_double (p, last);
+							else if (is_red (s->link[!last]))
+								g->link[dir2] = ast_rbt_single (p, last);
+							
+							/* Ensure correct coloring */
+							q->red = g->link[dir2]->red = 1;
+							g->link[dir2]->link[0]->red = 0;
+							g->link[dir2]->link[1]->red = 0;
+						}
+					}
+				}
+			}
+		}
+		
+		/* Replace and remove if found */
+		if (f != NULL) {
+			f->object = q->object;
+			p->link[p->link[1] == q] =
+				q->link[q->link[0] == NULL];
+			if (tree->object_destroy_fn)
+				(*tree->object_destroy_fn)(q);
+			free(q);
+		}
+		
+		/* Update root and make it black */
+		tree->root = head.link[1];
+		if (tree->root != NULL)
+			tree->root->red = 0;
+	}
+	
+	return 1;
+}
+
+
+void *ast_rbt_find_object(struct ast_rbt_tree *tree, const void *object)
+{
+	if (tree->root != NULL) {
+		struct ast_rbt_node head = {0}; /* False tree root */
+		struct ast_rbt_node *q; /* Helpers */
+		int dir = 1;
+		int cmp;
+	
+		/* Set up helpers */
+		q = &head;
+		q->link[1] = tree->root;
+		
+		/* Search and push a red down */
+		while (q->link[dir] != NULL) {
+			
+			/* Update helpers */
+			q = q->link[dir];
+			cmp = (*tree->object_compare_fn)(q->object,object);
+			dir = (cmp == -1);
+			
+			/* Save found node */
+			if (cmp == 0) {
+				return q->object;
+			}
+		}
+	}
+	return NULL;
+}
+
+/* for the priority queue */
+void *ast_rbt_get_first_object(struct ast_rbt_tree *tree)
+{
+	struct ast_rbt_node *q = tree->root; /* Helpers */
+	while (q && q->link[0])
+		q = q->link[0];
+	if (q)
+		return q->object;
+	return NULL;
+}
+
+#ifdef TEST
+
+/* ========== */
+/* test funcs */
+/* ========== */
+
+/* in this test, the object is an int; */
+
+int int_cmp(const void *o1, const void *o2)
+{
+	const int i1 = (int)o1;
+	const int i2 = (int)o2;
+	
+	if (i1 < i2)
+		return -1;
+	else if (i1 == i2)
+		return 0;
+	else
+		return 1;
+}
+
+/* for timing */
+
+void tvdiff( struct timeval *tstart, struct timeval *tend, struct timeval *tdiff)
+{
+	tdiff->tv_usec = tend->tv_usec + 1000000 - tstart->tv_usec;
+	if (tdiff->tv_usec >= 1000000) {
+		tdiff->tv_usec -= 1000000;
+		tdiff->tv_sec = tend->tv_sec - tstart->tv_sec;
+	} else {
+		tdiff->tv_sec = tend->tv_sec - tstart->tv_sec - 1;
+	}
+}
+
+	
+void loadrevorder(struct ast_rbt_tree *tree, struct timeval *diff, int size)
+{
+	struct timeval start, end;
+	int i;
+	
+	gettimeofday(&start, NULL);
+	for (i=size;i>0;i--) {
+		ast_rbt_insert(tree, (void*)i);
+	}
+	gettimeofday(&end, NULL);
+	tvdiff(&start, &end, diff);
+}
+
+void loadfororder(struct ast_rbt_tree *tree, struct timeval *diff, int size)
+{
+	struct timeval start, end;
+	int i;
+	
+	gettimeofday(&start, NULL);
+	for (i=0;i<size;i++) {
+		ast_rbt_insert(tree, (void*)i);
+	}
+	gettimeofday(&end, NULL);
+	tvdiff(&start, &end, diff);
+}
+
+/* random numbers */
+unsigned int glob_seed = 0;
+
+static int my_rand(int incl_low, int incl_high, unsigned int *seedp)
+{
+	if (incl_high == 0)
+		return 0;
+	
+	return incl_low + (rand_r(seedp) % incl_high);
+}
+
+/* test cycles */
+
+void loadrandorder(struct ast_rbt_tree *tree, struct timeval *diff, int size)
+{
+	struct timeval start, end;
+	int i,r,x;
+	static int scoreboard[1000000];
+
+	for (i=0; i<size; i++)
+		scoreboard[i] = i+1;
+
+	for (i=0; i<size; i++) {
+		r = my_rand(0,size-1,&glob_seed);
+		x = scoreboard[i];
+		scoreboard[i] = scoreboard[r];
+		scoreboard[r] = x;
+	}
+			
+	gettimeofday(&start, NULL);
+	for (i=0;i<size;i++) {
+		ast_rbt_insert(tree, (void*)scoreboard[i]);
+	}
+	gettimeofday(&end, NULL);
+	tvdiff(&start, &end, diff);
+}
+
+void delindividnodes(struct ast_rbt_tree *tree, struct timeval *diff, int size)
+{
+	struct timeval start, end;
+	int i;
+	
+	gettimeofday(&start, NULL);
+	for (i=0;i<size;i++) {
+		ast_rbt_remove(tree, (void*)(i+1));
+	}
+	gettimeofday(&end, NULL);
+	tvdiff(&start, &end, diff);
+}
+
+void destroytree(struct ast_rbt_tree *tree, struct timeval *diff)
+{
+	struct timeval start, end;
+
+	gettimeofday(&start, NULL);
+	ast_rbt_destroy_tree(tree);
+	gettimeofday(&end, NULL);
+	tvdiff(&start, &end, diff);
+}
+
+void verifytree(struct ast_rbt_tree *tree, struct timeval *diff)
+{
+	struct timeval start, end;
+
+	gettimeofday(&start, NULL);
+	ast_rbt_assert(tree, tree->root);
+	gettimeofday(&end, NULL);
+	tvdiff(&start, &end, diff);
+}
+
+void findtreerevorder(struct ast_rbt_tree *tree, struct timeval *diff, int size)
+{
+	struct timeval start, end;
+	void *res;
+	int num;
+	int i;
+	
+	gettimeofday(&start, NULL);
+	for (i=size;i>0;i--) {
+		res = ast_rbt_find_object(tree, (void*)i);
+		num = (int)res;
+		if (num != i)
+			printf("Didn't find %d\n", i);
+	}
+	gettimeofday(&end, NULL);
+	tvdiff(&start, &end, diff);
+}
+
+void findtreefororder(struct ast_rbt_tree *tree, struct timeval *diff, int size)
+{
+	struct timeval start, end;
+	void *res;
+	int num;
+	int i;
+	
+	gettimeofday(&start, NULL);
+	for (i=0;i<size;i++) {
+		res = ast_rbt_find_object(tree, (void*)i);
+		num = (int)res;
+		if (num != i)
+			printf("Didn't find %d\n", i);
+	}
+	gettimeofday(&end, NULL);
+	tvdiff(&start, &end, diff);
+}
+
+void unload_from_bottom(struct ast_rbt_tree *tree, struct timeval *diff)
+{
+	struct timeval start, end;
+	void *res;
+	int i=0;
+	
+	gettimeofday(&start, NULL);
+	
+	while ( (res=ast_rbt_get_first_object(tree))) {
+		if ( (int)res <= i)
+			printf("get_first didn't get the first\n");
+		i = (int)res;
+		ast_rbt_remove(tree, res);
+	}
+
+	gettimeofday(&end, NULL);
+	tvdiff(&start, &end, diff);
+}
+
+
+main(int argc,char **argv)
+{
+	struct ast_rbt_tree *tree = ast_rbt_make_tree(int_cmp, NULL);
+	int i;
+	struct timeval start, end, diff;
+
+	glob_seed = (unsigned int)time(0);
+	srand(glob_seed);
+	
+	/* load up a tree in reverse order */
+	loadrevorder(tree, &diff, 10000);
+	printf("Load up a tree in rev. order, 10K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 10K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,10000);
+	printf("Lookup all elements in tree, 10K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 10000);
+	printf("Delete all elements in tree, one by one, 10K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 10K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+#ifdef OVERKILL
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrevorder(tree, &diff, 50000);
+	printf("Load up a tree in rev. order, 50K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 50K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,50000);
+	printf("Lookup all elements in tree, 50K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 50000);
+	printf("Delete all elements in tree, one by one, 50K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 50K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrevorder(tree, &diff, 100000);
+	printf("Load up a tree in rev. order, 100K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 100K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,100000);
+	printf("Lookup all elements in tree, 100K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 100000);
+	printf("Delete all elements in tree, one by one, 100K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 100K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrevorder(tree, &diff, 200000);
+	printf("Load up a tree in rev. order, 200K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 200K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,200000);
+	printf("Lookup all elements in tree, 200K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 200000);
+	printf("Delete all elements in tree, one by one, 200K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 200K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrevorder(tree, &diff, 300000);
+	printf("Load up a tree in rev. order, 300K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 300K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,300000);
+	printf("Lookup all elements in tree, 300K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 300000);
+	printf("Delete all elements in tree, one by one, 300K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 300K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrevorder(tree, &diff, 400000);
+	printf("Load up a tree in rev. order, 400K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 400K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,400000);
+	printf("Lookup all elements in tree, 400K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 400000);
+	printf("Delete all elements in tree, one by one, 400K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 400K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrevorder(tree, &diff, 500000);
+	printf("Load up a tree in rev. order, 500K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 500K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,500000);
+	printf("Lookup all elements in tree, 500K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 500000);
+	printf("Delete all elements in tree, one by one, 500K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 500K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrevorder(tree, &diff, 1000000);
+	printf("Load up a tree in rev. order, 1M nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 1M nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,1000000);
+	printf("Lookup all elements in tree, 1M nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 1000000);
+	printf("Delete all elements in tree, one by one, 1M nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 1M nodes: %d.%06d sec\n\n\n\n", diff.tv_sec, diff.tv_usec);
+
+#endif
+
+	/* load up a tree in random order */
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrandorder(tree, &diff, 10000);
+	printf("Load up a tree in rand. order, 10K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 10K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,10000);
+	printf("Lookup all elements in tree, 10K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 10000);
+	printf("Delete all elements in tree, one by one, 10K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 10K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+#ifdef OVERKILL
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrandorder(tree, &diff, 50000);
+	printf("Load up a tree in rand. order, 50K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 50K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,50000);
+	printf("Lookup all elements in tree, 50K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 50000);
+	printf("Delete all elements in tree, one by one, 50K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 50K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrandorder(tree, &diff, 100000);
+	printf("Load up a tree in rand. order, 100K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 100K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,100000);
+	printf("Lookup all elements in tree, 100K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 100000);
+	printf("Delete all elements in tree, one by one, 100K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 100K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrandorder(tree, &diff, 200000);
+	printf("Load up a tree in rand. order, 200K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 200K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,200000);
+	printf("Lookup all elements in tree, 200K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 200000);
+	printf("Delete all elements in tree, one by one, 200K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 200K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrandorder(tree, &diff, 300000);
+	printf("Load up a tree in rand. order, 300K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 300K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,300000);
+	printf("Lookup all elements in tree, 300K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 300000);
+	printf("Delete all elements in tree, one by one, 300K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 300K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrandorder(tree, &diff, 400000);
+	printf("Load up a tree in rand. order, 400K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 400K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,400000);
+	printf("Lookup all elements in tree, 400K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 400000);
+	printf("Delete all elements in tree, one by one, 400K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 400K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrandorder(tree, &diff, 500000);
+	printf("Load up a tree in rand. order, 500K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	verifytree(tree, &diff);
+	printf("Verify a tree, 500K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	findtreerevorder(tree, &diff,500000);
+	printf("Lookup all elements in tree, 500K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	delindividnodes(tree, &diff, 500000);
+	printf("Delete all elements in tree, one by one, 500K nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);
+	destroytree(tree, &diff);
+	printf("Destroy a tree, 500K nodes: %d.%06d sec\n\n", diff.tv_sec, diff.tv_usec);
+
+	tree = ast_rbt_make_tree(int_cmp, NULL);
+	loadrandorder(tree, &diff, 1000000);
+	printf("Load up a tree in rand. order, 1M nodes: %d.%06d sec\n", diff.tv_sec, diff.tv_usec);

[... 643 lines stripped ...]



More information about the asterisk-commits mailing list