[asterisk-commits] murf: branch murf/fast-ast2 r87039 - /team/murf/fast-ast2/main/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Oct 25 12:19:05 CDT 2007


Author: murf
Date: Thu Oct 25 12:19:04 2007
New Revision: 87039

URL: http://svn.digium.com/view/asterisk?view=rev&rev=87039
Log:
Just the hashtab stuff folded back in; pulled the latest hashtab out of datastructs. It compiles.

Modified:
    team/murf/fast-ast2/main/Makefile
    team/murf/fast-ast2/main/pbx.c

Modified: team/murf/fast-ast2/main/Makefile
URL: http://svn.digium.com/view/asterisk/team/murf/fast-ast2/main/Makefile?view=diff&rev=87039&r1=87038&r2=87039
==============================================================================
--- team/murf/fast-ast2/main/Makefile (original)
+++ team/murf/fast-ast2/main/Makefile Thu Oct 25 12:19:04 2007
@@ -27,7 +27,7 @@
 	netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
 	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
+	astobj2.o hashtab.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

Modified: team/murf/fast-ast2/main/pbx.c
URL: http://svn.digium.com/view/asterisk/team/murf/fast-ast2/main/pbx.c?view=diff&rev=87039&r1=87038&r2=87039
==============================================================================
--- team/murf/fast-ast2/main/pbx.c (original)
+++ team/murf/fast-ast2/main/pbx.c Thu Oct 25 12:19:04 2007
@@ -67,6 +67,7 @@
 #include "asterisk/devicestate.h"
 #include "asterisk/stringfields.h"
 #include "asterisk/event.h"
+#include "asterisk/hashtab.h"
 #include "asterisk/module.h"
 
 /*!
@@ -76,6 +77,17 @@
  * aspects of this PBX.  The switching scheme as it exists right now isn't
  * terribly bad (it's O(N+M), where N is the # of extensions and M is the avg #
  * of priorities, but a constant search time here would be great ;-)
+ *
+ * A new algorithm to do searching based on a 'compiled' pattern tree is introduced
+ * here, and shows a fairly flat (constant) search time, even for over
+ * 1000 patterns. Still needs some work-- there are some fine points of the matching
+ * spec about tie-breaking based on the characters in character sets, but this
+ * should be do-able via the weight system currently being used.
+ *
+ * Also, using a hash table for context/priority name lookup can help prevent
+ * the find_extension routines from absorbing exponential cpu cycles. I've tested
+ * find_extension with red-black trees, which have O(log2(n)) speed.Right now,
+ * I'm using hash tables, which do searches (ideally) in O(1) time.
  *
  */
 
@@ -132,6 +144,8 @@
 	void *data;			/*!< Data to use (arguments) */
 	void (*datad)(void *);		/*!< Data destructor */
 	struct ast_exten *peer;		/*!< Next higher priority with our extension */
+	struct ast_hashtab *peer_tree;    /*!< Priorities list in tree form -- only on the head of the peer list */
+	struct ast_hashtab *peer_label_tree; /*!< labeled priorities in the peer list -- only on the head of the peer list */
 	const char *registrar;		/*!< Registrar */
 	struct ast_exten *next;		/*!< Extension with a greater ID */
 	char stuff[0];
@@ -166,10 +180,33 @@
 	const char pattern[0];
 };
 
