Main Page | Modules | Data Structures | File List | Data Fields

hmap.c

00001 /* 
00002  * Copyright (c) 2005-2008 by KoanLogic s.r.l. - All rights reserved.  
00003  */
00004 
00005 static const char rcsid[] =
00006     "$Id: hmap.c,v 1.18 2008/03/11 07:14:16 tho Exp $";
00007 
00008 #include <stdlib.h>
00009 #include <unistd.h>
00010 #include <string.h>
00011 #include <stdio.h>
00012 
00013 #include <toolbox/memory.h>
00014 #include <toolbox/carpal.h>
00015 #include <toolbox/hmap.h>
00016 #include <toolbox/misc.h>
00017 
00023 /* default limits handled by policies */
00024 #define U_HMAP_MAX_SIZE      512
00025 #define U_HMAP_MAX_ELEMS     U_HMAP_MAX_SIZE
00026 #define U_HMAP_RATE_FULL     0.75
00027 #define U_HMAP_RATE_RESIZE   3
00028 
00029 
00030 /* policy queue object */
00031 struct u_hmap_q_s 
00032 {
00033     void *key,
00034          *o;
00035 
00036     TAILQ_ENTRY(u_hmap_q_s) next;
00037 };
00038 
00039 /* hmap policy representation */
00040 struct u_hmap_pcy_s 
00041 {
00042     int (*pop)(u_hmap_t *hmap, u_hmap_o_t **obj); 
00043     int (*push)(u_hmap_t *hmap, u_hmap_o_t *obj,
00044             u_hmap_q_t **data); 
00045 
00046     enum {
00047         U_HMAP_PCY_OP_PUT = 0x1,
00048         U_HMAP_PCY_OP_GET = 0x2
00049     } ops;
00050     
00051     TAILQ_HEAD(u_hmap_q_h_s, u_hmap_q_s) queue;
00052 };
00053 typedef struct u_hmap_q_h_s u_hmap_q_h_t;
00054 
00055 /* hmap representation */
00056 struct u_hmap_s 
00057 {
00058     u_hmap_opts_t *opts;        /* hmap options */
00059 
00060     size_t sz,                  /* current size */
00061            size,                /* array size */
00062            threshold,           /* when to resize */
00063            px;                  /* index into prime numbers array */
00064 
00065     u_hmap_pcy_t pcy;    /* discard policy */
00066 
00067     LIST_HEAD(u_hmap_e_s, u_hmap_o_s) *hmap;    /* the hashmap */
00068 };
00069 typedef struct u_hmap_e_s u_hmap_e_t;
00070 
00071 static int _get (u_hmap_t *hmap, void *key, 
00072         u_hmap_o_t **o);
00073 
00074 static int _opts_check (u_hmap_opts_t *opts);
00075 static int _pcy_setup (u_hmap_t *hmap);
00076 static const char *_pcy2str(u_hmap_pcy_type_t policy);
00077 
00078 static void _o_free (u_hmap_t *hmap, u_hmap_o_t *obj);
00079 
00080 static u_hmap_q_t *_q_o_new (void *key);
00081 static void _q_o_free (u_hmap_q_t *s);
00082 
00083 static size_t _f_hash (void *key, size_t size);
00084 static int _f_comp (void *k1, void *k2);
00085 static void _f_free (u_hmap_o_t *obj);
00086 static u_string_t *_f_str (u_hmap_o_t *obj);
00087 
00088 static int _queue_push (u_hmap_t *hmap, u_hmap_o_t *obj, 
00089         u_hmap_q_t **data);
00090 static int _queue_push_count (u_hmap_t *hmap, u_hmap_o_t *obj,
00091         u_hmap_q_t **counts);
00092 static int _queue_pop_front (u_hmap_t *hmap, u_hmap_o_t **obj);
00093 static int _queue_pop_back (u_hmap_t *hmap, u_hmap_o_t **obj);
00094 
00095 static int _resize(u_hmap_t *hmap);
00096 static int _next_prime(size_t *prime, size_t sz, size_t *idx);
00097 
00098 
00106 const char *u_hmap_strerror (u_hmap_ret_t rc)
00107 {
00108     switch (rc)
00109     {
00110         case U_HMAP_ERR_NONE:
00111             return "success";
00112         case U_HMAP_ERR_FAIL:
00113             return "general failure";
00114         case U_HMAP_ERR_EXISTS:
00115             return "element already exists in table";
00116     }
00117     return NULL;
00118 }
00119 
00120 /* Default hash function */
00121 static size_t _f_hash (void *key, size_t size)
00122 {
00123     size_t h = 0;
00124     unsigned char *k = (unsigned char *) key;
00125 
00126     dbg_ifb (key == NULL) return -1;
00127 
00128     while (*k)
00129     {
00130         h += *k++;
00131         h += (h << 10);
00132         h ^= (h >> 6);
00133     }
00134 
00135     h += (h << 3);
00136     h ^= (h >> 11);
00137 
00138     return (h + (h << 15)) % size;
00139 }
00140 
00141 /* Default comparison function for key comparison */
00142 static int _f_comp (void *k1, void *k2) 
00143 {
00144     dbg_ifb (k1 == NULL) return -1;    
00145     dbg_ifb (k2 == NULL) return -1;  
00146     
00147     return strcmp((char *)k1, (char *)k2);
00148 }
00149 
00150 /* Default function for freeing hmap objects  */
00151 static void _f_free (u_hmap_o_t *obj)
00152 {
00153     dbg_ifb (obj == NULL) return;
00154 
00155     u_free(obj->key); 
00156     u_free(obj->val); 
00157 }
00158 
00159 /* Default string representation of objects */
00160 static u_string_t *_f_str (u_hmap_o_t *obj)
00161 {
00162     enum { MAX_OBJ_STR = 256 };
00163     char buf[MAX_OBJ_STR];
00164     u_string_t *s = NULL;
00165     char *key,
00166          *val;
00167 
00168     dbg_err_if (obj == NULL);
00169 
00170     key = (char *) obj->key,
00171     val = (char *) obj->val;
00172 
00173     dbg_err_if (u_snprintf(buf, MAX_OBJ_STR, "[%s:%s]", key, val));    
00174     dbg_err_if (u_string_create(buf, strlen(buf)+1, &s));
00175 
00176     return s;
00177 
00178 err:
00179     return NULL;
00180 }
00181 
00182 /* Check validity of options */
00183 static int _opts_check (u_hmap_opts_t *opts)
00184 {
00185     dbg_err_if (opts == NULL);
00186 
00187     dbg_err_if (opts->size == 0);
00188     dbg_err_if (opts->max == 0);
00189     dbg_err_if (opts->type != U_HMAP_TYPE_CHAIN && 
00190             opts->type != U_HMAP_TYPE_LINEAR);
00191     dbg_err_if (opts->policy < U_HMAP_PCY_NONE || 
00192             opts->policy > U_HMAP_PCY_LFU);
00193     dbg_err_if (opts->f_hash == NULL);
00194     dbg_err_if (opts->f_comp == NULL);
00195 
00196     return U_HMAP_ERR_NONE;
00197 
00198 err:
00199     return U_HMAP_ERR_FAIL;
00200 }
00201 
00202 /* Setup policy parameters */
00203 static int _pcy_setup (u_hmap_t *hmap) 
00204 {
00205     dbg_return_if (hmap == NULL, ~0);
00206 
00207     switch (hmap->opts->policy) 
00208     {
00209         case U_HMAP_PCY_NONE:
00210             hmap->pcy.push = NULL;
00211             hmap->pcy.pop = NULL;
00212             hmap->pcy.ops = 0;
00213             break;
00214         case U_HMAP_PCY_LRU:
00215             hmap->pcy.push = _queue_push;
00216             hmap->pcy.pop = _queue_pop_back;
00217             hmap->pcy.ops = U_HMAP_PCY_OP_PUT | U_HMAP_PCY_OP_GET;
00218             break;
00219         case U_HMAP_PCY_FIFO:
00220             hmap->pcy.push = _queue_push;
00221             hmap->pcy.pop = _queue_pop_back;
00222             hmap->pcy.ops = U_HMAP_PCY_OP_PUT;
00223             break;
00224         case U_HMAP_PCY_LFU:
00225             hmap->pcy.push = _queue_push_count;
00226             hmap->pcy.pop = _queue_pop_front;
00227             hmap->pcy.ops = U_HMAP_PCY_OP_PUT | U_HMAP_PCY_OP_GET;
00228             break;
00229         default:
00230             dbg("Invalid policy: %d", hmap->opts->policy);
00231             return U_HMAP_ERR_FAIL;
00232     }
00233 
00234     return U_HMAP_ERR_NONE;
00235 }
00236 
00249 int u_hmap_new (u_hmap_opts_t *opts, u_hmap_t **hmap)
00250 {
00251     size_t i;
00252     u_hmap_t *c = NULL;
00253 
00254     /* allow (opts == NULL) */
00255     dbg_return_if (hmap == NULL, ~0);
00256    
00257     dbg_return_sif ((c = (u_hmap_t *) u_zalloc(sizeof(u_hmap_t))) == NULL, ~0);
00258     
00259     dbg_err_if (u_hmap_opts_new(&c->opts));
00260     if (opts)
00261     {
00262         dbg_err_if (u_hmap_opts_copy(c->opts, opts));
00263         dbg_err_if (_opts_check(c->opts));
00264     }
00265     u_hmap_opts_dbg(c->opts);
00266     dbg_err_if (_pcy_setup(c));
00267 
00268     c->size = c->opts->size;
00269     dbg_err_if (_next_prime(&c->size, c->size, &c->px));
00270     c->threshold = U_HMAP_RATE_FULL * c->size;
00271 
00272     dbg_err_sif ((c->hmap = (u_hmap_e_t *) 
00273                 u_zalloc(sizeof(u_hmap_e_t) * 
00274                     c->size)) == NULL);
00275 
00276     /* initialise entries */
00277     for (i = 0; i < c->size; ++i)
00278         LIST_INIT(&c->hmap[i]);
00279 
00280     TAILQ_INIT(&c->pcy.queue);
00281     
00282     dbg("[hmap]");
00283     dbg("threshold: %u", c->threshold);
00284 
00285     *hmap = c;
00286 
00287     return U_HMAP_ERR_NONE;
00288 
00289 err:
00290     u_free(c);
00291     *hmap = NULL;    
00292     return U_HMAP_ERR_FAIL;
00293 }
00294 
00306 int u_hmap_copy (u_hmap_t *to, u_hmap_t *from)
00307 {
00308     u_hmap_o_t *obj;
00309     size_t i;
00310     
00311     dbg_err_if (to == NULL);
00312     dbg_err_if (from == NULL);
00313 
00314     for (i = 0; i < from->size; ++i) 
00315     { 
00316         while ((obj = LIST_FIRST(&from->hmap[i])) != NULL) 
00317         {
00318             LIST_REMOVE(obj, next);
00319             dbg_err_if (u_hmap_put(to, obj, NULL));
00320         }
00321     }
00322 
00323     return U_HMAP_ERR_NONE;
00324 
00325 err:
00326     return U_HMAP_ERR_FAIL;
00327 }
00328 
00336 void u_hmap_dbg (u_hmap_t *hmap)
00337 {
00338     enum { MAX_LINE = 255 };
00339     u_string_t *s = NULL, *st = NULL;
00340     u_hmap_o_t *obj;
00341     size_t i;
00342 
00343     dbg_ifb (hmap == NULL) return;
00344 
00345     dbg ("<hmap>");
00346     for (i = 0; i < hmap->size; ++i) 
00347     {
00348         dbg_ifb (u_string_create("", 1, &s)) return;
00349         dbg_err_if (u_string_clear(s));
00350         dbg_err_if (u_string_append(s, "|", 1));
00351 
00352         LIST_FOREACH(obj, &hmap->hmap[i], next) 
00353         {
00354             if (hmap->opts->f_str == NULL) 
00355             {
00356                 dbg_err_if (u_string_append(s, "[]", 2));
00357             } else {
00358                 st = hmap->opts->f_str(obj);
00359                 dbg_err_if (u_string_append(s, u_string_c(st),
00360                             u_string_len(st)-1));
00361                 u_string_free(st);
00362             }
00363         } 
00364         dbg_err_if (u_string_append(s, "|", 1));
00365         dbg(u_string_c(s));
00366         dbg_ifb (u_string_free(s)) return;
00367     }
00368     dbg("</hmap>");
00369     return;
00370 
00371 err:
00372     U_FREEF(st, u_string_free);
00373     U_FREEF(s, u_string_free);
00374     return;   
00375 }
00376 
00377 /*
00378  * \brief   Delete an object from the hmap
00379  * 
00380  * Delete object with given \a key from \a hmap and return it (if the object is
00381  * owned by user).
00382  * 
00383  * \param hmap      hmap object
00384  * \param key       key of object to be deleted 
00385  * \param obj       deleted object
00386  * 
00387  * \return U_HMAP_ERR_NONE on success, U_HMAP_ERR_FAIL on failure
00388  */
00389 int u_hmap_del (u_hmap_t *hmap, void *key, u_hmap_o_t **obj) 
00390 {
00391     u_hmap_o_t *o = NULL;
00392 
00393     dbg_err_if (hmap == NULL);
00394     dbg_err_if (key == NULL);
00395 
00396     if (obj)
00397         *obj = NULL;
00398 
00399     if (_get(hmap, key, &o))
00400         return U_HMAP_ERR_FAIL;
00401 
00402     dbg_err_if (o == NULL);
00403     LIST_REMOVE(o, next);
00404 
00405     if (hmap->opts->options & U_HMAP_OPTS_OWNSDATA)
00406         _o_free(hmap, o);
00407     else
00408         if (obj)
00409             *obj = o;
00410 
00411     hmap->sz--;
00412 
00413     return U_HMAP_ERR_NONE;
00414     
00415 err:
00416     return U_HMAP_ERR_FAIL;
00417 }
00418 
00419 /* Retrieve an hmap element given a key */
00420 static int _get (u_hmap_t *hmap, void *key, 
00421                        u_hmap_o_t **o)
00422 {
00423     u_hmap_o_t *obj;
00424     u_hmap_e_t *x;
00425     int comp;
00426         size_t hash;
00427 
00428     dbg_err_if (hmap == NULL);
00429     dbg_err_if (key == NULL);
00430     dbg_err_if (o == NULL);
00431 
00432         hash = hmap->opts->f_hash(key, hmap->size);
00433 
00434         if (hmap->opts->f_hash != &_f_hash && 
00435                         !(hmap->opts->options & U_HMAP_OPTS_HASH_STRONG)) {
00436                 enum { MAX_INT = 20 };
00437                 char h[MAX_INT];
00438 
00439                 u_snprintf(h, MAX_INT, "%u", hash);
00440                 hash = _f_hash(h, hmap->size);
00441         }
00442 
00443         x = &hmap->hmap[hash];
00444 
00445     switch (hmap->opts->type)
00446     {
00447         case U_HMAP_TYPE_CHAIN:
00448 
00449             LIST_FOREACH(obj, x, next) 
00450             {
00451                 if ((comp = hmap->opts->f_comp(key, obj->key)) == 0) 
00452                 { /* object found */ 
00453                     *o = obj;
00454                     return U_HMAP_ERR_NONE;
00455                 } else if (comp < 0) { /* cannot be in list (ordered) */
00456                     *o = NULL;
00457                     break;
00458                 }
00459             }
00460             break;
00461 
00462 
00463         case U_HMAP_TYPE_LINEAR:
00464             {
00465                 size_t last = ((hash + hmap->size -1) % hmap->size);
00466 
00467                 for (; hash != last; hash = ((hash +1) % hmap->size), 
00468                         x = &hmap->hmap[hash])
00469                 {
00470                     if (!LIST_EMPTY(x)) 
00471                     {
00472                         obj = LIST_FIRST(x);
00473                         
00474                         if ((hmap->opts->f_comp(key, obj->key)) == 0)
00475                         {
00476                             *o = obj;
00477                             return U_HMAP_ERR_NONE; 
00478                         }
00479                     }
00480                 }
00481             }
00482 
00483             break;
00484     }
00485 
00486 err: 
00487     return U_HMAP_ERR_FAIL;
00488 }
00489 
00497 void u_hmap_pcy_dbg (u_hmap_t *hmap)
00498 {
00499     u_hmap_q_t *data;
00500     u_string_t *s = NULL;
00501 
00502     dbg_ifb (hmap == NULL) return;
00503 
00504     dbg_ifb (u_string_create("", 1, &s)) return;
00505     dbg_err_if (u_string_clear(s));
00506     dbg_err_if (u_string_append(s, "Policy: [", strlen("Policy: [")));
00507 
00508     TAILQ_FOREACH(data, &hmap->pcy.queue, next) 
00509     {
00510         dbg_err_if (u_string_append(s, "(", 1));
00511         dbg_err_if (u_string_append(s, data->key, strlen(data->key)));
00512         dbg_err_if (u_string_append(s, ")", 1));
00513     }
00514     dbg_err_if (u_string_append(s, "]", 1));
00515     dbg(u_string_c(s));
00516     dbg_if (u_string_free(s));
00517 
00518     return;
00519     
00520  err:
00521     U_FREEF(s, u_string_free);
00522     return;
00523 }
00524 
00525 /* pop the front of an object queue */
00526 static int _queue_pop_front (u_hmap_t *hmap, u_hmap_o_t **obj)
00527 {
00528     u_hmap_q_t *first;
00529 
00530     dbg_err_if (hmap == NULL);
00531 
00532     dbg_err_if ((first = TAILQ_FIRST(&hmap->pcy.queue)) == NULL);
00533     dbg_err_if (u_hmap_del(hmap, first->key, obj));
00534     TAILQ_REMOVE(&hmap->pcy.queue, first, next);
00535     _q_o_free(first);
00536 
00537     return U_HMAP_ERR_NONE;
00538 
00539 err:
00540     return U_HMAP_ERR_FAIL;
00541 }
00542 
00543 /* pop the back of an object queue */
00544 static int _queue_pop_back (u_hmap_t *hmap, u_hmap_o_t **obj)
00545 {
00546     u_hmap_q_t *last;
00547 
00548     dbg_err_if (hmap == NULL);
00549 
00550     dbg_err_if ((last = TAILQ_LAST(&hmap->pcy.queue, u_hmap_q_h_s))
00551             == NULL);
00552     dbg_err_if (u_hmap_del(hmap, last->key, obj));
00553     TAILQ_REMOVE(&hmap->pcy.queue, last, next);
00554     _q_o_free(last);
00555     
00556     return U_HMAP_ERR_NONE;
00557 
00558 err: 
00559     return U_HMAP_ERR_FAIL;
00560 }
00561 
00562 /* push object data onto queue */
00563 static int _queue_push (u_hmap_t *hmap, u_hmap_o_t *obj,
00564         u_hmap_q_t **data)
00565 {
00566     u_hmap_q_t *new;
00567 
00568     dbg_err_if (hmap == NULL);
00569     dbg_err_if (obj == NULL);
00570     dbg_err_if (data == NULL);
00571 
00572     if (*data == NULL) 
00573     {  /* no reference to queue entry */
00574         dbg_err_if ((new = _q_o_new(obj->key)) == NULL);
00575         TAILQ_INSERT_HEAD(&hmap->pcy.queue, new, next);
00576         *data = new;
00577     } else { /* have element in queue - move to head */
00578         TAILQ_REMOVE(&hmap->pcy.queue, *data, next);
00579         TAILQ_INSERT_HEAD(&hmap->pcy.queue, *data, next);
00580     }
00581     return U_HMAP_ERR_NONE;
00582     
00583 err:
00584     return U_HMAP_ERR_FAIL;
00585 }
00586 
00587 /* Increment count data object and push onto queue */
00588 static int _queue_push_count (u_hmap_t *hmap, u_hmap_o_t *obj, 
00589         u_hmap_q_t **counts)
00590 {
00591     u_hmap_q_t *new, *t;
00592     int *count;
00593 
00594     dbg_err_if (hmap == NULL);
00595     dbg_err_if (obj == NULL);
00596     dbg_err_if (counts == NULL);
00597 
00598     if (*counts == NULL) /* no reference to queue entry */
00599     {  
00600         dbg_err_if ((new = _q_o_new(obj->key)) == NULL);
00601         TAILQ_INSERT_HEAD(&hmap->pcy.queue, new, next);
00602         *counts = TAILQ_FIRST(&hmap->pcy.queue);
00603         dbg_err_sif ((count = (int *) u_zalloc(sizeof(int))) == NULL);
00604         new->o = (void *) count;
00605         *counts = new;
00606     } else { /* have element in queue - move to head */
00607         count = (int *) (*counts)->o;
00608         memset((void *) count, (*count)++, sizeof(int));
00609 
00610         if ((t = TAILQ_NEXT(*counts, next))) 
00611         {
00612             for (; t && ((*count) > *((int *) t->o)); t = TAILQ_NEXT(t, next))
00613                 ;
00614             TAILQ_REMOVE(&hmap->pcy.queue, *counts, next);
00615             if (t)
00616                 TAILQ_INSERT_BEFORE(t, *counts, next);
00617             else
00618                 TAILQ_INSERT_TAIL(&hmap->pcy.queue, *counts, next);
00619         }
00620     }
00621     return U_HMAP_ERR_NONE;
00622     
00623 err:
00624     return U_HMAP_ERR_FAIL;
00625 }
00626 
00641 int u_hmap_put (u_hmap_t *hmap, u_hmap_o_t *obj, u_hmap_o_t **old)
00642 {
00643     u_hmap_o_t *o;
00644     u_hmap_e_t *x;
00645     int comp;
00646         size_t hash;
00647 
00648     dbg_err_if (hmap == NULL);
00649     dbg_err_if (obj == NULL);
00650 
00651     if (old)
00652         *old = NULL;
00653 
00654     if (hmap->sz >= hmap->threshold) {
00655         dbg("hmap full");
00656         if (hmap->opts->policy == U_HMAP_PCY_NONE) {
00657             dbg_err_if (_resize(hmap));
00658         } else {
00659             dbg("freeing according to policy %d", hmap->opts->policy);
00660             dbg_err_if (hmap->pcy.pop(hmap, old));
00661         }
00662     }
00663 
00664     hash = hmap->opts->f_hash(obj->key, hmap->size);
00665 
00666     if (hmap->opts->f_hash != &_f_hash &&
00667             !(hmap->opts->options & U_HMAP_OPTS_HASH_STRONG)) {
00668         enum { MAX_INT = 20 };
00669         char h[MAX_INT];
00670 
00671         u_snprintf(h, MAX_INT, "%u", hash);
00672         hash = _f_hash(h, hmap->size);
00673     }
00674 
00675     if (hmap->opts->policy != U_HMAP_PCY_NONE &&
00676             hmap->sz >= hmap->opts->max) 
00677     {
00678         dbg("Cache full - freeing according to policy %d", hmap->opts->policy);
00679         hmap->pcy.pop(hmap, old);
00680     }
00681 
00682         x = &hmap->hmap[hash];
00683 
00684     switch (hmap->opts->type) 
00685     {
00686         case U_HMAP_TYPE_CHAIN:
00687 
00688             if (LIST_EMPTY(x))
00689             {
00690                 LIST_INSERT_HEAD(x, obj, next);
00691                 goto end;
00692             } else {
00693                 LIST_FOREACH(o, x, next) 
00694                 {
00695                     /* object already hmapd */
00696                     if ((comp = hmap->opts->f_comp(obj->key, o->key)) == 0) 
00697                     { 
00698                         /* overwrite */
00699                         if (!(hmap->opts->options & U_HMAP_OPTS_NO_OVERWRITE))
00700                         {
00701                             LIST_INSERT_AFTER(o, obj, next);
00702                             LIST_REMOVE(o, next);
00703                             hmap->sz--;
00704                             /* XXX pop from policy queue */
00705 
00706                             if (hmap->opts->options & U_HMAP_OPTS_OWNSDATA)
00707                                 _o_free(hmap, o);
00708                             else
00709                                 if (old)
00710                                     *old = o;
00711 
00712                             goto end;
00713 
00714                         /* don't overwrite */
00715                         } else {
00716 
00717                             if (hmap->opts->options & U_HMAP_OPTS_OWNSDATA)
00718                                 _o_free(hmap, obj);                          
00719                             else
00720                                 if (old) 
00721                                     *old = obj; 
00722                             
00723                             return U_HMAP_ERR_EXISTS; 
00724                         }
00725                     } else { 
00726                         if (comp < 0) 
00727                         {
00728                             LIST_INSERT_BEFORE(o, obj, next); 
00729                             goto end;
00730                         } else if (!LIST_NEXT(o, next)) {
00731                             LIST_INSERT_AFTER(o, obj, next);
00732                             goto end;
00733                         }
00734                     }
00735                 }
00736             }
00737             break;
00738 
00739         case U_HMAP_TYPE_LINEAR:
00740 
00741             {
00742                 size_t last = ((hash + hmap->size -1) % hmap->size);
00743 
00744                 for (; hash != last; hash = ((hash+1) % hmap->size), 
00745                         x = &hmap->hmap[hash])
00746                 {
00747                     if (LIST_EMPTY(x)) 
00748                     {
00749                         LIST_INSERT_HEAD(x, obj, next);
00750                         goto end;
00751 
00752                     } else {
00753 
00754                         o = LIST_FIRST(x);
00755 
00756                         /* object already hmapd */
00757                         if (hmap->opts->f_comp(o->key, obj->key) == 0)
00758                         {
00759                             /* overwrite */
00760                             if (!(hmap->opts->options & U_HMAP_OPTS_NO_OVERWRITE)) 
00761                             {
00762                                 LIST_INSERT_AFTER(o, obj, next);
00763                                 LIST_REMOVE(o, next);
00764                                 hmap->sz--;
00765                                 /* XXX pop from policy queue */
00766 
00767                                 if (hmap->opts->options & U_HMAP_OPTS_OWNSDATA)
00768                                     _o_free(hmap, obj);
00769                                 else 
00770                                     if (old)
00771                                         *old = obj;
00772 
00773                                 goto end;
00774 
00775                             /* don't overwrite */
00776                             } else {
00777 
00778                                 if (hmap->opts->options & U_HMAP_OPTS_OWNSDATA)
00779                                     _o_free(hmap, obj);
00780                                 else 
00781                                     if (old)
00782                                         *old = obj;
00783 
00784                                 return U_HMAP_ERR_EXISTS;
00785                             }
00786                         }
00787                     }
00788                 }
00789             }
00790             break;
00791     }
00792 
00793 err:
00794     return U_HMAP_ERR_FAIL;
00795 
00796 end:
00797     hmap->sz++;
00798 
00799     if (hmap->pcy.ops & U_HMAP_PCY_OP_PUT)
00800         hmap->pcy.push(hmap, obj, &obj->pqe);
00801 
00802     return U_HMAP_ERR_NONE;
00803 }
00804 
00818 int u_hmap_get (u_hmap_t *hmap, void *key, u_hmap_o_t **obj) 
00819 {
00820     dbg_err_if (hmap == NULL);
00821     dbg_err_if (key == NULL);
00822     dbg_err_if (obj == NULL);
00823 
00824     if (_get(hmap, key, obj)) 
00825     {
00826         *obj = NULL;
00827         return U_HMAP_ERR_FAIL;
00828     }
00829     dbg_err_if (obj == NULL);
00830 
00831     if (hmap->pcy.ops & U_HMAP_PCY_OP_GET)
00832         hmap->pcy.push(hmap, *obj, &(*obj)->pqe);
00833         
00834     return U_HMAP_ERR_NONE;
00835 
00836 err:
00837     return U_HMAP_ERR_FAIL;
00838 }
00839 
00851 int u_hmap_foreach (u_hmap_t *hmap, int f(void *val))
00852 {
00853     u_hmap_o_t *obj;
00854     size_t i;
00855 
00856     dbg_err_if (hmap == NULL);
00857     dbg_err_if (f == NULL);
00858 
00859     for (i = 0; i < hmap->size; ++i) 
00860     { 
00861         LIST_FOREACH(obj, &hmap->hmap[i], next)
00862             dbg_err_if (f(obj->val));
00863     }
00864 
00865     return U_HMAP_ERR_NONE;
00866 
00867 err:
00868     return U_HMAP_ERR_FAIL;
00869 }
00870 
00882 int u_hmap_foreach_keyval(u_hmap_t *hmap, int f(void *key, void *val))
00883 {
00884     struct u_hmap_o_s *obj;
00885     size_t i;
00886 
00887     dbg_err_if (hmap == NULL);
00888     dbg_err_if (f == NULL);
00889 
00890     for (i = 0; i < hmap->size; ++i) 
00891     {
00892         LIST_FOREACH(obj, &hmap->hmap[i], next)
00893                 dbg_err_if (f(obj->key,obj->val));
00894     }
00895 
00896     return U_HMAP_ERR_NONE;
00897 err:
00898     return U_HMAP_ERR_FAIL;
00899 }
00900 
00901 
00911 void u_hmap_free (u_hmap_t *hmap)
00912 {
00913     u_hmap_o_t *obj;
00914     u_hmap_q_t *data;
00915     size_t i;
00916 
00917     dbg_ifb (hmap == NULL) return;
00918 
00919     /* free the hashhmap */
00920     for (i = 0; i < hmap->size; ++i) 
00921     {
00922         while ((obj = LIST_FIRST(&hmap->hmap[i])) != NULL) 
00923         {
00924             LIST_REMOVE(obj, next);
00925             _o_free(hmap, obj);
00926         }
00927     }
00928 
00929     u_free(hmap->hmap);
00930 
00931     /* free the policy queue */
00932     while ((data = TAILQ_FIRST(&hmap->pcy.queue)) != NULL) 
00933     {
00934         TAILQ_REMOVE(&hmap->pcy.queue, data, next);
00935         _q_o_free(data);
00936     }
00937 
00938     u_free(hmap->opts);
00939     u_free(hmap);
00940 
00941     return;
00942 }
00943 
00955 int u_hmap_opts_new (u_hmap_opts_t **opts)
00956 {
00957     u_hmap_opts_t *o;
00958 
00959     dbg_err_if (opts == NULL);
00960 
00961     dbg_err_sif ((o = (u_hmap_opts_t *) u_zalloc(sizeof(u_hmap_opts_t))) 
00962             == NULL);
00963     
00964     u_hmap_opts_init(o);
00965 
00966     *opts = o;
00967     
00968     return U_HMAP_ERR_NONE;
00969 err:
00970     *opts = NULL;
00971     return U_HMAP_ERR_FAIL;
00972 }
00973 
00984 int u_hmap_opts_copy (u_hmap_opts_t *to, u_hmap_opts_t *from)
00985 {
00986     dbg_err_if (to == NULL);
00987     dbg_err_if (from == NULL);
00988 
00989     memcpy(to, from, sizeof(u_hmap_opts_t));
00990 
00991     return U_HMAP_ERR_NONE;
00992         
00993 err:
00994     return U_HMAP_ERR_FAIL;
00995 }
00996 
01004 void u_hmap_opts_init (u_hmap_opts_t *opts)
01005 {
01006     dbg_ifb (opts == NULL) return;
01007 
01008     opts->size = U_HMAP_MAX_SIZE;
01009     opts->type = U_HMAP_TYPE_CHAIN;
01010     opts->max = U_HMAP_MAX_ELEMS;
01011     opts->policy = U_HMAP_PCY_NONE;
01012     opts->options = 0;
01013     opts->f_hash = &_f_hash;
01014     opts->f_comp = &_f_comp;
01015     opts->f_free = &_f_free;
01016     opts->f_str = &_f_str;
01017     
01018     return;
01019 }
01020 
01028 void u_hmap_opts_free (u_hmap_opts_t *opts)
01029 {
01030     dbg_ifb (opts == NULL) return;
01031 
01032     u_free(opts);
01033 }
01034 
01042 void u_hmap_opts_dbg (u_hmap_opts_t *opts)
01043 {
01044     dbg_ifb (opts == NULL) return;
01045 
01046     dbg("[hmap options]");
01047     dbg("size: %u", opts->size);
01048     dbg("max: %u", opts->max);
01049     dbg("policy: %s", _pcy2str(opts->policy));
01050     dbg("ownsdata: %d, &f_free: %x", 
01051             (opts->options & U_HMAP_OPTS_OWNSDATA)>0,
01052             &opts->f_free);
01053     dbg("no_overwrite: %d", (opts->options & U_HMAP_OPTS_NO_OVERWRITE)>0);
01054 }
01055 
01068 u_hmap_o_t *u_hmap_o_new (void *key, void *val)
01069 {
01070     u_hmap_o_t *obj = NULL;
01071 
01072     dbg_return_if (key == NULL, NULL);
01073     dbg_return_if (val == NULL, NULL);
01074 
01075     dbg_err_sif ((obj = (u_hmap_o_t *) 
01076                 u_zalloc(sizeof(u_hmap_o_t))) == NULL);
01077     
01078     obj->key = key;
01079     obj->val = val;
01080     obj->pqe = NULL;
01081 
01082     return obj;
01083  
01084 err:
01085     u_free(obj);
01086     return NULL;
01087 }
01088 
01100 void u_hmap_o_free (u_hmap_o_t *obj)
01101 {
01102     dbg_ifb (obj == NULL) return;
01103     
01104     u_free(obj);
01105 }
01106 
01107 /* Free a data object including content if U_HMAP_OPTS_OWNSDATA */
01108 static void _o_free (u_hmap_t *hmap, u_hmap_o_t *obj)
01109 {
01110     dbg_ifb (hmap == NULL) return;
01111     dbg_ifb (obj == NULL) return;
01112 
01113     if (hmap->opts->options & U_HMAP_OPTS_OWNSDATA) 
01114     {
01115         if (hmap->opts->f_free)
01116             hmap->opts->f_free(obj);
01117 
01118         u_hmap_o_free(obj); 
01119     }
01120 }
01121 
01122 /* Allocate a new queue data object */
01123 static u_hmap_q_t *_q_o_new (void *key)
01124 {
01125     u_hmap_q_t *data = NULL;
01126 
01127     dbg_return_if (key == NULL, NULL);
01128 
01129     dbg_err_sif ((data = (u_hmap_q_t *)
01130                 u_zalloc(sizeof(u_hmap_q_t))) == NULL);
01131 
01132     data->key = key;
01133     data->o = NULL;
01134     
01135     return data;
01136 
01137 err:
01138     u_free(data);
01139     return NULL;
01140 }
01141 
01142 /* Free a data queue object */
01143 static void _q_o_free (u_hmap_q_t *data)
01144 {
01145     dbg_ifb (data == NULL) return;
01146 
01147     u_free(data->o);
01148     u_free(data);
01149 }
01150 
01151 /* Get a string representation of a policy */
01152 static const char *_pcy2str (u_hmap_pcy_type_t policy)
01153 {
01154     switch (policy)
01155     {
01156         case U_HMAP_PCY_NONE:
01157             return "none";
01158         case U_HMAP_PCY_FIFO:
01159             return "fifo";
01160         case U_HMAP_PCY_LRU:
01161             return "fifo";
01162         case U_HMAP_PCY_LFU:
01163             return "lfu";
01164     }
01165     return NULL;
01166 }
01167 
01168 static int _next_prime(size_t *prime, size_t sz, size_t *idx) 
01169 {
01170     static size_t primes[] = {
01171         13, 19, 29, 41, 59, 79, 107, 149, 197, 263, 347, 457, 599, 787, 1031,
01172         1361, 1777, 2333, 3037, 3967, 5167, 6719, 8737, 11369, 14783, 19219,
01173         24989, 32491, 42257, 54941, 71429, 92861, 120721, 156941, 204047,
01174         265271, 344857, 448321, 582821, 757693, 985003, 1280519, 1664681,
01175         2164111, 2813353, 3657361, 4754591, 6180989, 8035301, 10445899,
01176         13579681, 17653589, 22949669, 29834603, 38784989, 50420551, 65546729,
01177         85210757, 110774011, 144006217, 187208107, 243370577, 316381771,
01178         411296309, 534685237, 695090819, 903618083, 1174703521, 1527114613,
01179         1837299131, 2147483647
01180     };
01181 
01182     size_t i;
01183 
01184     dbg_err_if (prime == NULL);
01185     dbg_err_if (sz == 0);
01186     dbg_err_if (idx == NULL);
01187     
01188     for (i = *idx; i < sizeof(primes)/sizeof(size_t); ++i) {
01189         if (primes[i] >= sz) {
01190             *idx = i;
01191             *prime = primes[i];
01192             goto ok;
01193         }
01194     }
01195     dbg_err_ifm (1, "hmap size limit exceeded"); 
01196 
01197 ok:
01198     return 0;
01199 
01200 err:    
01201     return ~0;    
01202 }
01203 
01204 static int _resize(u_hmap_t *hmap)
01205 {
01206     u_hmap_opts_t *newopts = NULL;
01207     u_hmap_t *newmap = NULL;
01208 
01209     dbg_err_if (hmap == NULL);
01210 
01211     if (hmap->opts->policy != U_HMAP_PCY_NONE)
01212         return 0;
01213 
01214     dbg("resize from: %u", hmap->size);
01215     
01216     /* copy old options */
01217     dbg_err_if (u_hmap_opts_new(&newopts));
01218     dbg_err_if (u_hmap_opts_copy(newopts, hmap->opts));
01219     
01220     /* set rate and create the new hashmap */
01221     dbg_err_if (_next_prime(&newopts->size, 
01222             U_HMAP_RATE_RESIZE * newopts->size, 
01223             &hmap->px));
01224     dbg_err_if (u_hmap_new(newopts, &newmap));
01225     u_hmap_opts_free(newopts);
01226 
01227     /* remove any ownership from old map to copy elements */
01228     hmap->opts->options &= !U_HMAP_OPTS_OWNSDATA;
01229     
01230     /* copy old elements to new map policy */
01231     dbg_err_if (u_hmap_copy(newmap, hmap));
01232 
01233     /* free old allocated objects */
01234     u_hmap_opts_free(hmap->opts);
01235     u_free(hmap->hmap);
01236 
01237     /* copy new map to this hmap */
01238     memcpy(hmap, newmap, sizeof(u_hmap_t));
01239     u_free(newmap);
01240 
01241     dbg("resized to: %u", hmap->size);
01242 
01243     return 0;
01244 
01245 err:
01246     return ~0;
01247 }
01248 

←Products
© 2005-2008 - KoanLogic S.r.l. - All rights reserved