Main Page | Modules | Class List | Directories | File List | Class Members | File Members | Related Pages

hi_util_kmap.c

Go to the documentation of this file.
00001 /*
00002 *
00003 *  kmap.c  -  a generic map library - maps key + data pairs
00004 *  
00005 *  Uses Lexical Keyword Trie 
00006 *    The tree uses linked lists to build the finite automata
00007 *
00008 *  MapKeyFind(): Performs a setwise strcmp() equivalant.
00009 *
00010 *  Notes:
00011 *
00012 *  Keys may be ascii or binary, both may be of random sizes.
00013 *  Each key may be a different size, or all one size.
00014 *  Fast dictionary lookup, proportional to the length of the key,
00015 *  and independent of the number of keys in the table.
00016 *  May use more memory than a hash table, depends.
00017 *  Memory is allocated as needed, so none is wasted.
00018 *   
00019 *  Copyright (C) 2002 Marc Norton
00020 *
00021 */
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <ctype.h>
00026 
00027 #include "hi_util_kmap.h"
00028 #include "hi_util_xmalloc.h"
00029 
00030 //#define MEMASSERT(p) if(!p){printf("KMAP-No Memory: File: %s Line:%d!\n",__FILE__,__LINE__);exit(0);}
00031 
00032 #define MEMASSERT(p) 
00033 #define LOWERCASE tolower
00034 
00035 /*
00036 *
00037 */
00038 static void * s_malloc( int n )
00039 {
00040     void * p;
00041     
00042     p = xmalloc( n );
00043     
00044     MEMASSERT(p);
00045     
00046     return p;
00047 }
00048 
00049 /*
00050 *
00051 */
00052 static void s_free( void * p )
00053 {
00054     if( p ) xfree( p );
00055 }
00056 /*
00057 *
00058 */
00059 KMAP * KMapNew( void (*userfree)(void*p) )
00060 {
00061     KMAP * km = (KMAP*) s_malloc( sizeof(KMAP) );
00062     
00063     if( !km ) return 0;
00064     
00065     memset(km, 0, sizeof(KMAP));  
00066     
00067     km->userfree = userfree;
00068     
00069     return km;
00070 }
00071 /*
00072 *
00073 */
00074 void KMapSetNoCase( KMAP * km, int flag )
00075 {
00076     km->nocase = flag;
00077 }
00078 
00079 /*
00080 *   Free the list of key+data pair nodes - used by findfirst/next
00081 */
00082 static int KMapFreeNodeList(KMAP * km )
00083 {
00084     KEYNODE * k, *kold;
00085     
00086     for( k=km->keylist; k; )
00087     {
00088         if( k->key )
00089         {
00090             s_free( k->key );
00091         }
00092         if( km->userfree && k->userdata )
00093         {
00094             km->userfree( k->userdata ); 
00095         }
00096         kold = k;
00097         k = k->next;
00098         s_free(kold);
00099     }
00100     
00101     return 0;
00102 }
00103 /*
00104 *     Recursively walk and free nodes
00105 */
00106 static void KMapFreeNode( KMAP * km, KMAPNODE * r)
00107 {
00108     if( r->sibling )
00109     {
00110         KMapFreeNode( km, r->sibling );
00111     }
00112     
00113     if( r->child )
00114     {
00115         KMapFreeNode( km, r->child );
00116     }
00117     
00118     s_free( r );
00119 } 
00120 /*
00121 *  Free the KMAP and all of it's memory and nodes
00122 */
00123 void KMapDelete( KMAP * km )
00124 {
00125     KMAPNODE * r;
00126     int        i;
00127     
00128     /* Free the tree - on root node at a time */
00129     for(i=0;i<256;i++)
00130     {
00131         r = km->root[i];
00132         if( r )
00133         { 
00134             KMapFreeNode(km,r); 
00135         }
00136     }
00137     
00138     /* Free the node list */
00139     KMapFreeNodeList( km );
00140     
00141     s_free(km);
00142 }
00143 
00144 /*
00145 *  Add key + data pair to the linked list of nodes
00146 */
00147 static KEYNODE *  KMapAddKeyNode(KMAP * km,void * key, int n, void * userdata )
00148 {
00149     KEYNODE * knode = (KEYNODE*) s_malloc( sizeof(KEYNODE) );
00150     
00151     if( !knode || n < 0 ) 
00152         return 0;
00153     
00154     memset(knode, 0, sizeof(KEYNODE) );  
00155     
00156     knode->key = (unsigned char*)s_malloc(n); // Alloc the key space
00157     if( !knode->key )
00158         return 0;
00159     
00160     memcpy(knode->key,key,n); // Copy the key
00161     knode->nkey     = n;
00162     knode->userdata = userdata;
00163     
00164     if( km->keylist ) // Insert at front of list 
00165     {
00166         knode->next = km->keylist;
00167         km->keylist = knode;
00168     }
00169     else
00170     {
00171         km->keylist = knode;
00172     }
00173     
00174     return knode;
00175 }
00176 /*
00177 *  Create a character node
00178 */
00179 static KMAPNODE * KMapCreateNode(KMAP * km)
00180 {
00181     KMAPNODE * mn=(KMAPNODE*)s_malloc( sizeof(KMAPNODE) );
00182     
00183     if(!mn)
00184         return NULL;
00185     
00186     memset(mn,0,sizeof(KMAPNODE));
00187     
00188     km->nchars++;
00189     
00190     return mn;
00191 }
00192 
00193 /*
00194 *    key : ptr to bytes of data used to identify this item
00195 *          may be text string, or binary character sequence.
00196 *    n   : > 0 number of bytes in the key
00197 *          <=0 key is a null terminated  ascii string
00198 *    userdata - ptr to data to associate with this key
00199 *
00200 *   returns:
00201 *            -1 - no memory
00202 *             1 - key already in table
00203 *             0 - key added successfully
00204 */
00205 int KMapAdd( KMAP *km, void * key, int n, void * userdata )
00206 {
00207     int            i,ksize;
00208     int            type;
00209     unsigned char *P = (unsigned char *)key;
00210     KMAPNODE      *root;
00211     unsigned char  xkey[256];
00212     
00213     if( n <= 0 )
00214     {
00215         n = strlen( (char*) key );
00216         if( n > sizeof(xkey) )
00217             return -99;
00218     }
00219     
00220     if( km->nocase )
00221     {
00222         for(i=0;i<n;i++)
00223             xkey[i] = LOWERCASE( P[i] );
00224         P = xkey;
00225     }
00226     
00227     /* Save key size */
00228     ksize = n;
00229     
00230     //printf("adding key='%.*s'\n",n,P);
00231     
00232     /* Make sure we at least have a root character for the tree */
00233     if( !km->root[ *P ] )
00234     {
00235         root = KMapCreateNode(km);
00236         if( !root )
00237             return -1;
00238         km->root[ *P ] = root;
00239         root->nodechar = *P;
00240         
00241     }else{
00242         
00243         root = km->root[ *P ];
00244     }
00245     
00246     /* Walk exisitng Patterns */   
00247     while( n )
00248     {
00249         if( root->nodechar == *P )
00250         {
00251             //printf("matched char = %c, nleft = %d\n",*P,n-1);
00252             P++;
00253             n--;
00254             if( n && root->child )
00255             {
00256                 root=root->child;   
00257             }
00258             else /* cannot continue */
00259             {
00260                 type = 0; /* Expand the tree via the child */
00261                 break; 
00262             }
00263         }
00264         else
00265         {
00266             if( root->sibling )
00267             {
00268                 root=root->sibling;
00269             }
00270             else /* cannot continue */
00271             {
00272                 type = 1; /* Expand the tree via the sibling */
00273                 break; 
00274             }
00275         }
00276     }
00277     
00278     
00279     /* 
00280     * Add the next char of the Keyword, if any
00281     */
00282     if( n )
00283     {
00284         if( type == 0 )
00285         {
00286         /*
00287         *  Start with a new child to finish this Keyword 
00288             */
00289             //printf("added child branch nodechar = %c \n",*P);
00290             root->child= KMapCreateNode( km );
00291             if( !root->child )
00292                 return -1;
00293             root=root->child;
00294             root->nodechar  = *P;
00295             P++;
00296             n--;
00297         }
00298         else
00299         { 
00300         /*
00301         *  Start a new sibling bracnch to finish this Keyword 
00302             */
00303             //printf("added sibling branch nodechar = %c \n",*P);
00304             root->sibling= KMapCreateNode( km );
00305             if( !root->sibling )
00306                 return -1;
00307             root=root->sibling;
00308             root->nodechar  = *P;
00309             P++;
00310             n--;
00311         }
00312     }
00313     
00314     /*
00315     *    Finish the keyword as child nodes
00316     */
00317     while( n )
00318     {
00319         //printf("added child nodechar = %c \n",*P);
00320         root->child = KMapCreateNode(km);
00321         if( !root->child )
00322             return -1;
00323         root=root->child;
00324         root->nodechar  = *P;
00325         P++;
00326         n--;
00327     }
00328     
00329     /* 
00330     * Iteration support - Add this key/data to the linked list 
00331     * This allows us to do a findfirst/findnext search of 
00332     * all map nodes.
00333     */
00334     if( root->knode ) /* Already present */
00335         return 1;
00336     
00337     root->knode = KMapAddKeyNode( km, key, ksize, userdata );
00338     if( !root->knode )
00339         return -1;
00340     
00341     return 0;
00342 }
00343 
00344 /*
00345 *  Exact Keyword Match - unique keys, with just one piece of 
00346 *  'userdata' , for multiple entries, we could use a list
00347 *  of 'userdata' nodes.
00348 */
00349 void *  KMapFind( KMAP * ks, void * key, int n )
00350 {
00351     unsigned char * T = (unsigned char *)key;
00352     KMAPNODE      * root;
00353     unsigned char   xkey[256];
00354     int             i;
00355     
00356     if( n <= 0 )
00357     {
00358         n = strlen( (char*)key );
00359         if( n > sizeof(xkey) )
00360             return 0;
00361         
00362     }
00363     if( ks->nocase )
00364     {
00365         for(i=0;i<n;i++)
00366             xkey[i] = LOWERCASE( T[i] );
00367         
00368         T = xkey;
00369     }
00370     //printf("finding key='%.*s'\n",n,T);
00371     
00372     /* Check if any keywords start with this character */
00373     root = ks->root[ *T ];
00374     if( !root ) return NULL;
00375     
00376     while( n )
00377     {
00378         if( root->nodechar == *T )
00379         {
00380             T++;
00381             n--;
00382             if( n && root->child )
00383             {
00384                 root = root->child;   
00385             }
00386             else /* cannot continue -- match is over */
00387             {
00388                 break; 
00389             }
00390         }
00391         else
00392         {
00393             if( root->sibling )
00394             {
00395                 root = root->sibling;
00396             }
00397             else /* cannot continue */
00398             {
00399                 break; 
00400             }
00401         }
00402     }
00403     
00404     if( !n )
00405     {
00406         if (root && root->knode)
00407             return root->knode->userdata; /* success */
00408     }
00409     
00410     return NULL;
00411 }
00412 /*
00413 *
00414 */
00415 KEYNODE * KMapFindFirstKey( KMAP * km )
00416 {
00417     km->keynext = km->keylist;
00418     
00419     if(!km->keynext)
00420     {
00421         return NULL;
00422     }
00423     
00424     return km->keynext;
00425 }
00426 /*
00427 *
00428 */
00429 void * KMapFindFirst( KMAP * km )
00430 {
00431     km->keynext = km->keylist;
00432     
00433     if(!km->keynext)
00434     {
00435         return NULL;
00436     }
00437     
00438     return km->keynext->userdata;
00439 }
00440 /*
00441 *
00442 */
00443 KEYNODE * KMapFindNextKey( KMAP * km )
00444 {
00445     if( !km->keynext )
00446         return 0;
00447     
00448     km->keynext = km->keynext->next; 
00449     
00450     if( !km->keynext )
00451         return 0;
00452     
00453     return km->keynext;
00454 }
00455 /*
00456 *
00457 */
00458 void * KMapFindNext( KMAP * km )
00459 {
00460     if( !km->keynext )
00461         return 0;
00462     
00463     km->keynext = km->keynext->next; 
00464     
00465     if( !km->keynext )
00466         return 0;
00467     
00468     return km->keynext->userdata;
00469 }
00470 
00471 #ifdef KMAP_MAIN
00472 /*
00473 *
00474 */
00475 int main( int argc, char ** argv )
00476 {
00477     int    i,n=10;
00478     KMAP * km;
00479     char * p;
00480     char   str[80];
00481     
00482     printf("usage: kmap nkeys (default=10)\n\n");
00483     
00484     km = KMapNew( free );  /* use 'free' to free 'userdata' */
00485     
00486     KMapSetNoCase(km,1);  //need to add xlat....
00487     
00488     if( argc > 1 )
00489     {
00490         n = atoi(argv[1]);
00491     }
00492     
00493     for(i=1;i<=n;i++)
00494     {
00495         sprintf(str,"KeyWord%d",i);
00496         KMapAdd( km, str, 0 /* strlen(str) */, strupr(strdup(str)) );
00497         printf("Adding Key=%s\n",str);
00498     }
00499     printf("xmem: %u bytes, %d chars\n",xmalloc_bytes(),km->nchars);
00500     
00501     printf("\nKey Find test...\n");
00502     for(i=1;i<=n;i++)
00503     {
00504         sprintf(str,"KeyWord%d",i);
00505         p = (char*) KMapFind( km, str,  0 /*strlen(str) */ );
00506         if(p)printf("key=%s, data=%*s\n",str,strlen(str),p);
00507         else printf("'%s' NOT found.\n",str);
00508     }
00509     
00510     KMapSetNoCase(km,0);  // this should fail all key searches
00511     printf("\nKey Find test2...\n");
00512     for(i=1;i<=n;i++)
00513     {
00514         sprintf(str,"KeyWord%d",i);
00515         p = (char*) KMapFind( km, str,  0 /*strlen(str) */ );
00516         if(p)printf("key=%s, data=%*s\n",str,strlen(str),p);
00517         else printf("'%s' NOT found.\n",str);
00518     }
00519     
00520     printf("\nKey FindFirst/Next test...\n");
00521     for(p = (char*) KMapFindFirst(km); p; p=(char*)KMapFindNext(km) )
00522         printf("data=%s\n",p);
00523     
00524     printf("\nKey FindFirst/Next test done.\n");
00525     
00526     KMapDelete( km );
00527     
00528     printf("xmem: %u bytes\n",xmalloc_bytes());
00529     
00530     printf("normal pgm finish.\n");
00531     
00532     return 0;
00533 }
00534 
00535 #endif
00536 
00537 

Generated on Sun May 14 14:51:15 2006 by  doxygen 1.4.2