- more generic hash functions
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Thu, 2 Feb 2006 19:29:21 +0000 (19:29 +0000)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Thu, 2 Feb 2006 19:29:21 +0000 (19:29 +0000)
 - named flags support: the flags can now have names
 Example:
 flags a, b:6, test_flag; # b is set to the 6 flag, the rest are
                          # allocated automatically
 route{
setflag(test_flag);
 ...
 }

cfg.lex
cfg.y
clist.h
flags.c
flags.h
hashes.h [new file with mode: 0644]
main.c

diff --git a/cfg.lex b/cfg.lex
index 4141e72..9afd9e1 100644 (file)
--- a/cfg.lex
+++ b/cfg.lex
@@ -130,6 +130,7 @@ FORCE_TCP_ALIAS             "force_tcp_alias"|"add_tcp_alias"
 SETFLAG                setflag
 RESETFLAG      resetflag
 ISFLAGSET      isflagset
+FLAGS_DECL     "flags"|"bool"
 SET_HOST               "rewritehost"|"sethost"|"seth"
 SET_HOSTPORT   "rewritehostport"|"sethostport"|"sethp"
 SET_USER               "rewriteuser"|"setuser"|"setu"
@@ -331,6 +332,7 @@ EAT_ABLE    [\ \t\b\r]
 <INITIAL>{SETFLAG}     { count(); yylval.strval=yytext; return SETFLAG; }
 <INITIAL>{RESETFLAG}   { count(); yylval.strval=yytext; return RESETFLAG; }
 <INITIAL>{ISFLAGSET}   { count(); yylval.strval=yytext; return ISFLAGSET; }
+<INITIAL>{FLAGS_DECL}  { count(); yylval.strval=yytext; return FLAGS_DECL; }
 <INITIAL>{MSGLEN}      { count(); yylval.strval=yytext; return MSGLEN; }
 <INITIAL>{RETCODE}     { count(); yylval.strval=yytext; return RETCODE; }
 <INITIAL>{ROUTE}       { count(); yylval.strval=yytext; return ROUTE; }
diff --git a/cfg.y b/cfg.y
index 8f1c521..fcc536c 100644 (file)
--- a/cfg.y
+++ b/cfg.y
@@ -69,6 +69,7 @@
  * 2005-12-19  select framework (mma)
  * 2006-01-06  AVP index support (mma)
  * 2005-01-07  optional semicolon in statement, PARAM_STR&PARAM_STRING