+/*! \brief match_char: forms a syntax tree for quick matching of extension patterns */
+struct match_char
+{
+	int is_pattern; /* the pattern started with '_' */
+	char *x;
+	int specificity; /* simply the strlen of x, or 10 for X, 9 for Z, and 8 for N; and '.' and '!' will add 11 ? */
+	struct match_char *alt_char;
+	struct match_char *next_char;
+	struct ast_exten *exten; /* attached to last char of a pattern for exten */
+};
+
+struct scoreboard  /* make sure all fields are 0 before calling new_find_extension */
+{
+	int total_specificity;
+	int total_length;
+	char last_char;   /* set to ! or . if they are the end of the pattern */
+	int canmatch;     /* if the string to match was just too short */
+	struct ast_exten *canmatch_exten;
+	struct ast_exten *exten;
+};
+
 /*! \brief ast_context: An extension context */
 struct ast_context {
 	ast_rwlock_t lock; 			/*!< A lock to prevent multiple threads from clobbering the context */
 	struct ast_exten *root;			/*!< The root of the list of extensions */
+	struct ast_hashtab *root_tree;            /*!< The root of the list of extensions in threaded red-black tree form */
+ 	struct match_char *pattern_tree;        /*!< A tree to speed up extension pattern matching */
 	struct ast_context *next;		/*!< Link them together */
 	struct ast_include *includes;		/*!< Include other contexts */
 	struct ast_ignorepat *ignorepats;	/*!< Patterns for which to continue playing dialtone */
@@ -283,6 +320,75 @@
 static int pbx_builtin_setvar_multiple(struct ast_channel *, void *);
 static int pbx_builtin_importvar(struct ast_channel *, void *);
 static void set_ext_pri(struct ast_channel *c, const char *exten, int pri);
+void new_find_extension(const char *str, struct scoreboard *score, struct match_char *tree, int length, int spec, const char *callerid);
+struct match_char *already_in_tree(struct match_char *current, char *pat);
+struct match_char *add_pattern_node(struct ast_context *con, struct match_char *current, char *pattern, int is_pattern, int already, int specificity);
+struct match_char *create_match_char_tree(struct ast_context *con);
+struct ast_exten *get_canmatch_exten(struct match_char *node);
+static int matchcid(const char *cidpattern, const char *callerid);
+static int hashtab_compare_contexts(const void *ah_a, const void *ah_b);
+static int hashtab_compare_extens(const void *ha_a, const void *ah_b);
+static int hashtab_compare_exten_numbers(const void *ah_a, const void *ah_b);
+static int hashtab_compare_exten_labels(const void *ah_a, const void *ah_b);
+static int hashtab_hash_contexts(const void *obj);
+static int hashtab_hash_extens(const void *obj);
+static int hashtab_hash_priority(const void *obj);
+static int hashtab_hash_labels(const void *obj);
+
+/* labels, contexts are case sensitive  priority numbers are ints */
+static int hashtab_compare_contexts(const void *ah_a, const void *ah_b)
+{
+	const struct ast_context *ac = ah_a;
+	const struct ast_context *bc = ah_b;
+	/* assume context names are registered in a string table! */
+	return ac->name != bc->name;
+}
+
+static int hashtab_compare_extens(const void *ah_a, const void *ah_b)
+{
+	const struct ast_exten *ac = ah_a;
+	const struct ast_exten *bc = ah_b;
+	return strcmp(ac->exten, bc->exten);
+}
+
+static int hashtab_compare_exten_numbers(const void *ah_a, const void *ah_b)
+{
+	const struct ast_exten *ac = ah_a;
+	const struct ast_exten *bc = ah_b;
+	return ac->priority != bc->priority;
+}
+
+static int hashtab_compare_exten_labels(const void *ah_a, const void *ah_b)
+{
+	const struct ast_exten *ac = ah_a;
+	const struct ast_exten *bc = ah_b;
+	return ac->label != bc->label;
+}
+
+static int hashtab_hash_contexts(const void *obj)
+{
+	const struct ast_context *ac = obj;
+	return ast_hashtab_hash_string(ac->name);
+}
+
+static int hashtab_hash_extens(const void *obj)
+{
+	const struct ast_exten *ac = obj;
+	return ast_hashtab_hash_string(ac->exten);
+}
+
+static int hashtab_hash_priority(const void *obj)
+{
+	const struct ast_exten *ac = obj;
+	return ast_hashtab_hash_int(ac->priority);
+}
+
+static int hashtab_hash_labels(const void *obj)
+{
+	const struct ast_exten *ac = obj;
+	return ast_hashtab_hash_string(ac->label);
+}
+
 
 AST_RWLOCK_DEFINE_STATIC(globalslock);
 static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
@@ -554,6 +660,8 @@
 };
 
 static struct ast_context *contexts;
+static struct ast_hashtab *contexts_tree = NULL;
+
 AST_RWLOCK_DEFINE_STATIC(conlock); 		/*!< Lock for the ast_context list */
 
 static AST_RWLIST_HEAD_STATIC(apps, ast_app);
