[svn-commits] murf: branch murf/fast-ast r44744 - /team/murf/fast-ast/main/pbx.c

svn-commits at lists.digium.com svn-commits at lists.digium.com
Sat Oct 7 20:26:20 MST 2006


Author: murf
Date: Sat Oct  7 22:26:20 2006
New Revision: 44744

URL: http://svn.digium.com/view/asterisk?rev=44744&view=rev
Log:
more notes. Haven't even tried to compile my additions yet.

Modified:
    team/murf/fast-ast/main/pbx.c

Modified: team/murf/fast-ast/main/pbx.c
URL: http://svn.digium.com/view/asterisk/team/murf/fast-ast/main/pbx.c?rev=44744&r1=44743&r2=44744&view=diff
==============================================================================
--- team/murf/fast-ast/main/pbx.c (original)
+++ team/murf/fast-ast/main/pbx.c Sat Oct  7 22:26:20 2006
@@ -599,6 +599,161 @@
 static void pbx_destroy(struct ast_pbx *p)
 {
 	free(p);
+}
+
+struct match_char
+{
+	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_match; /* attached to last char of a pattern for exten */
+};
+
+/* 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.
+ *
+ * */
+
+struct scoreboard  /* make sure all fields are 0 before calling new_find_extension */
+{
+	int total_specificity;
+	int total_length;
+	struct ast_extension *exten;
+};
+
+/* 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_extension *exten)
+{
+	if (length > board->total_length) {
+		board->total_specificity = spec;
+		board->total_length = length;
+		board->exten = exten;
+	} else if (length == board->total_length && spec < board->total_specificity) {
+		board->total_specificity = spec;
+		board->total_length = length;
+		board->exten = exten;
+	}
+}
+
+void new_find_extension(char *str, struct scoreboard *score, struct match_char *tree, int length, int spec)
+{
+	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 && *(str+1))
+				find_extension(str+1, score, p->next_char, length+1, spec+8);
+			else if (p->exten) {
+				update_scoreboard(score, length+1, spec+8, p->exten);
+				return;
+			} else {
+				return;
+			}
+		} else if (p->x[0] == 'Z' && p->x[1] == 0 && *str >= '1' && *str <= '9' ) {
+			if (p->next && *(str+1))
+				find_extension(str+1, score, p->next_char, length+1, spec+9);
+			else if (p->exten) {
+				update_scoreboard(score, length+1, spec+9, p->exten);
+				return;
+			} else {
+				return;
+			}
+		} else if (p->x[0] == 'X' && p->x[1] == 0 && *str >= '0' && *str <= '9' ) {
+			if (p->next && *(str+1))
+				find_extension(str+1, score, p->next_char, length+1, spec+10);
+			else if (p->exten) {
+				update_scoreboard(score, length+1, spec+10, p->exten);
+				return;
+			} else {
+				return;
+			}
+		} else if (p->x[0] == '.' && p->x[1] == 0 ) {
+			update_scoreboard(score, length+1, spec+11, p->exten);
+			return;
+		} else if (p->x[0] == '!' && p->x[1] == 0 ) {
+			update_scoreboard(score, length+1, spec+11, p->exten);
+			return;
+		} else if (index(p->x, *str)) {
+			if (p->next && *(str+1))
+				find_extension(str+1, score, p->next_char, length+1, spec+p->specificity);
+			else if (p->exten) {
+				update_scoreboard(score, length+1, spec+p->specificity, p->exten);
+				return;
+			} else {
+				return;
+			}
+
+		} 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.
+ */
+
+void create_match_char_tree(struct ast_context *con)
+{
 }
 
 /*
@@ -962,6 +1117,10 @@
 	struct ast_exten *e, *eroot;
 	struct ast_include *i;
 	struct ast_sw *sw;
+	struct ast_exten pattern;
+
+	pattern->label = label;
+	pattern->priority = priority;
 
 	/* Initialize status if appropriate */
 	if (q->stacklen == 0) {
@@ -983,11 +1142,16 @@
 	if (bypass)	/* bypass means we only look there */
 		tmp = bypass;
 	else {	/* look in contexts */
+		struct ast_context item;
+		item->name = name;
+		tmp = trb_find(contexts_tree,&item);
+#ifdef NOTNOW
 		tmp = NULL;
 		while ((tmp = ast_walk_contexts(tmp)) ) {
 			if (!strcmp(tmp->name, context))
 				break;
 		}
+#endif
 		if (!tmp)
 			return NULL;
 	}
@@ -1012,6 +1176,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 = trb_find(eroot->peer_label_tree, &pattern);
+		} else {
+			e = trb_find(eroot->peer_tree, &pattern);
+		}
+#ifdef NOTNOW
 		while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
 			/* Match label or priority */
 			if (action == E_FINDLABEL) {
@@ -1023,6 +1195,7 @@
 				break;	/* found it */
 			} /* else keep searching */
 		}