+ * 2006-02-02  named flags support (andrei)
  */
 
 %{
@@ -94,6 +95,7 @@
 #include "ut.h"
 #include "dset.h"
 #include "select.h"
+#include "flags.h"
 
 #include "config.h"
 #ifdef USE_TLS
@@ -275,6 +277,8 @@ static struct socket_id* mk_listen_id(char*, int, int);
 %token MCAST_TTL
 %token TOS
 
+%token FLAGS_DECL
+
 %token ATTR_MARK
 %token SELECT_MARK
 %token ATTR_FROMUSER
@@ -344,6 +348,8 @@ static struct socket_id* mk_listen_id(char*, int, int);
 //%type <intval> class_id
 %type <intval> assign_op
 %type <select> select_id
+%type <strval> flag_name;
+
 /*%type <route_el> rules;
   %type <route_el> rule;
 */
@@ -361,6 +367,7 @@ statements:
        ;
 statement:
        assign_stm
+       | flags_decl
        | module_stm
        | {rt=REQUEST_ROUTE;} route_stm
        | {rt=FAILURE_ROUTE;} failure_route_stm
@@ -427,6 +434,27 @@ id_lst:
        phostport               {  $$=$1 ; }
        | phostport id_lst      { $$=$1; $$->next=$2; }
        ;
+
+flags_decl:            FLAGS_DECL      flag_list
+                       |       FLAGS_DECL error { yyerror("flag list expected\n") };
+;
+flag_list:             flag_spec
+                       |       flag_spec COMMA flag_list
+;
+
+flag_spec:             flag_name       { if (register_flag($1,-1)<0)
+                                                               yyerror("register flag failed");
+                                               }
+                       |       flag_name COLON NUMBER {
+                                               if (register_flag($1, $3)<0)
+                                                               yyerror("register flag failed");
+                                                                               }
+;
+
+flag_name:             STRING  { $$=$1; }
+                       |       ID              { $$=$1; }
+;
+
 assign_stm:
        DEBUG_V EQUAL NUMBER { debug=$3; }
        | DEBUG_V EQUAL error  { yyerror("number  expected"); }
@@ -1476,11 +1504,42 @@ cmd:
        | LOG_TOK LPAREN NUMBER COMMA STRING RPAREN     {$$=mk_action(LOG_T, 2, NUMBER_ST, (void*)$3, STRING_ST, $5); }
        | LOG_TOK error                 { $$=0; yyerror("missing '(' or ')' ?"); }
        | LOG_TOK LPAREN error RPAREN   { $$=0; yyerror("bad log argument"); }
-       | SETFLAG LPAREN NUMBER RPAREN  {$$=mk_action(SETFLAG_T, 1, NUMBER_ST, (void*)$3); }
+       | SETFLAG LPAREN NUMBER RPAREN  {
+                                                       if (check_flag($3)==-1)
+                                                               yyerror("bad flag value");
+                                                       $$=mk_action(SETFLAG_T, 1, NUMBER_ST,
+                                                                                                       (void*)$3);
+                                                                       }
+       | SETFLAG LPAREN flag_name RPAREN       {
+                                                       i_tmp=get_flag_no($3, strlen($3));
+                                                       if (i_tmp<0) yyerror("flag not declared");
+                                                       $$=mk_action(SETFLAG_T, 1, NUMBER_ST,
+                                                                               (void*)i_tmp); 
+                                                                       }
        | SETFLAG error                 { $$=0; yyerror("missing '(' or ')'?"); }
-       | RESETFLAG LPAREN NUMBER RPAREN {$$=mk_action(RESETFLAG_T, 1, NUMBER_ST, (void*)$3); }
+       | RESETFLAG LPAREN NUMBER RPAREN {
+                                                       if (check_flag($3)==-1)
+                                                               yyerror("bad flag value");
+                                                       $$=mk_action(RESETFLAG_T, 1, NUMBER_ST, (void*)$3);
+                                                                       }
+       | RESETFLAG LPAREN flag_name RPAREN     {
+                                                       i_tmp=get_flag_no($3, strlen($3));
+                                                       if (i_tmp<0) yyerror("flag not declared");
+                                                       $$=mk_action(RESETFLAG_T, 1, NUMBER_ST,
+                                                                               (void*)i_tmp); 
+                                                                       }
        | RESETFLAG error               { $$=0; yyerror("missing '(' or ')'?"); }
-       | ISFLAGSET LPAREN NUMBER RPAREN {$$=mk_action(ISFLAGSET_T, 1, NUMBER_ST, (void*)$3); }
+       | ISFLAGSET LPAREN NUMBER RPAREN {
+                                                       if (check_flag($3)==-1)
+                                                               yyerror("bad flag value");
+                                                       $$=mk_action(ISFLAGSET_T, 1, NUMBER_ST, (void*)$3);
+                                                                       }
+       | ISFLAGSET LPAREN flag_name RPAREN     {
+                                                       i_tmp=get_flag_no($3, strlen($3));
+                                                       if (i_tmp<0) yyerror("flag not declared");
+                                                       $$=mk_action(ISFLAGSET_T, 1, NUMBER_ST,
+                                                                               (void*)i_tmp); 
+                                                                       }
        | ISFLAGSET error { $$=0; yyerror("missing '(' or ')'?"); }
        | ERROR LPAREN STRING COMMA STRING RPAREN {$$=mk_action(ERROR_T, 2, STRING_ST, $3, STRING_ST, $5); }
        | ERROR error { $$=0; yyerror("missing '(' or ')' ?"); }
diff --git a/clist.h b/clist.h
index c1863cb..22c27ca 100644 (file)
--- a/clist.h
+++ b/clist.h
@@ -49,7 +49,7 @@
  */
 #define clist_insert_sublist(head, s, e, next, prev) \
        do{ \
-               (s)->prev=(head); \
+               (s)->prev=(void*)(head); \
                (e)->next=(head)->next; \
                (e)->next->prev=(e); \
                (head)->next=s;   \
diff --git a/flags.c b/flags.c
index c2d87f5..0af65cb 100644 (file)
--- a/flags.c
+++ b/flags.c
@@ -28,6 +28,7 @@
  * History:
  * --------
  *  2003-03-19  replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei)
+ *  2006-02-02  named flags support (andrei)
  */
 
 
@@ -38,6 +39,7 @@
 #include "flags.h"
 #include "error.h"
 #include "stdlib.h"
+#include "hashes.h"
 
 int setflag( struct sip_msg* msg, flag_t flag ) {
        msg->flags |= 1 << flag;
@@ -59,15 +61,166 @@ int flag_in_range( flag_t flag ) {
                        flag, MAX_FLAG );
                return 0;
        }
-       if (flag<=0) {
+       if (flag<0) {
                LOG(L_ERR, "ERROR: message flag (%d) must be in range %d..%d\n",
-                       flag, 1, MAX_FLAG );
+                       flag, 0, MAX_FLAG );
                return 0;
        }
        return 1;
 }
 
 