@@ -647,6 +755,296 @@
 static void pbx_destroy(struct ast_pbx *p)
 {
 	ast_free(p);
+}
+
+/* form a tree that fully describes all the patterns in a context's extensions 
+ * in this tree, a "node" consists of a series of match_char structs linked in a chain
+ * via the alt_char pointers. More than one pattern can share the same parts of the 
+ * tree as other extensions with the same pattern to that point. The algorithm to
+ * find which pattern best matches a string, would follow **all** matching paths. As
+ * complete matches are found, a "max" match record would be updated if the match first involves 
+ * a longer string, then, on a tie, a smaller total of specificity. This can be accomplished
+ * by recursive calls affecting a shared scoreboard.
+ * As and example, consider these 4 extensions:
+ * (a) NXXNXXXXXX
+ * (b) 307754XXXX 
+ * (c) fax
+ * (d) NXXXXXXXXX
+ *
+ * In the above, between (a) and (d), (a) is a more specific pattern than (d), and would win over
+ * most numbers. For all numbers beginning with 307754, (b) should always win.
+ *
+ * These pattern should form a tree that looks like this:
+ *   { "N" }  --next-->  { "X" }  --next--> { "X" } --next--> { "N" } --next--> { "X" } ... blah ... --> { "X" exten_match: (a) }
+ *      |                                                        |
+ *      |                                                        |alt
+ *      |alt                                                     |
+ *      |                                                     { "X" } --next--> { "X" } ... blah ... --> { "X" exten_match: (d) }
+ *      |
+ *   { "3" }  --next-->  { "0" }  --next--> { "7" } --next--> { "7" } --next--> { "5" } ... blah ... --> { "X" exten_match: (b) }
+ *      |
+ *      |alt
+ *      |
+ *   { "f" }  --next-->  { "a" }  --next--> { "x"  exten_match: (c) }
+ *
+ *   In the above, I could easily turn "N" into "23456789", but I think that a quick "if( *z >= '2' && *z <= '9' )" might take
+ *   fewer CPU cycles than a call to index("23456789",*z), where *z is the char to match...
+ *
+ *   traversal is pretty simple: one routine merely traverses the alt list, and for each match in the pattern,  it calls itself
+ *   on the corresponding next pointer, incrementing also the pointer of the string to be matched, and passing the total specificity and length.
+ *   We pass a pointer to a scoreboard down through, also.
+ *   When an exten_match pointer is set, or a '.' or a '!' is encountered, we update the scoreboard only if the length is greater, or in case
+ *   of equal length, if the specificity is lower, and return. Hope the limit on stack depth won't be a problem... this routine should 
+ *   be pretty lean as far a stack usage goes. Any non-match terminates the recursion down a branch.
+ *
+ *   In the above example, with the number "3077549999" as the pattern, the traversor should match extensions a, b and d.  All are
+ *   of length 10; but they have total specificities of  96, 46, and 98, respectively. (b) wins with its lower specificity number!
+ *
+ *   Just how much time this algorithm might save over a plain linear traversal over all possible patterns is unknown. But, it should
+ *   be pretty close to optimal for this sort of overall algorithm. The only other improvement in speed I can imagine would involve locality
+ *   of reference. For instance, if all that is wanted is the next priority in an extension, then, it would be wasteful to try matching
+ *   extensions at that point in time! This is an easy optimization.
+ *
+ * */
+
+/* you only really update the scoreboard, if the new score is BETTER than the 
+ * one stored there. ignore it otherwise.
+ */
+
+
+static void update_scoreboard(struct scoreboard *board, int length, int spec, struct ast_exten *exten, char last, const char *callerid)
+{
+	/* wait a minute, here's where we check that matchcid stuff... if they mismatch,
+	 * then there's no use in continuing the rest of the checks! */
+	if (exten->matchcid && !matchcid(exten->cidmatch, callerid))
+		return;       /* matchcid's didn't-- so this isn't a matching candidate */
+	else if (exten->matchcid)
+		length++;    /* if a cid match is made, then that should count at least as an extra matching digit */
+
+	if (length > board->total_length) {
+		board->total_specificity = spec;
+		board->total_length = length;
+		board->exten = exten;
+		board->last_char = last;
+	} else if (length == board->total_length && spec < board->total_specificity) {
+		board->total_specificity = spec;
+		board->total_length = length;
+		board->exten = exten;
+		board->last_char = last;
+	}
+}
+
+struct ast_exten *get_canmatch_exten(struct match_char *node)
+{
+	/* find the exten at the end of the rope */
+	struct match_char *node2 = node;
+	for (node2 = node; node2; node2 = node2->next_char)
+		if (node2->exten)
+			return node2->exten;
+	return 0;
+}
+
+void new_find_extension(const char *str, struct scoreboard *score, struct match_char *tree, int length, int spec, const char *callerid)
+{
+	struct match_char *p; /* note minimal stack storage requirements */
+
+	for (p=tree; p; p=p->alt_char)
+	{
+		if (p->x[0] == 'N' && p->x[1] == 0 && *str >= '2' && *str <= '9' ) {
+			if (p->next_char && *(str+1))
+				new_find_extension(str+1, score, p->next_char, length+1, spec+8, callerid);
+			else if (p->exten) {
+				update_scoreboard(score, length+1, spec+8, p->exten,0,callerid);
+				return;
+			} else if (p->next_char && !*(str+1)) {
+				score->canmatch = 1;
+				score->canmatch_exten = get_canmatch_exten(p);
+			} else {
+				return;
+			}
+		} else if (p->x[0] == 'Z' && p->x[1] == 0 && *str >= '1' && *str <= '9' ) {
+			if (p->next_char && *(str+1))
+				new_find_extension(str+1, score, p->next_char, length+1, spec+9, callerid);
+			else if (p->exten) {
+				update_scoreboard(score, length+1, spec+9, p->exten,0, callerid);
+				return;
+			} else if (p->next_char && !*(str+1)) {
+				score->canmatch = 1;
+				score->canmatch_exten = get_canmatch_exten(p);
+			} else {
+				return;
+			}
+		} else if (p->x[0] == 'X' && p->x[1] == 0 && *str >= '0' && *str <= '9' ) {
+			if (p->next_char && *(str+1))
+				new_find_extension(str+1, score, p->next_char, length+1, spec+10, callerid);
+			else if (p->exten) {
+				update_scoreboard(score, length+1, spec+10, p->exten,0, callerid);
+				return;
+			} else if (p->next_char && !*(str+1)) {
+				score->canmatch = 1;
+				score->canmatch_exten = get_canmatch_exten(p);
+			} else {
+				return;
+			}
+		} else if (p->x[0] == '.' && p->x[1] == 0 ) {
+			update_scoreboard(score, length+1, spec+11, p->exten, '.', callerid);
+			return;
+		} else if (p->x[0] == '!' && p->x[1] == 0 ) {
+			update_scoreboard(score, length+1, spec+11, p->exten, '!', callerid);
+			return;
+		} else if (index(p->x, *str)) {
+			if (p->next_char && *(str+1))
+				new_find_extension(str+1, score, p->next_char, length+1, spec+p->specificity, callerid);
+			else if (p->exten) {
+				update_scoreboard(score, length+1, spec+p->specificity, p->exten,0, callerid);
+				return;
+			} else if (p->next_char && !*(str+1)) {
+				score->canmatch = 1;
+				score->canmatch_exten = get_canmatch_exten(p);
+			} else {
+				return;
+			}
+
+		}
+	}
+}
+
+/* the algorithm for forming the extension pattern tree is also a bit simple; you 
+ * traverse all the extensions in a context, and for each char of the extension,
+ * you see if it exists in the tree; if it doesn't, you add it at the appropriate
+ * spot. What more can I say? At the end of the list, you cap it off by adding the
+ * address of the extension involved. Duplicate patterns will be complained about.
+ *
+ * Ideally, this would be done for each context after it is created and fully 
+ * filled. It could be done as a finishing step after extensions.conf or .ael is
+ * loaded, or it could be done when the first search is encountered. It should only
+ * have to be done once, until the next unload or reload.
+ *
+ * I guess forming this pattern tree would be analogous to compiling a regex.
+ */
+
+
+struct match_char *already_in_tree(struct match_char *current, char *pat)
+{
+	struct match_char *t;
+	if (!current)
+		return 0;
+	for (t=current; t; t=t->alt_char) {
+		if (strcmp(pat,t->x) == 0) /* uh, we may want to sort exploded [] contents to make matching easy */
+			return t;
+	}
+	return 0;
+}
+
+struct match_char *add_pattern_node(struct ast_context *con, struct match_char *current, char *pattern, int is_pattern, int already, int specificity)
+{
+	struct match_char *m = ast_calloc(1,sizeof(struct match_char));
+	m->x = strdup(pattern);
+	m->is_pattern = is_pattern;
+	if (specificity == 1 && is_pattern && pattern[0] == 'N')
+		m->specificity = 8;
+	else if (specificity == 1 && is_pattern && pattern[0] == 'Z')
+		m->specificity = 9;
+	else if (specificity == 1 && is_pattern && pattern[0] == 'X')
+		m->specificity = 10;
+	else if (specificity == 1 && is_pattern && pattern[0] == '.')
+		m->specificity = 11;
+	else if (specificity == 1 && is_pattern && pattern[0] == '!')
+		m->specificity = 11;
+	else
+		m->specificity = specificity;
+	
+	if (!con->pattern_tree) {
+		con->pattern_tree = m;
+	} else {
+		if (already) { /* switch to the new regime (traversing vs appending)*/
+			m->alt_char = current->alt_char;
+			current->alt_char = m;
+		} else {
+			if (current->next_char) {
+				m->alt_char = current->next_char->alt_char;
+				current->next_char = m;
+			} else {
+				current->next_char = m;
+			}
+		}
+	}
+	return m;
+}
+
+struct match_char *create_match_char_tree(struct ast_context *con)
+{
+	struct ast_hashtab_iter *t1;
+	struct ast_exten *e1;
+	struct match_char *m1,*m2;
+	char buf[256];
+	int already;
+	int specif;
+	
+	t1 = ast_hashtab_start_traversal(con->root_tree);
+	while( (e1 = ast_hashtab_next(t1)) ) {
+		int pattern = 0;
+		char *s1 = e1->exten;
+		m1 = con->pattern_tree; /* each pattern starts over at the root of the pattern tree */
+		already = 1;
+
+		if ( *s1 == '_') {
+			pattern = 1;
+			s1++;
+		}
+		while( *s1 )
+		{
+			if (pattern && *s1 == '[' && *(s1-1) != '\\') {
+				char *s2 = buf;
+				while (*s1 != ']' && *(s1-1) != '\\' ) {
+					if (*s1 == '\\') {
+						if (*(s1+1) == ']') {
+							*s2++ = ']';
+							s1++;s1++;
+						} else if (*(s1+1) == '\\') {
+							*s2++ = '\\';
+							s1++;s1++;
+						} else if (*(s1+1) == '-') {
+							*s2++ = '-';
+							s1++; s1++;
+						} else if (*(s1+1) == '[') {
+							*s2++ = '[';
+							s1++; s1++;
+						}
+					} else if (*s1 == '-') { /* remember to add some error checking to all this! */
+						char s3 = *(s1-1);
+						char s4 = *(s1+1);
+						for (s3++; s3 <= s4; s3++) {
+							*s2++ = s3;
+						}
+						s1++; s1++;
+					} else {
+						*s2++ = *s1++;
+					}
+				}
+				specif = strlen(buf);
+			} else {
+				if (*s1 == '\\') {
+					s1++;
+					buf[0] = *s1;
+				} else {
+					buf[0] = *s1;
+				}
+				buf[1] = 0;
+				specif = 1;
+			}
+
+			if (already && (m2=already_in_tree(m1,buf))) {
+				m1 = m2->next_char; /* m1 points to the node to compare against */
+			} else {
+				m1 = add_pattern_node(con, m1, buf, pattern, already,specif); /* m1 is the node just added */
+				already = 0;
+			}
+			s1++; /* advance to next char */
+		}
+		m1->exten =  e1; /* that last node should have an exten pointer */
+	}
+	return m1; /* just in case you want to see how the pattern ended */
 }
 
 /*
@@ -938,14 +1336,35 @@
 	return extension_match_core(pattern, data, needmore);
 }
 
+struct fake_context /* this struct is purely for matching in the hashtab */
+{
+	ast_mutex_t lock; 			
+	struct ast_exten *root;		
+	struct trb_table *root_tree;            
+	struct match_char *pattern_tree;       
+	struct ast_context *next;	
+	struct ast_include *includes;		
+	struct ast_ignorepat *ignorepats;	
+	const char *registrar;	
+	AST_LIST_HEAD_NOLOCK(, ast_sw) alts;	
+	ast_mutex_t macrolock;		
+	char name[256];		
+};
+
 struct ast_context *ast_context_find(const char *name)
 {
 	struct ast_context *tmp = NULL;
+	struct fake_context item;
+	strncpy(item.name,name,256);
 	ast_rdlock_contexts();
+	if( contexts_tree )
+		tmp = ast_hashtab_lookup(contexts_tree,&item);
+#ifdef NOTNOW
 	while ( (tmp = ast_walk_contexts(tmp)) ) {
 		if (!name || !strcasecmp(name, tmp->name))
 			break;
 	}
+#endif
 	ast_unlock_contexts();
 	return tmp;
 }
