hashtb.c

Go to the documentation of this file.
00001 /**
00002  * @file hashtb.c
00003  * @brief Hash table.
00004  * 
00005  * Part of the CCNx C Library.
00006  *
00007  * Copyright (C) 2009 Palo Alto Research Center, Inc.
00008  *
00009  * This library is free software; you can redistribute it and/or modify it
00010  * under the terms of the GNU Lesser General Public License version 2.1
00011  * as published by the Free Software Foundation.
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00015  * Lesser General Public License for more details. You should have received
00016  * a copy of the GNU Lesser General Public License along with this library;
00017  * if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
00018  * Fifth Floor, Boston, MA 02110-1301 USA.
00019  */
00020 #include <stddef.h>
00021 #include <stdint.h>
00022 #include <stdlib.h>
00023 #include <string.h>
00024 
00025 #include <ccn/hashtb.h>
00026 
00027 struct node;
00028 struct node {
00029     struct node* link;
00030     size_t hash;
00031     size_t keysize;
00032     size_t extsize;
00033     /* user data follows immediately, followed by key */
00034 };
00035 #define DATA(ht, p) ((void *)((p) + 1))
00036 #define KEY(ht, p) ((unsigned char *)((p) + 1) + ht->item_size)
00037 
00038 #define CHECKHTE(ht, hte) ((uintptr_t)((hte)->priv[1]) == ~(uintptr_t)(ht))
00039 #define MARKHTE(ht, hte) ((hte)->priv[1] = (void*)~(uintptr_t)(ht))
00040 
00041 struct hashtb {
00042     struct node **bucket;
00043     size_t item_size;           /* Size of client's per-entry data */
00044     unsigned n_buckets;
00045     int n;                      /* Number of entries */
00046     int refcount;               /* Number of open enumerators */
00047     struct node *deferred;      /* deferred cleanup */
00048     struct hashtb_param param;  /* saved client parameters */
00049 };
00050 
00051 size_t
00052 hashtb_hash(const unsigned char *key, size_t key_size)
00053 {
00054     size_t h;
00055     size_t i;
00056     for (h = key_size + 23, i = 0; i < key_size; i++)
00057         h = ((h << 6) ^ (h >> 27)) + key[i];
00058     return(h);
00059 }
00060 
00061 struct hashtb *
00062 hashtb_create(size_t item_size, const struct hashtb_param *param)
00063 {
00064     struct hashtb *ht;
00065     ht = calloc(1, sizeof(*ht));
00066     if (ht != NULL) {
00067         ht->item_size = item_size;
00068         ht->n = 0;
00069         ht->n_buckets = 7;
00070         ht->bucket = calloc(ht->n_buckets, sizeof(ht->bucket[0]));
00071         if (ht->bucket == NULL) {
00072                 free(ht);
00073                 return (NULL); /*ENOMEM*/
00074         }
00075         if (param != NULL)
00076             ht->param = *param;
00077     }
00078     return(ht);
00079 }
00080 
00081 void *
00082 hashtb_get_param(struct hashtb *ht, struct hashtb_param *param)
00083 {
00084     if (param != NULL)
00085         *param = ht->param;
00086     return(ht->param.finalize_data);
00087 }
00088 
00089 void
00090 hashtb_destroy(struct hashtb **htp)
00091 {
00092     if (*htp != NULL) {
00093         struct hashtb_enumerator tmp;
00094         struct hashtb_enumerator *e = hashtb_start(*htp, &tmp);
00095         while (e->key != NULL)
00096             hashtb_delete(e);
00097         hashtb_end(&tmp);
00098         if ((*htp)->refcount == 0) {
00099             free((*htp)->bucket);
00100             free(*htp);
00101             *htp = NULL;
00102         }
00103         else
00104             abort(); /* perhaps a bit brutal... */
00105     }
00106 }
00107 
00108 int
00109 hashtb_n(struct hashtb *ht)
00110 {
00111     return(ht->n);
00112 }
00113 
00114 void *
00115 hashtb_lookup(struct hashtb *ht, const void *key, size_t keysize)
00116 {
00117     struct node *p;
00118     size_t h;
00119     if (key == NULL)
00120         return(NULL);
00121     h = hashtb_hash(key, keysize);
00122     for (p = ht->bucket[h % ht->n_buckets]; p != NULL; p = p->link) {
00123         if (p->hash < h)
00124             continue;
00125         if (p->hash > h)
00126             break;
00127         if (keysize == p->keysize && 0 == memcmp(key, KEY(ht, p), keysize))
00128             return(DATA(ht, p));
00129     }
00130     return(NULL);
00131 }
00132 
00133 static void
00134 setpos(struct hashtb_enumerator *hte, struct node **pp)
00135 {
00136     struct hashtb *ht = hte->ht;
00137     struct node *p = NULL;
00138     hte->priv[0] = pp;
00139     if (pp != NULL)
00140         p = *pp;
00141     if (p == NULL) {
00142         hte->key = NULL;
00143         hte->keysize = 0;
00144         hte->extsize = 0;
00145         hte->data = NULL;
00146     }
00147     else {
00148         hte->key = KEY(ht, p);
00149         hte->keysize = p->keysize;
00150         hte->extsize = p->extsize;
00151         hte->data = DATA(ht, p);
00152     }
00153 }
00154 
00155 static struct node **
00156 scan_buckets(struct hashtb *ht, unsigned b)
00157 {
00158     for (; b < ht->n_buckets; b++)
00159         if (ht->bucket[b] != NULL)
00160             return &(ht->bucket[b]);
00161     return(NULL);
00162 }
00163 
00164 #define MAX_ENUMERATORS 30
00165 struct hashtb_enumerator *
00166 hashtb_start(struct hashtb *ht, struct hashtb_enumerator *hte)
00167 {
00168     MARKHTE(ht, hte);
00169     hte->datasize = ht->item_size;
00170     hte->ht = ht;
00171     ht->refcount++;
00172     if (ht->refcount > MAX_ENUMERATORS)
00173         abort(); /* probably somebody is missing a call to hashtb_end() */
00174     setpos(hte, scan_buckets(ht, 0));
00175     return(hte);
00176 }
00177 
00178 void
00179 hashtb_end(struct hashtb_enumerator *hte)
00180 {
00181     struct hashtb *ht = hte->ht;
00182     struct node *p;
00183     hashtb_finalize_proc f;
00184     if (!CHECKHTE(ht, hte) || ht->refcount <= 0) abort();
00185     if (ht->refcount == 1) {
00186         /* do deferred deallocation */
00187         f = ht->param.finalize;
00188         while (ht->deferred != NULL) {
00189             setpos(hte, &(ht->deferred));
00190             if (f != NULL)
00191                 (*f)(hte);
00192             p = ht->deferred;
00193             ht->deferred = p->link;
00194             free(p);
00195         }
00196     }
00197     hte->priv[0] = 0;
00198     hte->priv[1] = 0;
00199     ht->refcount--;
00200 }
00201 
00202 void
00203 hashtb_next(struct hashtb_enumerator *hte)
00204 {
00205     struct node **pp = hte->priv[0];
00206     struct node **ppp;
00207     if (pp != NULL) {
00208         ppp = pp;
00209         pp = &((*pp)->link);
00210         if (*pp == NULL)
00211            pp = scan_buckets(hte->ht, ((*ppp)->hash % hte->ht->n_buckets) + 1);
00212     }
00213     setpos(hte, pp);
00214 }
00215 
00216 int
00217 hashtb_seek(struct hashtb_enumerator *hte, const void *key, size_t keysize, size_t extsize)
00218 {
00219     struct node *p = NULL;
00220     struct hashtb *ht = hte->ht;
00221     struct node **pp;
00222     size_t h;
00223     if (key == NULL) {
00224         setpos(hte, NULL);
00225         return(-1);
00226     }
00227     if (ht->refcount == 1 && ht->n > ht->n_buckets * 3) {
00228         ht->refcount--;
00229         hashtb_rehash(ht, 2 * ht->n + 1);
00230         ht->refcount++;
00231     }
00232     h = hashtb_hash(key, keysize);
00233     pp = &(ht->bucket[h % ht->n_buckets]);
00234     for (p = *pp; p != NULL; pp = &(p->link), p = p->link) {
00235         if (p->hash < h)
00236             continue;
00237         if (p->hash > h)
00238             break;
00239         if (keysize == p->keysize && 0 == memcmp(key, KEY(ht, p), keysize)) {
00240             setpos(hte, pp);
00241             return(HT_OLD_ENTRY);
00242         }
00243     }
00244     p = calloc(1, sizeof(*p) + ht->item_size + keysize + extsize);
00245     if (p == NULL) {
00246         setpos(hte, NULL);
00247         return(-1);
00248     }
00249     memcpy(KEY(ht, p), key, keysize + extsize);
00250     p->hash = h;
00251     p->keysize = keysize;
00252     p->extsize = extsize;
00253     p->link = *pp;
00254     *pp = p;
00255     hte->ht->n += 1;
00256     setpos(hte, pp);
00257     return(HT_NEW_ENTRY);
00258 }
00259 
00260 void
00261 hashtb_delete(struct hashtb_enumerator *hte)
00262 {
00263     struct hashtb *ht = hte->ht;
00264     struct node **pp = hte->priv[0];
00265     struct node *p = *pp;
00266     if ((p != NULL) && CHECKHTE(ht, hte) && KEY(ht, p) == hte->key) {
00267         *pp = p->link;
00268         if (*pp == NULL)
00269            pp = scan_buckets(hte->ht, (p->hash % hte->ht->n_buckets) + 1);
00270         hte->ht->n -= 1;
00271         if (ht->refcount == 1) {
00272             hashtb_finalize_proc f = ht->param.finalize;
00273             if (f != NULL)
00274                 (*f)(hte);
00275             free(p);
00276         }
00277         else {
00278             p->link = ht->deferred;
00279             ht->deferred = p;
00280         }
00281         setpos(hte, pp);
00282     }
00283 }
00284 
00285 void
00286 hashtb_rehash(struct hashtb *ht, unsigned n_buckets)
00287 {
00288     struct node **bucket = NULL;
00289     struct node **pp;
00290     struct node *p;
00291     struct node *q;
00292     size_t h;
00293     unsigned i;
00294     unsigned b;
00295     if (ht->refcount != 0 || n_buckets < 1 || n_buckets == ht->n_buckets)
00296         return;
00297     bucket = calloc(n_buckets, sizeof(bucket[0]));
00298     if (bucket == NULL) return; /* ENOMEM */
00299     for (i = 0; i < ht->n_buckets; i++) {
00300         for (p = ht->bucket[i]; p != NULL; p = q) {
00301             q = p->link;
00302             h = p->hash;
00303             b = h % n_buckets;
00304             for (pp = &bucket[b]; *pp != NULL && ((*pp)->hash < h); pp = &((*pp)->link))
00305                 continue;
00306             p->link = *pp;
00307             *pp = p;
00308         }
00309     }
00310     free(ht->bucket);
00311     ht->bucket = bucket;
00312     ht->n_buckets = n_buckets;
00313 }
00314 

Generated on Thu Feb 16 00:44:00 2012 for Content-Centric Networking in C by  doxygen 1.5.6