+/* use 2^k */
+#define FLAGS_NAME_HASH_ENTRIES                32
+
+struct flag_entry{
+       str name;
+       int no;
+       struct flag_entry* next;
+       struct flag_entry* prev;
+};
+
+
+struct flag_hash_head{
+       struct flag_entry* next;
+       struct flag_entry* prev;
+};
+
+static struct flag_hash_head  name2flags[FLAGS_NAME_HASH_ENTRIES];
+static unsigned char registered_flags[MAX_FLAG];
+
+
+void init_named_flags()
+{
+       int r;
+       
+       for (r=0; r<FLAGS_NAME_HASH_ENTRIES; r++)
+               clist_init(&name2flags[r], next, prev);
+}
+
+
+
+/* returns 0 on success, -1 on error */
+int check_flag(int n)
+{
+       if (!flag_in_range(n))
+               return -1;
+       if (registered_flags[n]){
+               LOG(L_WARN, "WARNING: check_flag: flag %d is already used by "
+                                       " a named flag\n", n);
+       }
+       return 0;
+}
+
+
+inline static struct flag_entry* flag_search(struct flag_hash_head* lst,
+                                                                                               char* name, int len)
+{
+       struct flag_entry* fe;
+       
+       clist_foreach(lst, fe, next){
+               if ((fe->name.len==len) && (memcmp(fe->name.s, name, len)==0)){
+                       /* found */
+                       return fe;
+               }
+       }
+       return 0;
+}
+
+
+
+/* returns flag entry or 0 on not found */
+inline static struct flag_entry* get_flag_entry(char* name, int len)
+{
+       int h;
+       /* get hash */
+       h=get_hash1_raw(name, len) & (FLAGS_NAME_HASH_ENTRIES-1);
+       return flag_search(&name2flags[h], name, len);
+}
+
+
+
+/* returns flag number, or -1 on error */
+int get_flag_no(char* name, int len)
+{
+       struct flag_entry* fe;
+       
+       fe=get_flag_entry(name, len);
+       return (fe)?fe->no:-1;
+}
+
+
+
+/* resgiter a new flag name and associates it with pos
+ * pos== -1 => any position will do 
+ * returns flag pos on success (>=0)
+ *         -1  flag is an alias for an already existing flag
+ *         -2  flag already registered
+ *         -3  mem. alloc. failure
+ *         -4  invalid pos
+ *         -5 no free flags */
+int register_flag(char* name, int pos)
+{
+       struct flag_entry* e;
+       int len;
+       unsigned int r;
+       static unsigned int crt_flag=0;
+       unsigned int last_flag;
+       unsigned int h;
+       
+       len=strlen(name);
+       h=get_hash1_raw(name, len) & (FLAGS_NAME_HASH_ENTRIES-1);
+       /* check if the name already exists */
+       e=flag_search(&name2flags[h], name, len);
+       if (e){
+               LOG(L_WARN, "WARNING: register_flag: flag %.*s already registered\n",
+                                       len, name);
+               return -2;
+       }
+       /* check if there is already another flag registered at pos */
+       if (pos!=-1){
+               if ((pos<0) || (pos>MAX_FLAG)){
+                       LOG(L_ERR, "ERROR: register_flag: invalid flag %.*s "
+                                       "position(%d)\n", len, name, pos);
+                       return -4;
+               }
+               if (registered_flags[pos]!=0){
+                       LOG(L_WARN, "WARNING: register_flag:  %.*s:  flag %d already in "
+                                       "use under another name\n", len, name, pos);
+                       /* continue */
+               }
+       }else{
+               /* alloc an empty flag */
+               last_flag=crt_flag+MAX_FLAG;
+               for (; crt_flag!=last_flag; crt_flag++){
+                       r=crt_flag%MAX_FLAG;
+                       if (registered_flags[r]==0){
+                               pos=r;
+                               break;
+                       }
+               }
+               if (pos==-1){
+                       LOG(L_ERR, "ERROR: register_flag: could not register %.*s"
+                                       " - too many flags\n", len, name);
+                       return -5;
+               }
+       }
+       registered_flags[pos]++;
+       
+       e=pkg_malloc(sizeof(struct flag_entry));
+       if (e==0){
+               LOG(L_ERR, "ERROR: register_flag: memory allocation failure\n");
+               return -3;
+       }
+       e->name.s=name;
+       e->name.len=len;
+       e->no=pos;
+       clist_insert(&name2flags[h], e, next, prev);
+       return pos;
+}
+
+
+
 #ifdef _GET_AWAY
 
 /* wrapping functions for flag processing  */