@@ -977,6 +1396,12 @@
 	struct ast_exten *e, *eroot;
 	struct ast_include *i;
 	struct ast_sw *sw;
+	struct ast_exten pattern;
+	struct scoreboard score;
+
+	pattern.label = label;
+	pattern.priority = priority;
+
 
 	/* Initialize status if appropriate */
 	if (q->stacklen == 0) {
@@ -998,18 +1423,65 @@
 	if (bypass)	/* bypass means we only look there */
 		tmp = bypass;
 	else {	/* look in contexts */
+		struct fake_context item;
+		strncpy(item.name,context,256);
+		tmp = ast_hashtab_lookup(contexts_tree,&item);
+#ifdef NOTNOW
 		tmp = NULL;
 		while ((tmp = ast_walk_contexts(tmp)) ) {
 			if (!strcmp(tmp->name, context))
 				break;
 		}
+#endif
 		if (!tmp)
 			return NULL;
 	}
 
 	if (q->status < STATUS_NO_EXTENSION)
 		q->status = STATUS_NO_EXTENSION;
-
+	
+	/* Do a search for matching extension */
+	eroot = NULL;
+	score.total_specificity = 0;
+	score.exten = 0;
+	score.total_length = 0;
+	if (!tmp->pattern_tree)
+		create_match_char_tree(tmp);
+	new_find_extension(exten, &score, tmp->pattern_tree, 0, 0, callerid);
+	eroot = score.exten;
+
+	if (score.last_char == '!' && action == E_MATCHMORE) {
+		/* We match an extension ending in '!'.
+		 * The decision in this case is final and is NULL (no match).
+		 */
+		return NULL;
+	}
+
+	if (!eroot && action == E_CANMATCH && score.canmatch_exten) {
+		q->status = STATUS_SUCCESS;
+		return score.canmatch_exten;
+	}
+
+	if (eroot) {
+		/* found entry, now look for the right priority */
+		if (q->status < STATUS_NO_PRIORITY)
+			q->status = STATUS_NO_PRIORITY;
+		e = NULL;
+		if (action == E_FINDLABEL && label ) {
+			if (q->status < STATUS_NO_LABEL)
+				q->status = STATUS_NO_LABEL;
+			e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern);
+		} else {
+			e = ast_hashtab_lookup(eroot->peer_tree, &pattern);
+		}
+		if (e) {	/* found a valid match */
+			q->status = STATUS_SUCCESS;
+			q->foundcontext = context;
+			return e;
+		}
+	}
+
+#ifdef NOTNOW2
 	/* scan the list trying to match extension and CID */
 	eroot = NULL;
 	while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
