jabberd2  2.2.16
util/xhash.c
Go to the documentation of this file.
00001 /*
00002  * jabberd - Jabber Open Source Server
00003  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
00004  *                    Ryan Eatmon, Robert Norris
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
00019  */
00020 
00021 #include "xhash.h"
00022 #include "util.h"
00023 
00024 
00025 /* Generates a hash code for a string.
00026  * This function uses the ELF hashing algorithm as reprinted in 
00027  * Andrew Binstock, "Hashing Rehashed," Dr. Dobb's Journal, April 1996.
00028  */
00029 static int _xhasher(const char *s, int len)
00030 {
00031     /* ELF hash uses unsigned chars and unsigned arithmetic for portability */
00032     const unsigned char *name = (const unsigned char *)s;
00033     unsigned long h = 0, g;
00034     int i;
00035 
00036     for(i=0;i<len;i++)
00037     { /* do some fancy bitwanking on the string */
00038         h = (h << 4) + (unsigned long)(name[i]);
00039         if ((g = (h & 0xF0000000UL))!=0)
00040             h ^= (g >> 24);
00041         h &= ~g;
00042 
00043     }
00044 
00045     return (int)h;
00046 }
00047 
00048 
00049 static xhn _xhash_node_new(xht h, int index)
00050 {
00051     xhn n;
00052     int i = index % h->prime;
00053 
00054     /* track total */
00055     h->count++;
00056 
00057 #ifdef XHASH_DEBUG
00058     h->stat[i]++;
00059 #endif
00060  
00061     // if the zen[i] is empty, reuse it, else get a new one.
00062     n = &h->zen[i];
00063     
00064     if( n->key != NULL ) 
00065     {
00066         if( h->free_list )
00067         {
00068             n = h->free_list;
00069             h->free_list = h->free_list->next;        
00070         }else
00071             n = pmalloco(h->p, sizeof(_xhn));
00072 
00073         //add it to the bucket list head.
00074         n->prev = &h->zen[i];
00075         n->next = h->zen[i].next;
00076 
00077         if( n->next ) n->next->prev = n;
00078         h->zen[i].next = n;
00079     }
00080 
00081     return n;
00082 }
00083 
00084 
00085 static xhn _xhash_node_get(xht h, const char *key, int len, int index)
00086 {
00087     xhn n;
00088     int i = index % h->prime;
00089     for(n = &h->zen[i]; n != NULL; n = n->next)
00090         if(n->key != NULL && (n->keylen==len) && (strncmp(key, n->key, len) == 0))
00091             return n;
00092     return NULL;
00093 }
00094 
00095 
00096 xht xhash_new(int prime)
00097 {
00098     xht xnew;
00099     pool_t p;
00100 
00101 /*    log_debug(ZONE,"creating new hash table of size %d",prime); */
00102 
00109     p = pool_heap(sizeof(_xhn)*prime + sizeof(_xht));
00110     xnew = pmalloco(p, sizeof(_xht));
00111     xnew->prime = prime;
00112     xnew->p = p;
00113     xnew->zen = pmalloco(p, sizeof(_xhn)*prime); /* array of xhn size of prime */
00114 
00115     xnew->free_list = NULL;
00116     
00117     xnew->iter_bucket = -1;
00118     xnew->iter_node = NULL;
00119 
00120 #ifdef XHASH_DEBUG
00121     xnew->stat = pmalloco(p, sizeof(int)*prime );
00122 #else
00123     xnew->stat = NULL;
00124 #endif
00125 
00126     return xnew;
00127 }
00128 
00129 
00130 void xhash_putx(xht h, const char *key, int len, void *val)
00131 {
00132     int index;
00133     xhn n;
00134 
00135     if(h == NULL || key == NULL)
00136         return;
00137 
00138     index = _xhasher(key,len);
00139 
00140     /* dirty the xht */
00141     h->dirty++;
00142 
00143     /* if existing key, replace it */
00144     if((n = _xhash_node_get(h, key, len, index)) != NULL)
00145     {
00146 /*        log_debug(ZONE,"replacing %s with new val %X",key,val); */
00147 
00148         n->key = key;
00149         n->keylen = len;
00150         n->val = val;
00151         return;
00152     }
00153 
00154 /*    log_debug(ZONE,"saving %s val %X",key,val); */
00155 
00156     /* new node */
00157     n = _xhash_node_new(h, index);
00158     n->key = key;
00159     n->keylen = len;
00160     n->val = val;
00161 }
00162 
00163 void xhash_put(xht h, const char *key, void *val)
00164 {
00165     if(h == NULL || key == NULL) return;
00166     xhash_putx(h,key,strlen(key),val);
00167 }
00168 
00169 
00170 void *xhash_getx(xht h, const char *key, int len)
00171 {
00172     xhn n;
00173 
00174     if(h == NULL || key == NULL || len <= 0 || (n = _xhash_node_get(h, key, len, _xhasher(key,len))) == NULL)
00175     {
00176 /*        log_debug(ZONE,"failed lookup of %s",key); */
00177         return NULL;
00178     }
00179 
00180 /*    log_debug(ZONE,"found %s returning %X",key,n->val); */
00181     return n->val;
00182 }
00183 
00184 void *xhash_get(xht h, const char *key)
00185 {
00186     if(h == NULL || key == NULL) return NULL;
00187     return xhash_getx(h,key,strlen(key));
00188 }
00189 
00190 void xhash_zap_inner( xht h, xhn n, int index)
00191 {
00192     int i = index % h->prime;
00193 
00194     // if element:n is in bucket list and it's not the current iter
00195     if( &h->zen[i] != n && h->iter_node != n )
00196     {
00197         if(n->prev) n->prev->next = n->next;
00198         if(n->next) n->next->prev = n->prev;
00199 
00200         // add it to the free_list head.
00201         n->prev = NULL;
00202         n->next = h->free_list;
00203         h->free_list = n;
00204     }
00205 
00206     //empty the value.
00207     n->key = NULL;
00208     n->val = NULL;
00209 
00210     /* dirty the xht and track the total */
00211     h->dirty++;
00212     h->count--;
00213 
00214 #ifdef XHASH_DEBUG
00215     h->stat[i]--;
00216 #endif
00217 }
00218 
00219 void xhash_zapx(xht h, const char *key, int len)
00220 {
00221     xhn n;
00222     int index;
00223 
00224     if( !h || !key ) return;
00225     
00226     index = _xhasher(key,len);
00227     n = _xhash_node_get(h, key, len, index);
00228     if( !n ) return;
00229 
00230 /*    log_debug(ZONE,"zapping %s",key); */
00231 
00232     xhash_zap_inner(h ,n, index );
00233 }
00234 
00235 void xhash_zap(xht h, const char *key)
00236 {
00237     if(h == NULL || key == NULL) return;
00238     xhash_zapx(h,key,strlen(key));
00239 }
00240 
00241 void xhash_free(xht h)
00242 {
00243 /*    log_debug(ZONE,"hash free %X",h); */
00244 
00246     if(h) pool_free(h->p);
00247 
00248 }
00249 
00250 void xhash_stat( xht h )
00251 {
00252 #ifdef XHASH_DEBUG
00253     if( !h ) return;
00254     
00255     fprintf(stderr, "XHASH: table prime: %d , number of elements: %d\n", h->prime, h->count );
00256 
00257     int i;
00258     for( i = 0; i< h->prime ; ++i )
00259     {
00260         if( h->stat[i] > 1 )
00261             fprintf(stderr, "%d: %d\t", i, h->stat[i]);
00262     }
00263     fprintf(stderr, "\n");
00264     
00265 #endif
00266 }
00267 
00268 void xhash_walk(xht h, xhash_walker w, void *arg)
00269 {
00270     int i;
00271     xhn n;
00272 
00273     if(h == NULL || w == NULL)
00274         return;
00275 
00276 /*    log_debug(ZONE,"walking %X",h); */
00277 
00278     for(i = 0; i < h->prime; i++)
00279         for(n = &h->zen[i]; n != NULL; n = n->next)
00280             if(n->key != NULL && n->val != NULL)
00281                 (*w)(n->key, n->keylen, n->val, arg);
00282 }
00283 
00285 int xhash_dirty(xht h)
00286 {
00287     int dirty;
00288 
00289     if(h == NULL) return 1;
00290 
00291     dirty = h->dirty;
00292     h->dirty = 0;
00293     return dirty;
00294 }
00295 
00297 int xhash_count(xht h)
00298 {
00299     if(h == NULL) return 0;
00300 
00301     return h->count;
00302 }
00303 
00305 pool_t xhash_pool(xht h)
00306 {
00307     return h->p;
00308 }
00309 
00311 int xhash_iter_first(xht h) {
00312     if(h == NULL) return 0;
00313 
00314     h->iter_bucket = -1;
00315     h->iter_node = NULL;
00316 
00317     return xhash_iter_next(h);
00318 }
00319 
00320 int xhash_iter_next(xht h) {
00321     if(h == NULL) return 0;
00322 
00323     /* next in this bucket */
00324     h->iter_node = h->iter_node ? h->iter_node->next : NULL;
00325     while(h->iter_node != NULL) {
00326         xhn n = h->iter_node;
00327 
00328         if(n->key != NULL && n->val != NULL)
00329             return 1;
00330 
00331         h->iter_node = n->next;
00332 
00333         if (n != &h->zen[h->iter_bucket]) {
00334             if(n->prev) n->prev->next = n->next;
00335             if(n->next) n->next->prev = n->prev;
00336 
00337             // add it to the free_list head.
00338             n->prev = NULL;
00339             n->next = h->free_list;
00340             h->free_list = n;
00341         }
00342     }
00343 
00344     /* next bucket */
00345     for(h->iter_bucket++; h->iter_bucket < h->prime; h->iter_bucket++) {
00346         h->iter_node = &h->zen[h->iter_bucket];
00347 
00348         while(h->iter_node != NULL) {
00349             if(h->iter_node->key != NULL && h->iter_node->val != NULL)
00350                 return 1;
00351 
00352             h->iter_node = h->iter_node->next;
00353         }
00354     }
00355 
00356     /* there is no next */
00357     h->iter_bucket = -1;
00358     h->iter_node = NULL;
00359 
00360     return 0;
00361 }
00362 
00363 void xhash_iter_zap(xht h)
00364 {
00365     int index;
00366 
00367     if( !h || !h->iter_node ) return;
00368 
00369     index = _xhasher( h->iter_node->key, h->iter_node->keylen );
00370 
00371     xhash_zap_inner( h ,h->iter_node, index);
00372 }
00373 
00374 int xhash_iter_get(xht h, const char **key, int *keylen, void **val) {
00375     if(h == NULL || (key == NULL && val == NULL) || (key != NULL && keylen == NULL)) return 0;
00376 
00377     if(h->iter_node == NULL) {
00378         if(key != NULL) *key = NULL;
00379         if(val != NULL) *val = NULL;
00380         return 0;
00381     }
00382 
00383     if(key != NULL) {
00384         *key = h->iter_node->key;
00385         *keylen = h->iter_node->keylen;
00386     }
00387     if(val != NULL) *val = h->iter_node->val;
00388 
00389     return 1;
00390 }