+#endif
 		if (e) {	/* found a valid match */
 			q->status = STATUS_SUCCESS;
 			q->foundcontext = context;
@@ -2584,15 +2757,20 @@
 static struct ast_context *find_context_locked(const char *context)
 {
 	struct ast_context *c = NULL;
-
+	struct ast_context item;
+	item->name = context;
 	ast_lock_contexts();
+	c = trb_find(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;
 }
 
 /*
@@ -2807,15 +2985,22 @@
 {
 	struct ast_context *c = NULL;
 	int ret = -1;
-
+	struct ast_context item;
+	item->name = context;
 	ast_lock_contexts();
-
+	c = trb_find(contexts_tree,&item);
+	if (c)
+		ret = 0;
+
+
+#ifdef NOTNOW
 	while ((c = ast_walk_contexts(c))) {
 		if (!strcmp(ast_get_context_name(c), context)) {
 			ret = 0;
 			break;
 		}
 	}
+#endif
 
 	ast_unlock_contexts();
 
@@ -2835,16 +3020,22 @@
 {
 	struct ast_context *c = NULL;
 	int ret = -1;
+	struct ast_context item;
+	item->name = context;
 
 	ast_lock_contexts();
 
+	c = trb_find(contexts_tree,&item);
+	if (c)
+		ret = 0;
+#ifdef NOTNOW
 	while ((c = ast_walk_contexts(c))) {
 		if (!strcmp(ast_get_context_name(c), context)) {
 			ret = 0;
 			break;
 		}
 	}
-
+#endif
 	ast_unlock_contexts();
 
 	/* if we found context, unlock macrolock */
@@ -4391,12 +4582,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 */
+		trb_insert(eh->peer_tree, tmp);
+		if (tmp->label)
+			trb_insert(eh->peer_label_tree, tmp);
 		ep->peer = tmp;
 		return 0;	/* success */
 	}
@@ -4414,11 +4609,40 @@
 		 */
 		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 */
+			trb_delete(eh->peer_tree,e);
+			if (e->label)
+				trb_delete(eh->peer_label_tree,e);
+			trb_insert(eh->peer_tree,tmp);
+			if (tmp->label)
+				trb_insert(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;
+			trb_delete(tmp->peer_tree,e);
+			trb_insert(tmp->peer_tree,tmp);
+			if (e->label)
+				trb_delete(tmp->peer_label_tree,e);
+			if (tmp->label)
+				trb_insert(tmp->peer_label_tree,tmp);
+			trb_delete(con->root_tree, e->exten);
+			trb_insert(con->root_tree, tmp->exten);
 			el->next = tmp;
-		else			/* We're the very first extension.  */
+		} else {			/* We're the very first extension.  */
+			trb_delete(con->root_tree,e);
+			trb_insert(con->root_tree,tmp);
+			tmp->peer_tree = e->peer_tree;
+			tmp->peer_label_tree = e->peer_label_tree;
+			trb_delete(tmp->peer_tree,e);
+			trb_insert(tmp->peer_tree,tmp);
+			if (e->label)
+				trb_delete(tmp->peer_label_tree,e);
+			if (tmp->label)
+				trb_insert(tmp->peer_label_tree,tmp);
+			trb_delete(con->root_tree, e->exten);
+			trb_insert(con->root_tree, tmp->exten);
 			con->root = tmp;
 		if (tmp->priority == PRIORITY_HINT)
 			ast_change_hint(e,tmp);
@@ -4428,9 +4652,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)
+				trb_insert(eh->peer_label_tree, tmp);
+			trb_insert(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;
+			trb_insert(tmp->peer_tree,tmp);
+			if (tmp->label) {
+				trb_insert(tmp->peer_label_tree,tmp);
+			}
+			trb_delete(con->root_tree,e);
+			trb_insert(con->root_tree,tmp);
 			if (el)
 				el->next = tmp;	/* in the middle... */
 			else
@@ -4567,10 +4804,18 @@
 		 * so insert in the main list right before 'e' (if any)
 		 */
 		tmp->next = e;
-		if (el)
+		if (el) {
 			el->next = tmp;
-		else
+		} else {
+			con->root_tree = trb_create(trb_compare_extens,0,0);
 			con->root = tmp;
+			con->root->peer_tree = trb_create(trb_compare_exten_numbers,0,0);
+			con->root->peer_label_tree = trb_create(trb_compare_exten_labels,0,0);
+			if (label)
+				trb_insert(con->root->peer_label_tree,tmp);
+			trb_insert(con->root_tree, tmp);
+		}
+		trb_insert(con->root_tree, tmp);
 		ast_mutex_unlock(&con->lock);
 		if (tmp->priority == PRIORITY_HINT)
 			ast_add_hint(tmp);



More information about the svn-commits mailing list