@@ -1028,6 +1500,14 @@
 		if (q->status < STATUS_NO_PRIORITY)
 			q->status = STATUS_NO_PRIORITY;
 		e = NULL;
+		if (action == E_FINDLABEL && label ) {
+			if (q->status < STATUS_NO_LABEL)
+				q->status = STATUS_NO_LABEL;
+			e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern);
+		} else {
+			e = ast_hashtab_lookup(eroot->peer_tree, &pattern);
+		}
+#ifdef NOTNOW
 		while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
 			/* Match label or priority */
 			if (action == E_FINDLABEL) {
@@ -1039,12 +1519,14 @@
 				break;	/* found it */
 			} /* else keep searching */
 		}
+#endif
 		if (e) {	/* found a valid match */
 			q->status = STATUS_SUCCESS;
 			q->foundcontext = context;
 			return e;
 		}
 	}
+#endif
 	/* Check alternative switches */
 	AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
 		struct ast_switch *asw = pbx_findswitch(sw->name);
@@ -2733,6 +3215,10 @@
 	if (e->priority == PRIORITY_HINT)
 		ast_remove_hint(e);
 
+	if (e->peer_tree)
+		ast_hashtab_destroy(e->peer_tree,0);
+	if (e->peer_label_tree)
+		ast_hashtab_destroy(e->peer_label_tree, 0);
 	if (e->datad)
 		e->datad(e->data);
 	ast_free(e);
