Home | History | Annotate | Line # | Download | only in dist
hash.m4c revision 1.1.1.1
      1 /*
      2  * Copyright (c) 1984 through 2008, William LeFebvre
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *
     16  *     * Neither the name of William LeFebvre nor the names of other
     17  * contributors may be used to endorse or promote products derived from
     18  * this software without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /* hash.m4c */
     34 
     35 /* The file hash.c is generated from hash.m4c via the preprocessor M4 */
     36 
     37 /*
     38  * Hash table functions:  init, add, lookup, first, next, sizeinfo.
     39  * This is a conventional "bucket hash".  The key is hashed in to a number
     40  * less than or equal to the number of buckets and the result is used
     41  * to index in to the array of buckets.  Each bucket is a linked list
     42  * that contains all the key/value pairs which hashed to that index.
     43  */
     44 
     45 #include "config.h"
     46 
     47 #if STDC_HEADERS
     48 #include <string.h>
     49 #include <stdlib.h>
     50 #define memzero(a, b)		memset((a), 0, (b))
     51 #else /* !STDC_HEADERS */
     52 #ifdef HAVE_MEMCPY
     53 #define memzero(a, b)		memset((a), 0, (b))
     54 #else
     55 #define memcpy(a, b, c)		bcopy((b), (a), (c))
     56 #define memzero(a, b)		bzero((a), (b))
     57 #define memcmp(a, b, c)		bcmp((a), (b), (c))
     58 #endif /* HAVE_MEMCPY */
     59 #ifdef HAVE_STRINGS_H
     60 #include <strings.h>
     61 #else
     62 #ifdef HAVE_STRING_H
     63 #include <string.h>
     64 #endif
     65 #endif
     66 void *malloc();
     67 void free();
     68 char *strdup();
     69 #endif /* !STDC_HEADERS */
     70 
     71 /* After all that there are still some systems that don't have NULL defined */
     72 #ifndef NULL
     73 #define NULL 0
     74 #endif
     75 
     76 #ifdef HAVE_MATH_H
     77 #include <math.h>
     78 #endif
     79 
     80 #if !HAVE_PID_T
     81 typedef long pid_t;
     82 #endif
     83 #if !HAVE_ID_T
     84 typedef long id_t;
     85 #endif
     86 
     87 #include "hash.h"
     88 
     89 dnl # The m4 code uses MALLOC, FREE, and STRDUP for dynamic allocation.
     90 dnl # You can change what these get mapped to here:
     91 
     92 define(`MALLOC', `malloc($1)')
     93 define(`FREE', `free($1)')
     94 define(`STRDUP', `strdup($1)')
     95 
     96 static int
     97 next_prime(int x)
     98 
     99 {
    100     double i, j;
    101     int f;
    102 
    103     i = x;
    104     while (i++)
    105     {
    106 	f=1;
    107 	for (j=2; j<i; j++)
    108 	{
    109 	    if ((i/j)==floor(i/j))
    110 	    {
    111 		f=0;
    112 		break;
    113 	    }
    114 	}
    115 	if (f)
    116 	{
    117 	    return (int)i;
    118 	}
    119     }
    120     return 1;
    121 }
    122 
    123 /* string hashes */
    124 
    125 static int
    126 string_hash(hash_table *ht, char *key)
    127 
    128 {
    129     unsigned long s = 0;
    130     unsigned char ch;
    131     int shifting = 24;
    132 
    133     while ((ch = (unsigned char)*key++) != '\0')
    134     {
    135 	if (shifting == 0)
    136 	{
    137 	    s = s + ch;
    138 	    shifting = 24;
    139 	}
    140 	else
    141 	{
    142 	    s = s + (ch << shifting);
    143 	    shifting -= 8;
    144 	}
    145     }
    146 
    147     return (s % ht->num_buckets);
    148 }
    149 
    150 void ll_init(llist *q)
    151 
    152 {
    153     q->head = NULL;
    154     q->count = 0;
    155 }
    156 
    157 llistitem *ll_newitem(int size)
    158 
    159 {
    160     llistitem *qi;
    161 
    162     qi = (llistitem *)MALLOC(sizeof(llistitem) + size);
    163     qi->datum = ((void *)qi + sizeof(llistitem));
    164     return qi;
    165 }
    166 
    167 void ll_freeitem(llistitem *li)
    168 
    169 {
    170     FREE(li);
    171 }
    172 
    173 void ll_add(llist *q, llistitem *new)
    174 
    175 {
    176     new->next = q->head;
    177     q->head = new;
    178     q->count++;
    179 }
    180 
    181 void ll_extract(llist *q, llistitem *qi, llistitem *last)
    182 
    183 {
    184     if (last == NULL)
    185     {
    186 	q->head = qi->next;
    187     }
    188     else
    189     {
    190 	last->next = qi->next;
    191     }
    192     qi->next = NULL;
    193     q->count--;
    194 }
    195 
    196 #define LL_FIRST(q) ((q)->head)
    197 llistitem *
    198 ll_first(llist *q)
    199 
    200 {
    201     return q->head;
    202 }
    203 
    204 #define LL_NEXT(q, qi)  ((qi) != NULL ? (qi)->next : NULL)
    205 llistitem *
    206 ll_next(llist *q, llistitem *qi)
    207 
    208 {
    209     return (qi != NULL ? qi->next : NULL);
    210 }
    211 
    212 #define LL_ISEMPTY(ll)  ((ll)->count == 0)
    213 int
    214 ll_isempty(llist *ll)
    215 
    216 {
    217     return (ll->count == 0);
    218 }
    219 
    220 /*
    221  * hash_table *hash_create(int num)
    222  *
    223  * Creates a hash table structure with at least "num" buckets.
    224  */
    225 
    226 hash_table *
    227 hash_create(int num)
    228 
    229 {
    230     hash_table *result;
    231     bucket_t *b;
    232     int bytes;
    233     int i;
    234 
    235     /* create the resultant structure */
    236     result = (hash_table *)MALLOC(sizeof(hash_table));
    237 
    238     /* adjust bucket count to be prime */
    239     num = next_prime(num);
    240 
    241     /* create the buckets */
    242     bytes = sizeof(bucket_t) * num;
    243     result->buckets = b = (bucket_t *)MALLOC(bytes);
    244     result->num_buckets = num;
    245 
    246     /* create each bucket as a linked list */
    247     i = num;
    248     while (--i >= 0)
    249     {
    250 	ll_init(&(b->list));
    251 	b++;
    252     }
    253 
    254     return result;
    255 }
    256 
    257 /*
    258  * unsigned int hash_count(hash_table *ht)
    259  *
    260  * Return total number of elements contained in hash table.
    261  */
    262 
    263 unsigned int
    264 hash_count(hash_table *ht)
    265 
    266 {
    267     register int i = 0;
    268     register int cnt = 0;
    269     register bucket_t *bucket;
    270 
    271     bucket = ht->buckets;
    272     while (i++ < ht->num_buckets)
    273     {
    274 	cnt += bucket->list.count;
    275 	bucket++;
    276     }
    277 
    278     return cnt;
    279 }
    280 
    281 /*
    282  * void hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht)
    283  *
    284  * Fill in "sizes" with information about bucket lengths in hash
    285  * table "max".
    286  */
    287 
    288 void
    289 hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht)
    290 
    291 {
    292     register int i;
    293     register int idx;
    294     register bucket_t *bucket;
    295 
    296     memzero(sizes, max * sizeof(unsigned int));
    297 
    298     bucket = ht->buckets;
    299     i = 0;
    300     while (i++ < ht->num_buckets)
    301     {
    302 	idx = bucket->list.count;
    303 	sizes[idx >= max ? max-1 : idx]++;
    304 	bucket++;
    305     }
    306 }
    307 
    308 dnl # HASH_TABLE_TMPL(suffix, keytype, to_hash, to_cmp, to_alloc, to_free)
    309 dnl #
    310 dnl # This generates hash table functions suitable for keys
    311 dnl # of type "keytype".  The function names all end with "suffix".
    312 dnl # "to_hash" is an expression that generates a hash index (this
    313 dnl # expression can include key and ht).  "to_cmp" is an expression
    314 dnl # that compares "key" to "k1".  "to_alloc" is an expression that
    315 dnl # allocates space for "key", or empty if no allocation is needed.
    316 dnl # "to_free" is an expression that frees "key", or empty if no
    317 dnl # allocation is needed.
    318 
    319 define(`HASH_TABLE_TMPL', `
    320 
    321 /*
    322  * void hash_add_$1(hash_table *ht, $2 key, void *value)
    323  *
    324  * Add an element to table "ht".  The element is described by
    325  * "key" and "value".  Return NULL on success.  If the key
    326  * already exists in the table, then no action is taken and
    327  * the data for the existing element is returned.
    328  * Key type is $2
    329  */
    330 
    331 void *
    332 hash_add_$1(hash_table *ht, $2 key, void *value)
    333 
    334 {
    335     bucket_t *bucket;
    336     hash_item_$1 *hi;
    337     hash_item_$1 *h;
    338     llist *ll;
    339     llistitem *li;
    340     llistitem *newli;
    341     $2 k1;
    342 
    343     /* allocate the space we will need */
    344     newli = ll_newitem(sizeof(hash_item_$1));
    345     hi = (hash_item_$1 *)newli->datum;
    346 
    347     /* fill in the values */
    348     hi->key = ifelse($5, , key, $5);
    349     hi->value = value;
    350 
    351     /* hash to the bucket */
    352     bucket = &(ht->buckets[$3]);
    353 
    354     /* walk the list to make sure we do not have a duplicate */
    355     ll = &(bucket->list);
    356     li = LL_FIRST(ll);
    357     while (li != NULL)
    358     {
    359 	h = (hash_item_$1 *)li->datum;
    360 	k1 = h->key;
    361 	if ($4)
    362 	{
    363 	    /* found one */
    364 	    break;
    365 	}
    366 	li = LL_NEXT(ll, li);
    367     }
    368     li = NULL;
    369 
    370     /* is there already one there? */
    371     if (li == NULL)
    372     {
    373 	/* add the unique element to the buckets list */
    374 	ll_add(&(bucket->list), newli);
    375 	return NULL;
    376     }
    377     else
    378     {
    379 	/* free the stuff we just allocated */
    380 	ll_freeitem(newli);
    381 	return ((hash_item_$1 *)(li->datum))->value;
    382     }
    383 }
    384 
    385 /*
    386  * void *hash_replace_$1(hash_table *ht, $2 key, void *value)
    387  *
    388  * Replace an existing value in the hash table with a new one and
    389  * return the old value.  If the key does not already exist in
    390  * the hash table, add a new element and return NULL.
    391  * Key type is $2
    392  */
    393 
    394 void *
    395 hash_replace_$1(hash_table *ht, $2 key, void *value)
    396 
    397 {
    398     bucket_t *bucket;
    399     hash_item_$1 *hi;
    400     llist *ll;
    401     llistitem *li;
    402     void *result = NULL;
    403     $2 k1;
    404 
    405     /* find the bucket */
    406     bucket = &(ht->buckets[$3]);
    407 
    408     /* walk the list until we find the existing item */
    409     ll = &(bucket->list);
    410     li = LL_FIRST(ll);
    411     while (li != NULL)
    412     {
    413 	hi = (hash_item_$1 *)li->datum;
    414 	k1 = hi->key;
    415 	if ($4)
    416 	{
    417 	    /* found it: now replace the value with the new one */
    418 	    result = hi->value;
    419 	    hi->value = value;
    420 	    break;
    421 	}
    422 	li = LL_NEXT(ll, li);
    423     }
    424 
    425     /* if the element wasnt found, add it */
    426     if (result == NULL)
    427     {
    428 	li = ll_newitem(sizeof(hash_item_$1));
    429 	hi = (hash_item_$1 *)li->datum;
    430 	hi->key = ifelse($5, , key, $5);
    431 	hi->value = value;
    432 	ll_add(&(bucket->list), li);
    433     }
    434 
    435     /* return the old value (so it can be freed) */
    436     return result;
    437 }
    438 
    439 /*
    440  * void *hash_lookup_$1(hash_table *ht, $2 key)
    441  *
    442  * Look up "key" in "ht" and return the associated value.  If "key"
    443  * is not found, return NULL.  Key type is $2
    444  */
    445 
    446 void *
    447 hash_lookup_$1(hash_table *ht, $2 key)
    448 
    449 {
    450     bucket_t *bucket;
    451     llist *ll;
    452     llistitem *li;
    453     hash_item_$1 *h;
    454     void *result;
    455     $2 k1;
    456 
    457     result = NULL;
    458     if ((bucket = &(ht->buckets[$3])) != NULL)
    459     {
    460 	ll = &(bucket->list);
    461 	li = LL_FIRST(ll);
    462 	while (li != NULL)
    463 	{
    464 	    h = (hash_item_$1 *)li->datum;
    465 	    k1 = h->key;
    466 	    if ($4)
    467 	    {
    468 		result = h->value;
    469 		break;
    470 	    }
    471 	    li = LL_NEXT(ll, li);
    472 	}
    473     }
    474     return result;
    475 }
    476 
    477 /*
    478  * void *hash_remove_$1(hash_table *ht, $2 key)
    479  *
    480  * Remove the element associated with "key" from the hash table
    481  * "ht".  Return the value or NULL if the key was not found.
    482  */
    483 
    484 void *
    485 hash_remove_$1(hash_table *ht, $2 key)
    486 
    487 {
    488     bucket_t *bucket;
    489     llist *ll;
    490     llistitem *li;
    491     llistitem *lilast;
    492     hash_item_$1 *hi;
    493     void *result;
    494     $2 k1;
    495 
    496     result = NULL;
    497     if ((bucket = &(ht->buckets[$3])) != NULL)
    498     {
    499 	ll = &(bucket->list);
    500 	li = LL_FIRST(ll);
    501 	lilast = NULL;
    502 	while (li != NULL)
    503 	{
    504 	    hi = (hash_item_$1 *)li->datum;
    505 	    k1 = hi->key;
    506 	    if ($4)
    507 	    {
    508 		ll_extract(ll, li, lilast);
    509 		result = hi->value;
    510 		key = hi->key;
    511 		$6;
    512 		ll_freeitem(li);
    513 		break;
    514 	    }
    515 	    lilast = li;
    516 	    li = LL_NEXT(ll, li);
    517 	}
    518     }
    519     return result;
    520 }
    521 
    522 /*
    523  * hash_item_$1 *hash_first_$1(hash_table *ht, hash_pos *pos)
    524  *
    525  * First function to call when iterating through all items in the hash
    526  * table.  Returns the first item in "ht" and initializes "*pos" to track
    527  * the current position.
    528  */
    529 
    530 hash_item_$1 *
    531 hash_first_$1(hash_table *ht, hash_pos *pos)
    532 
    533 {
    534     /* initialize pos for first item in first bucket */
    535     pos->num_buckets = ht->num_buckets;
    536     pos->hash_bucket = ht->buckets;
    537     pos->curr = 0;
    538     pos->ll_last = NULL;
    539 
    540     /* find the first non-empty bucket */
    541     while(pos->hash_bucket->list.count == 0)
    542     {
    543 	pos->hash_bucket++;
    544 	if (++pos->curr >= pos->num_buckets)
    545 	{
    546 	    return NULL;
    547 	}
    548     }
    549 
    550     /* set and return the first item */
    551     pos->ll_item = LL_FIRST(&(pos->hash_bucket->list));
    552     return (hash_item_$1 *)pos->ll_item->datum;
    553 }
    554 
    555 
    556 /*
    557  * hash_item_$1 *hash_next_$1(hash_pos *pos)
    558  *
    559  * Return the next item in the hash table, using "pos" as a description
    560  * of the present position in the hash table.  "pos" also identifies the
    561  * specific hash table.  Return NULL if there are no more elements.
    562  */
    563 
    564 hash_item_$1 *
    565 hash_next_$1(hash_pos *pos)
    566 
    567 {
    568     llistitem *li;
    569 
    570     /* move item to last and check for NULL */
    571     if ((pos->ll_last = pos->ll_item) == NULL)
    572     {
    573 	/* are we really at the end of the hash table? */
    574 	if (pos->curr >= pos->num_buckets)
    575 	{
    576 	    /* yes: return NULL */
    577 	    return NULL;
    578 	}
    579 	/* no: regrab first item in current bucket list (might be NULL) */
    580 	li = LL_FIRST(&(pos->hash_bucket->list));
    581     }
    582     else
    583     {
    584 	/* get the next item in the llist */
    585 	li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item);
    586     }
    587 
    588     /* if its NULL we have to find another bucket */
    589     while (li == NULL)
    590     {
    591 	/* locate another bucket */
    592 	pos->ll_last = NULL;
    593 
    594 	/* move to the next one */
    595 	pos->hash_bucket++;
    596 	if (++pos->curr >= pos->num_buckets)
    597 	{
    598 	    /* at the end of the hash table */
    599 	    pos->ll_item = NULL;
    600 	    return NULL;
    601 	}
    602 
    603 	/* get the first element (might be NULL) */
    604 	li = LL_FIRST(&(pos->hash_bucket->list));
    605     }
    606 
    607     /* li is the next element to dish out */
    608     pos->ll_item = li;
    609     return (hash_item_$1 *)li->datum;
    610 }
    611 
    612 /*
    613  * void *hash_remove_pos_$1(hash_pos *pos)
    614  *
    615  * Remove the hash table entry pointed to by position marker "pos".
    616  * The data from the entry is returned upon success, otherwise NULL.
    617  */
    618 
    619 
    620 void *
    621 hash_remove_pos_$1(hash_pos *pos)
    622 
    623 {
    624     llistitem *li;
    625     void *ans;
    626     hash_item_$1 *hi;
    627     $2 key;
    628 
    629     /* sanity checks */
    630     if (pos == NULL || pos->ll_last == pos->ll_item)
    631     {
    632 	return NULL;
    633     }
    634 
    635     /* at this point pos contains the item to remove (ll_item)
    636        and its predecesor (ll_last) */
    637     /* extract the item from the llist */
    638     li = pos->ll_item;
    639     ll_extract(&(pos->hash_bucket->list), li, pos->ll_last);
    640 
    641     /* retain the data */
    642     hi = (hash_item_$1 *)li->datum;
    643     ans = hi->value;
    644 
    645     /* free up the space */
    646     key = hi->key;
    647     $6;
    648     ll_freeitem(li);
    649 
    650     /* back up to previous item */
    651     /* its okay for ll_item to be null: hash_next will detect it */
    652     pos->ll_item = pos->ll_last;
    653 
    654     return ans;
    655 }
    656 ')
    657 
    658 dnl # create hash talbe functions for unsigned int and for strings */
    659 
    660 HASH_TABLE_TMPL(`uint', `unsigned int', `(key % ht->num_buckets)', `key == k1', ,)
    661 HASH_TABLE_TMPL(`pid', `pid_t', `(key % ht->num_buckets)', `key == k1', ,)
    662 HASH_TABLE_TMPL(`string', `char *', `string_hash(ht, key)', `strcmp(key, k1) == 0', `STRDUP(key)', `FREE(key)')
    663 HASH_TABLE_TMPL(`pidthr', `pidthr_t', `((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)', `(key.k_pid == k1.k_pid && key.k_thr == k1.k_thr)', ,)
    664 #if HAVE_LWPID_T
    665 HASH_TABLE_TMPL(`lwpid', `lwpid_t', `(key % ht->num_buckets)', `key == k1', ,)
    666 #endif
    667