diff --git a/flags.h b/flags.h
index 2e09853..62b3235 100644 (file)
--- a/flags.h
+++ b/flags.h
@@ -45,4 +45,9 @@ int isflagset( struct sip_msg* msg, flag_t flag );
 
 int flag_in_range( flag_t flag );
 
+int register_flag(char* name, int pos);
+int get_flag_no(char* name, int len);
+int check_flag(int pos);
+void init_named_flags();
+
 #endif
diff --git a/hashes.h b/hashes.h
new file mode 100644 (file)
index 0000000..a0b74bf
--- /dev/null
+++ b/hashes.h
@@ -0,0 +1,181 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 iptelorg GmbH 
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    info@iptel.org
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/*
+ * History:
+ * --------
+ *  2006-02-02  created by andrei
+ */
+
+
+#ifndef _hashes_h
+#define _hashes_h
+
+#include "str.h"
+#include "mem/mem.h"
+#include "clist.h"
+
+
+
+/* internal use: hash update
+ * params: char* s   - string start,
+ *         char* end - end
+ *         char* p,  and unsigned v temporary vars (used)
+ *         unsigned h - result
+ * h should be initialized (e.g. set it to 0), the result in h */
+#define hash_update_str(s, end, p, v, h) \
+       do{ \
+               for ((p)=(s); (p)<=((end)-4); (p)+=4){ \
+                       (v)=(*(p)<<24)+((p)[1]<<16)+((p)[2]<<8)+(p)[3]; \
+                       (h)+=(v)^((v)>>3); \
+               } \
+               (v)=0; \
+               for (;(p)<(end); (p)++){ (v)<<=8; (v)+=*(p);} \
+               (h)+=(v)^((v)>>3); \
+       }while(0)
+
+
+/* internal use: call it to adjust the h from hash_update_str */
+#define hash_finish(h) (((h)+((h)>>11))+(((h)>>13)+((h)>>23)))
+
+
+
+/* "raw" 2 strings hash
+ * returns an unsigned int (which you can use modulo table_size as hash value)
+ */
+inline static unsigned int get_hash2_raw(str* key1, str* key2)
+{
+       char* p;
+       register unsigned v;
+       register unsigned h;
+       
+       h=0;
+       
+       hash_update_str(key1->s, key1->s+key1->len, p, v, h);
+       hash_update_str(key2->s, key2->s+key2->len, p, v, h);
+       return hash_finish(h);
+}
+
+
+
+/* "raw" 1 string hash
+ * returns an unsigned int (which you can use modulo table_size as hash value)
+ */
+inline static unsigned int get_hash1_raw(char* s, int len)
+{
+       char* p;
+       register unsigned v;
+       register unsigned h;
+       
+       h=0;
+       
+       hash_update_str(s, s+len, p, v, h);
+       return hash_finish(h);
+}
+
+
+
+/* generic, simple str keyed hash */
+
+struct str_hash_entry{
+       struct str_hash_entry* next;
+       struct str_hash_entry* prev;
+       str key;
+       unsigned int flags;
+       union{
+               void* p;
+               char* s;
+               int   n;
+               char  data[sizeof(void*)];
+       }u;
+};
+
+
+struct str_hash_head{
+       struct str_hash_entry* next;
+       struct str_hash_entry* prev;
+};
+
+
+struct str_hash_table{
+       struct str_hash_head* table;
+       int size;
+};
+
+
+
+/* returns 0 on success, <0 on failure */
+inline static int str_hash_alloc(struct str_hash_table* ht, int size)
+{
+       ht->table=pkg_malloc(sizeof(struct str_hash_head)*size);
+       if (ht->table==0)
+               return -1;
+       ht->size=size;
+       return 0;
+}
+
+
+
+inline static void str_hash_init(struct str_hash_table* ht)
+{
+       int r;
+       
+       for (r=0; r<ht->size; r++) clist_init(&(ht->table[r]), next, prev);
+}
+
+
+
+inline static void str_hash_add(struct str_hash_table* ht, 
+                                                               struct str_hash_entry* e)
+{
+       int h;
+       
+       h=get_hash1_raw(e->key.s, e->key.len) % ht->size;
+       clist_insert(&ht->table[h], e, next, prev);
+}
+
+
+
+inline static struct str_hash_entry* str_hash_get(struct str_hash_table* ht,
+                                                                       char* key, int len)
+{
+       int h;
+       struct str_hash_entry* e;
+       
+       h=get_hash1_raw(e->key.s, e->key.len) % ht->size;
+       clist_foreach(&ht->table[h], e, next){
+               if ((e->key.len==len) && (memcmp(e->key.s, key, len)==0))
+                       return e;
+       }
+       return 0;
+}
+
+
+#define str_hash_del(e) clist_rm(e, next, prev)
+
+
+
+#endif
diff --git a/main.c b/main.c
index 38db271..d053c4c 100644 (file)
--- a/main.c
+++ b/main.c
 #endif
 #include "usr_avp.h"
 #include "core_cmd.h"
+#include "flags.h"
 
 #include "stats.h"
 
@@ -1340,6 +1341,9 @@ try_again:
        /*register builtin  modules*/
        register_builtin_modules();
 
+       /* init named flags */
+       init_named_flags();
+
        yyin=cfg_stream;
        debug_save = debug;
        if ((yyparse()!=0)||(cfg_errors)){