@@ -2812,15 +3298,20 @@
 static struct ast_context *find_context_locked(const char *context)
 {
 	struct ast_context *c = NULL;
-
+	struct fake_context item;
+	strncpy(item.name, context, 256);
 	ast_rdlock_contexts();
+	c = ast_hashtab_lookup(contexts_tree,&item);
+#ifdef NOTNOW
+
 	while ( (c = ast_walk_contexts(c)) ) {
 		if (!strcmp(ast_get_context_name(c), context))
 			return c;
 	}
+#endif
 	ast_unlock_contexts();
 
-	return NULL;
+	return c;
 }
 
 /*!
@@ -3038,8 +3529,17 @@
 {
 	struct ast_context *c = NULL;
 	int ret = -1;
+	struct fake_context item;
 
 	ast_rdlock_contexts();
+
+	strncpy(item.name,context,256);
+	c = ast_hashtab_lookup(contexts_tree,&item);
+	if (c)
+		ret = 0;
+
+
+#ifdef NOTNOW
 
 	while ((c = ast_walk_contexts(c))) {
 		if (!strcmp(ast_get_context_name(c), context)) {
@@ -3048,6 +3548,7 @@
 		}
 	}
 
+#endif
 	ast_unlock_contexts();
 
 	/* if we found context, lock macrolock */
