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

srcs/toolbox/hmap.c

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

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