[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