@@ -3066,8 +3567,15 @@
 {
 	struct ast_context *c = NULL;
 	int ret = -1;
+	struct fake_context item;
 
 	ast_rdlock_contexts();
+
+	strncpy(item.name, context, 256);
+	c = ast_hashtab_lookup(contexts_tree,&item);
+	if (c)
+		ret = 0;
+#ifdef NOTNOW
 
 	while ((c = ast_walk_contexts(c))) {
 		if (!strcmp(ast_get_context_name(c), context)) {
@@ -3076,6 +3584,7 @@
 		}
 	}
 
+#endif
 	ast_unlock_contexts();
 
 	/* if we found context, unlock macrolock */
@@ -4020,7 +4529,23 @@
 static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay)
 {
 	struct ast_context *tmp, **local_contexts;
+	struct fake_context search;
 	int length = sizeof(struct ast_context) + strlen(name) + 1;
+
+	if (!contexts_tree)
+		contexts_tree = ast_hashtab_create(17,
+										   hashtab_compare_contexts, 
+										   ast_hashtab_resize_java,
+										   ast_hashtab_newsize_java,
+										   hashtab_hash_contexts,
+										   1);
+	strncpy(search.name,name,sizeof(search.name));
+	tmp = ast_hashtab_lookup(contexts_tree, &search);
+	
+	if (!existsokay && tmp) {
+		ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
+		return tmp;
+	}
 
 	if (!extcontexts) {
 		ast_wrlock_contexts();
@@ -4028,6 +4553,7 @@
 	} else
 		local_contexts = extcontexts;
 
+#ifdef NO_MORE
 	for (tmp = *local_contexts; tmp; tmp = tmp->next) {
 		if (!strcasecmp(tmp->name, name)) {
 			if (!existsokay) {
@@ -4039,16 +4565,19 @@
 			return tmp;
 		}
 	}
+#endif
 	if ((tmp = ast_calloc(1, length))) {
 		ast_rwlock_init(&tmp->lock);
 		ast_mutex_init(&tmp->macrolock);
 		strcpy(tmp->name, name);
 		tmp->root = NULL;
+		tmp->root_tree = NULL;
 		tmp->registrar = registrar;
 		tmp->next = *local_contexts;
 		tmp->includes = NULL;
 		tmp->ignorepats = NULL;
 		*local_contexts = tmp;
+		ast_hashtab_insert_safe(contexts_tree, tmp); /*put this context into the tree */
 		ast_debug(1, "Registered context '%s'\n", tmp->name);
 		ast_verb(3, "Registered extension context '%s'\n", tmp->name);
 	}
@@ -4829,12 +5358,16 @@
 	struct ast_exten *el, struct ast_exten *e, int replace)
 {
 	struct ast_exten *ep;
+	struct ast_exten *eh=e;
 
 	for (ep = NULL; e ; ep = e, e = e->peer) {
 		if (e->priority >= tmp->priority)
 			break;
 	}
 	if (!e) {	/* go at the end, and ep is surely set because the list is not empty */
+		ast_hashtab_insert_safe(eh->peer_tree, tmp);
+		if (tmp->label)
+			ast_hashtab_insert_safe(eh->peer_label_tree, tmp);
 		ep->peer = tmp;
 		return 0;	/* success */
 	}
@@ -4853,12 +5386,41 @@
 		 */
 		tmp->next = e->next;	/* not meaningful if we are not first in the peer list */
 		tmp->peer = e->peer;	/* always meaningful */
-		if (ep)			/* We're in the peer list, just insert ourselves */
+		if (ep)	{		/* We're in the peer list, just insert ourselves */
+			ast_hashtab_remove_object_via_lookup(eh->peer_tree,e);
+			if (e->label)
+				ast_hashtab_remove_object_via_lookup(eh->peer_label_tree,e);
+			ast_hashtab_insert_safe(eh->peer_tree,tmp);
+			if (tmp->label)
+				ast_hashtab_insert_safe(eh->peer_label_tree,tmp);
 			ep->peer = tmp;
-		else if (el)		/* We're the first extension. Take over e's functions */
+		} else if (el) {		/* We're the first extension. Take over e's functions */
+			tmp->peer_tree = e->peer_tree;
+			tmp->peer_label_tree = e->peer_label_tree;
+			ast_hashtab_remove_object_via_lookup(tmp->peer_tree,e);
+			ast_hashtab_insert_safe(tmp->peer_tree,tmp);
+			if (e->label)
+				ast_hashtab_remove_object_via_lookup(tmp->peer_label_tree,e);
+			if (tmp->label)
+				ast_hashtab_insert_safe(tmp->peer_label_tree,tmp);
+			ast_hashtab_remove_object_via_lookup(con->root_tree, e->exten);
+			ast_hashtab_insert_safe(con->root_tree, tmp->exten);
 			el->next = tmp;
-		else			/* We're the very first extension.  */
-			con->root = tmp;
+		} else {			/* We're the very first extension.  */
+			ast_hashtab_remove_object_via_lookup(con->root_tree,e);
+			ast_hashtab_insert_safe(con->root_tree,tmp);
+			tmp->peer_tree = e->peer_tree;
+			tmp->peer_label_tree = e->peer_label_tree;
+			ast_hashtab_remove_object_via_lookup(tmp->peer_tree,e);
+			ast_hashtab_insert_safe(tmp->peer_tree,tmp);
+			if (e->label)
+				ast_hashtab_remove_object_via_lookup(tmp->peer_label_tree,e);
+			if (tmp->label)
+			ast_hashtab_insert_safe(tmp->peer_label_tree,tmp);
+			ast_hashtab_remove_object_via_lookup(con->root_tree, e->exten);
+			ast_hashtab_insert_safe(con->root_tree, tmp->exten);
+ 			con->root = tmp;
+		}
 		if (tmp->priority == PRIORITY_HINT)
 			ast_change_hint(e,tmp);
 		/* Destroy the old one */
@@ -4868,9 +5430,22 @@
 	} else {	/* Slip ourselves in just before e */
 		tmp->peer = e;
 		tmp->next = e->next;	/* extension chain, or NULL if e is not the first extension */
-		if (ep)			/* Easy enough, we're just in the peer list */
+		if (ep) {			/* Easy enough, we're just in the peer list */
+			if (tmp->label)
+				ast_hashtab_insert_safe(eh->peer_label_tree, tmp);
+			ast_hashtab_insert_safe(eh->peer_tree, tmp);
 			ep->peer = tmp;
-		else {			/* we are the first in some peer list, so link in the ext list */
+		} else {			/* we are the first in some peer list, so link in the ext list */
+			tmp->peer_tree = e->peer_tree;
+			tmp->peer_label_tree = e ->peer_label_tree;
+			e->peer_tree = 0;
+			e->peer_label_tree = 0;
+			ast_hashtab_insert_safe(tmp->peer_tree,tmp);
+			if (tmp->label) {
+				ast_hashtab_insert_safe(tmp->peer_label_tree,tmp);
+			}
+			ast_hashtab_remove_object_via_lookup(con->root_tree,e);
+			ast_hashtab_insert_safe(con->root_tree,tmp);
 			if (el)
 				el->next = tmp;	/* in the middle... */
 			else
@@ -5005,10 +5580,49 @@
 		 * so insert in the main list right before 'e' (if any)
 		 */
 		tmp->next = e;
-		if (el)
+		if (el) {
 			el->next = tmp;
-		else
+			tmp->peer_tree = ast_hashtab_create(13,
+							hashtab_compare_exten_numbers,
+							ast_hashtab_resize_java,
+							ast_hashtab_newsize_java,
+							hashtab_hash_priority,
+							1);
+			tmp->peer_label_tree = ast_hashtab_create(7,
+								hashtab_compare_exten_labels,
+								ast_hashtab_resize_java,
+								ast_hashtab_newsize_java,
+								hashtab_hash_labels,
+								1);
+			if (label)
+				ast_hashtab_insert_safe(tmp->peer_label_tree,tmp);
+			ast_hashtab_insert_safe(tmp->peer_tree, tmp);
+
+		} else {
+			con->root_tree = ast_hashtab_create(27,
+						hashtab_compare_extens,
+						ast_hashtab_resize_java,
+						ast_hashtab_newsize_java,
+						hashtab_hash_extens,
+						1);
 			con->root = tmp;
+			con->root->peer_tree = ast_hashtab_create(13,
+								hashtab_compare_exten_numbers,
+								ast_hashtab_resize_java,
+								ast_hashtab_newsize_java,
+								hashtab_hash_priority,
+								1);
+			con->root->peer_label_tree = ast_hashtab_create(7,
+									hashtab_compare_exten_labels,
+									ast_hashtab_resize_java,
+									ast_hashtab_newsize_java,
+									hashtab_hash_labels,
+									1);
+			if (label)
+				ast_hashtab_insert_safe(con->root->peer_label_tree,tmp);
+			ast_hashtab_insert_safe(con->root->peer_tree, tmp);
+		}
+		ast_hashtab_insert_safe(con->root_tree, tmp);
 		ast_unlock_context(con);
 		if (tmp->priority == PRIORITY_HINT)
 			ast_add_hint(tmp);
@@ -5443,6 +6057,8 @@
 			break;
 		ast_wrlock_context(tmp);
 		ast_debug(1, "delete ctx %s %s\n", tmp->name, tmp->registrar);
+		ast_hashtab_remove_this_object(contexts_tree,tmp);
+		
 		next = tmp->next;
 		if (tmpl)
 			tmpl->next = next;




More information about the asterisk-commits mailing list