LCOV - code coverage report
Current view: top level - dhash - dhash.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 307 368 83.4 %
Date: 2014-04-01 Functions: 25 27 92.6 %

          Line data    Source code
       1             : /*
       2             :     Authors:
       3             :         John Dennis <jdennis.redhat.com>
       4             :         Esmond Pitt <ejp@ausmelb.oz.AU>
       5             : 
       6             :     This implementation was based on a 3/7/1989 public domain submission
       7             :     to comp.sources.misc by Esmond Pitt <ejp@ausmelb.oz.AU>.
       8             : 
       9             :     Copyright (C) 2009 Red Hat
      10             : 
      11             :     This program is free software; you can redistribute it and/or modify
      12             :     it under the terms of the GNU Lesser General Public License as published by
      13             :     the Free Software Foundation; either version 3 of the License, or
      14             :     (at your option) any later version.
      15             : 
      16             :     This program is distributed in the hope that it will be useful,
      17             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      18             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19             :     GNU Lesser General Public License for more details.
      20             : 
      21             :     You should have received a copy of the GNU Lesser General Public License
      22             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      23             : */
      24             : 
      25             : /*****************************************************************************/
      26             : /******************************** Documentation ******************************/
      27             : /*****************************************************************************/
      28             : 
      29             : /*
      30             :  * See documentation in corresponding header file dhash.h.
      31             :  *
      32             :  * Compilation controls:
      33             :  * DEBUG controls some informative traces, mainly for debugging.
      34             :  * HASH_STATISTICS causes hash_accesses and hash_collisions to be maintained;
      35             :  * when combined with DEBUG, these are displayed by hash_destroy().
      36             :  *
      37             :  */
      38             : 
      39             : /*****************************************************************************/
      40             : /******************************* Include Files *******************************/
      41             : /*****************************************************************************/
      42             : 
      43             : #include "config.h"
      44             : #include <stdio.h>
      45             : #include <string.h>
      46             : #include <stdlib.h>
      47             : #include <errno.h>
      48             : #include "dhash.h"
      49             : 
      50             : /*****************************************************************************/
      51             : /****************************** Internal Defines *****************************/
      52             : /*****************************************************************************/
      53             : 
      54             : #define PRIME_1                 37
      55             : #define PRIME_2                 1048583
      56             : 
      57             : #ifndef MIN
      58             :     #define MIN(a,b) (((a) < (b)) ? (a) : (b))
      59             : #endif
      60             : 
      61             : #ifndef MAX
      62             :     #define MAX(a,b) (((a) > (b)) ? (a) : (b))
      63             : #endif
      64             : 
      65             : #define halloc(table, size) table->halloc(size, table->halloc_pvt)
      66             : #define hfree(table, ptr) table->hfree(ptr, table->halloc_pvt)
      67             : #define hdelete_callback(table, type, entry) do { \
      68             :     if (table->delete_callback) { \
      69             :         table->delete_callback(entry, type, table->delete_pvt); \
      70             :     } \
      71             : } while(0)
      72             : 
      73             : /*****************************************************************************/
      74             : /************************** Internal Type Definitions ************************/
      75             : /*****************************************************************************/
      76             : 
      77             : typedef struct element_t {
      78             :     hash_entry_t entry;
      79             :     struct element_t *next;
      80             : } element_t, *segment_t;
      81             : 
      82             : 
      83             : struct hash_table_str {
      84             :     unsigned long   p;             /* Next bucket to be split */
      85             :     unsigned long   maxp;          /* upper bound on p during expansion */
      86             :     unsigned long   entry_count;   /* current # entries */
      87             :     unsigned long   bucket_count;  /* current # buckets */
      88             :     unsigned long   segment_count; /* current # segments */
      89             :     unsigned long   min_load_factor;
      90             :     unsigned long   max_load_factor;
      91             :     unsigned long   directory_size;
      92             :     unsigned int    directory_size_shift;
      93             :     unsigned long   segment_size;
      94             :     unsigned int    segment_size_shift;
      95             :     hash_delete_callback *delete_callback;
      96             :     void *delete_pvt;
      97             :     hash_alloc_func *halloc;
      98             :     hash_free_func *hfree;
      99             :     void *halloc_pvt;
     100             :     segment_t **directory;
     101             : #ifdef HASH_STATISTICS
     102             :     hash_statistics_t statistics;
     103             : #endif
     104             : 
     105             : };
     106             : 
     107             : typedef unsigned long address_t;
     108             : 
     109             : typedef struct hash_keys_callback_data_t {
     110             :     unsigned long index;
     111             :     hash_key_t *keys;
     112             : } hash_keys_callback_data_t;
     113             : 
     114             : typedef struct hash_values_callback_data_t {
     115             :     unsigned long index;
     116             :     hash_value_t *values;
     117             : } hash_values_callback_data_t;
     118             : 
     119             : struct _hash_iter_context_t {
     120             :     struct hash_iter_context_t iter;
     121             :     hash_table_t *table;
     122             :     unsigned long i, j;
     123             :     segment_t *s;
     124             :     element_t *p;
     125             : };
     126             : 
     127             : enum hash_iter_state {
     128             :     HI_STATE_0,
     129             :     HI_STATE_1,
     130             :     HI_STATE_2,
     131             :     HI_STATE_3A,
     132             :     HI_STATE_3B
     133             : };
     134             : 
     135             : /*****************************************************************************/
     136             : /**********************  External Function Declarations  *********************/
     137             : /*****************************************************************************/
     138             : 
     139             : /*****************************************************************************/
     140             : /**********************  Internal Function Declarations  *********************/
     141             : /*****************************************************************************/
     142             : 
     143             : static address_t convert_key(hash_key_t *key);
     144             : static address_t hash(hash_table_t *table, hash_key_t *key);
     145             : static bool key_equal(hash_key_t *a, hash_key_t *b);
     146             : static int contract_table(hash_table_t *table);
     147             : static int expand_table(hash_table_t *table);
     148             : static hash_entry_t *hash_iter_next(struct hash_iter_context_t *iter);
     149             : 
     150             : /*****************************************************************************/
     151             : /*************************  External Global Variables  ***********************/
     152             : /*****************************************************************************/
     153             : 
     154             : /*****************************************************************************/
     155             : /*************************  Internal Global Variables  ***********************/
     156             : /*****************************************************************************/
     157             : 
     158             : #ifdef DEBUG
     159             : int debug_level = 1;
     160             : #endif
     161             : 
     162             : /*****************************************************************************/
     163             : /***************************  Internal Functions  ****************************/
     164             : /*****************************************************************************/
     165             : 
     166         762 : static void *sys_malloc_wrapper(size_t size, void *pvt)
     167             : {
     168         762 :     return malloc(size);
     169             : }
     170             : 
     171         756 : static void sys_free_wrapper(void *ptr, void *pvt)
     172             : {
     173         756 :     return free(ptr);
     174             : }
     175             : 
     176             : static address_t convert_key(hash_key_t *key)
     177             : {
     178             :     address_t h;
     179             :     unsigned char *k;
     180             : 
     181      128197 :     switch(key->type) {
     182             :     case HASH_KEY_ULONG:
     183             :         h = key->ul;
     184             :         break;
     185             :     case HASH_KEY_STRING:
     186             :         /* Convert string to integer */
     187      650871 :         for (h = 0, k = (unsigned char *) key->str; *k; k++)
     188      589012 :             h = h * PRIME_1 ^ (*k - ' ');
     189             :         break;
     190             :     default:
     191             :         h = key->ul;
     192             :         break;
     193             :     }
     194             :     return h;
     195             : }
     196             : 
     197      256394 : static address_t hash(hash_table_t *table, hash_key_t *key)
     198             : {
     199             :     address_t h, address;
     200             : 
     201      384591 :     h = convert_key(key);
     202      128197 :     h %= PRIME_2;
     203      128197 :     address = h & (table->maxp-1);            /* h % maxp */
     204      128197 :     if (address < table->p)
     205       40358 :         address = h & ((table->maxp << 1)-1); /* h % (2*table->maxp) */
     206             : 
     207      128197 :     return address;
     208             : }
     209             : 
     210             : static bool is_valid_key_type(hash_key_enum key_type)
     211             : {
     212      127758 :     switch(key_type) {
     213             :     case HASH_KEY_ULONG:
     214             :     case HASH_KEY_STRING:
     215             :         return true;
     216             :     default:
     217             :         return false;
     218             :     }
     219             : }
     220             : 
     221             : static bool is_valid_value_type(hash_value_enum value_type)
     222             : {
     223         503 :     switch(value_type) {
     224             :     case HASH_VALUE_UNDEF:
     225             :     case HASH_VALUE_PTR:
     226             :     case HASH_VALUE_INT:
     227             :     case HASH_VALUE_UINT:
     228             :     case HASH_VALUE_LONG:
     229             :     case HASH_VALUE_ULONG:
     230             :     case HASH_VALUE_FLOAT:
     231             :     case HASH_VALUE_DOUBLE:
     232             :         return true;
     233             :     default:
     234             :         return false;
     235             :     }
     236             : }
     237             : 
     238      404652 : static bool key_equal(hash_key_t *a, hash_key_t *b)
     239             : {
     240      404652 :     if (a->type != b->type) return false;
     241             : 
     242      260065 :     switch(a->type) {
     243             :     case HASH_KEY_ULONG:
     244      135317 :         return (a->ul == b->ul);
     245             :     case HASH_KEY_STRING:
     246      124748 :         return (strcmp(a->str, b->str) == 0);
     247             :     }
     248             :     return false;
     249             : }
     250             : 
     251             : 
     252          52 : static int expand_table(hash_table_t *table)
     253             : {
     254             :     address_t  new_address;
     255             :     unsigned long old_segment_index, new_segment_index;
     256             :     unsigned long old_segment_dir, new_segment_dir;
     257             :     segment_t *old_segment, *new_segment;
     258             :     element_t *current, **previous, **last_of_new;
     259             : 
     260          52 :     if (table->bucket_count < (table->directory_size << table->segment_size_shift)) {
     261             : #ifdef DEBUG
     262             :         if (debug_level >= 2)
     263             :             fprintf(stderr, "expand_table on entry: bucket_count=%lu, segment_count=%lu p=%lu maxp=%lu\n",
     264             :                     table->bucket_count, table->segment_count, table->p, table->maxp);
     265             : #endif
     266             : #ifdef HASH_STATISTICS
     267          52 :         table->statistics.table_expansions++;
     268             : #endif
     269             : 
     270             :         /*
     271             :          * Locate the bucket to be split
     272             :          */
     273          52 :         old_segment_dir = table->p >> table->segment_size_shift;
     274          52 :         old_segment = table->directory[old_segment_dir];
     275          52 :         old_segment_index = table->p & (table->segment_size-1); /* p % segment_size */
     276             :         /*
     277             :          * Expand address space; if necessary create a new segment
     278             :          */
     279          52 :         new_address = table->maxp + table->p;
     280          52 :         new_segment_dir = new_address >> table->segment_size_shift;
     281          52 :         new_segment_index = new_address & (table->segment_size-1); /* new_address % segment_size */
     282          52 :         if (new_segment_index == 0) {
     283           2 :             table->directory[new_segment_dir] = (segment_t *)halloc(table, table->segment_size * sizeof(segment_t));
     284           2 :             if (table->directory[new_segment_dir] == NULL) {
     285             :                 return HASH_ERROR_NO_MEMORY;
     286             :             }
     287           2 :             memset(table->directory[new_segment_dir], 0, table->segment_size * sizeof(segment_t));
     288           2 :             table->segment_count++;
     289             :         }
     290          52 :         new_segment = table->directory[new_segment_dir];
     291             :         /*
     292             :          * Adjust state variables
     293             :          */
     294          52 :         table->p++;
     295          52 :         if (table->p == table->maxp) {
     296           1 :             table->maxp <<= 1;  /* table->maxp *= 2 */
     297           1 :             table->p = 0;
     298             :         }
     299          52 :         table->bucket_count++;
     300             :         /*
     301             :          * Relocate records to the new bucket
     302             :          */
     303          52 :         previous = &old_segment[old_segment_index];
     304          52 :         current = *previous;
     305          52 :         last_of_new = &new_segment[new_segment_index];
     306          52 :         *last_of_new = NULL;
     307         518 :         while (current != NULL) {
     308         414 :             if (hash(table, &current->entry.key) == new_address) {
     309             :                 /*
     310             :                  * Attach it to the end of the new chain
     311             :                  */
     312         192 :                 *last_of_new = current;
     313             :                 /*
     314             :                  * Remove it from old chain
     315             :                  */
     316         192 :                 *previous = current->next;
     317         192 :                 last_of_new = &current->next;
     318         192 :                 current = current->next;
     319         192 :                 *last_of_new = NULL;
     320             :             } else {
     321             :                 /*
     322             :                  * leave it on the old chain
     323             :                  */
     324         222 :                 previous = &current->next;
     325         222 :                 current = current->next;
     326             :             }
     327             :         }
     328             : #ifdef DEBUG
     329             :         if (debug_level >= 2)
     330             :             fprintf(stderr, "expand_table on exit: bucket_count=%lu, segment_count=%lu p=%lu maxp=%lu\n",
     331             :                     table->bucket_count, table->segment_count, table->p, table->maxp);
     332             : #endif
     333             :     }
     334             :     return HASH_SUCCESS;
     335             : }
     336             : 
     337          85 : static int contract_table(hash_table_t *table)
     338             : {
     339             :     address_t  new_address, old_address;
     340             :     unsigned long old_segment_index, new_segment_index;
     341             :     unsigned long old_segment_dir, new_segment_dir;
     342             :     segment_t *old_segment, *new_segment;
     343             :     element_t *current;
     344             : 
     345          85 :     if ((table->bucket_count > table->segment_size) && (table->segment_count > 1)) {
     346             : #ifdef DEBUG
     347             :         if (debug_level >= 2)
     348             :             fprintf(stderr, "contract_table on entry: bucket_count=%lu, segment_count=%lu p=%lu maxp=%lu\n",
     349             :                     table->bucket_count, table->segment_count, table->p, table->maxp);
     350             : #endif
     351             : 
     352             : #ifdef HASH_STATISTICS
     353          52 :         table->statistics.table_contractions++;
     354             : #endif
     355             :         /*
     356             :          * Locate the bucket to be merged with the last bucket
     357             :          */
     358          52 :         old_address = table->bucket_count - 1;
     359          52 :         old_segment_dir = old_address >> table->segment_size_shift;
     360          52 :         old_segment = table->directory[old_segment_dir];
     361          52 :         old_segment_index = old_address & (table->segment_size-1); /* old_address % segment_size */
     362             : 
     363             :         /*
     364             :          * Adjust state variables
     365             :          */
     366          52 :         if (table->p > 0) {
     367          51 :             table->p--;
     368             :         } else {
     369           1 :             table->maxp >>= 1;
     370           1 :             table->p = table->maxp - 1;
     371             :         }
     372          52 :         table->bucket_count--;
     373             : 
     374             :         /*
     375             :          * Find the last bucket to merge back
     376             :          */
     377          52 :         if((current = old_segment[old_segment_index]) != NULL) {
     378          25 :             new_address = hash(table, &old_segment[old_segment_index]->entry.key);
     379          25 :             new_segment_dir = new_address >> table->segment_size_shift;
     380          25 :             new_segment_index = new_address & (table->segment_size-1); /* new_address % segment_size */
     381          25 :             new_segment = table->directory[new_segment_dir];
     382             : 
     383             :             /*
     384             :              * Relocate records to the new bucket by splicing the two chains
     385             :              * together by inserting the old chain at the head of the new chain.
     386             :              * First find the end of the old chain, then set its next pointer to
     387             :              * point to the head of the new chain, set the head of the new chain to
     388             :              * point to the old chain, then finally set the head of the old chain to
     389             :              * NULL.
     390             :              */
     391             : 
     392          25 :             while (current->next != NULL) {
     393             :                 current = current->next;
     394             :             }
     395             : 
     396          25 :             current->next = new_segment[new_segment_index];
     397          25 :             new_segment[new_segment_index] = old_segment[old_segment_index];
     398          25 :             old_segment[old_segment_index] = NULL;
     399             :         }
     400             :         /*
     401             :          * If we have removed the last of the chains in this segment then free the
     402             :          * segment since its no longer in use.
     403             :          */
     404          52 :         if (old_segment_index == 0) {
     405           2 :             table->segment_count--;
     406           2 :             hfree(table, table->directory[old_segment_dir]);
     407             :         }
     408             : 
     409             : #ifdef DEBUG
     410             :         if (debug_level >= 2)
     411             :             fprintf(stderr, "contract_table on exit: bucket_count=%lu, segment_count=%lu p=%lu maxp=%lu\n",
     412             :                     table->bucket_count, table->segment_count, table->p, table->maxp);
     413             : #endif
     414             : 
     415             :     }
     416          85 :     return HASH_SUCCESS;
     417             : }
     418             : 
     419      532410 : static int lookup(hash_table_t *table, hash_key_t *key, element_t **element_arg, segment_t **chain_arg)
     420             : {
     421             :     address_t h;
     422             :     segment_t *current_segment;
     423             :     unsigned long segment_index, segment_dir;
     424             :     segment_t *chain, element;
     425             : 
     426      127758 :     *element_arg = NULL;
     427      127758 :     *chain_arg = NULL;
     428             : 
     429      127758 :     if (!table) return HASH_ERROR_BAD_TABLE;
     430             : 
     431             : #ifdef HASH_STATISTICS
     432      127758 :     table->statistics.hash_accesses++;
     433             : #endif
     434      127758 :     h = hash(table, key);
     435      127758 :     segment_dir = h >> table->segment_size_shift;
     436      127758 :     segment_index = h & (table->segment_size-1); /* h % segment_size */
     437             :     /*
     438             :      * valid segment ensured by hash()
     439             :      */
     440      127758 :     current_segment = table->directory[segment_dir];
     441             : 
     442             : #ifdef DEBUG
     443             :     if (debug_level >= 3)
     444             :         fprintf(stderr, "lookup: h=%lu, segment_dir=%lu, segment_index=%lu current_segment=%p\n",
     445             :                 h, segment_dir, segment_index, current_segment);
     446             : #endif
     447             : 
     448      127758 :     if (current_segment == NULL) return EFAULT;
     449      127758 :     chain = &current_segment[segment_index];
     450      127758 :     element = *chain;
     451             :     /*
     452             :      * Follow collision chain
     453             :      */
     454      938564 :     while (element != NULL && !key_equal(&element->entry.key, key)) {
     455      278396 :         chain = &element->next;
     456      278396 :         element = *chain;
     457             : #ifdef HASH_STATISTICS
     458      278396 :         table->statistics.hash_collisions++;
     459             : #endif
     460             :     }
     461      127758 :     *element_arg = element;
     462      127758 :     *chain_arg = chain;
     463             : 
     464      127758 :     return HASH_SUCCESS;
     465             : }
     466             : 
     467         501 : static bool hash_keys_callback(hash_entry_t *item, void *user_data)
     468             : {
     469         501 :     hash_keys_callback_data_t *data = (hash_keys_callback_data_t *)user_data;
     470             : 
     471         501 :     data->keys[data->index++] = item->key;
     472         501 :     return true;
     473             : }
     474             : 
     475         500 : static bool hash_values_callback(hash_entry_t *item, void *user_data)
     476             : {
     477         500 :     hash_values_callback_data_t *data = (hash_values_callback_data_t *)user_data;
     478             : 
     479         500 :     data->values[data->index++] = item->value;
     480         500 :     return true;
     481             : }
     482             : 
     483             : /*****************************************************************************/
     484             : /****************************  Exported Functions  ***************************/
     485             : /*****************************************************************************/
     486             : 
     487           0 : const char* hash_error_string(int error)
     488             : {
     489           0 :     switch(error) {
     490             :     case HASH_SUCCESS:              return "Success";
     491           0 :     case HASH_ERROR_BAD_KEY_TYPE:   return "Bad key type";
     492           0 :     case HASH_ERROR_BAD_VALUE_TYPE: return "Bad value type";
     493           0 :     case HASH_ERROR_NO_MEMORY:      return "No memory";
     494           0 :     case HASH_ERROR_KEY_NOT_FOUND:  return "Key not found";
     495           0 :     case HASH_ERROR_BAD_TABLE:      return "Bad table";
     496             :     }
     497           0 :     return NULL;
     498             : }
     499             : 
     500             : 
     501           1 : int hash_create(unsigned long count, hash_table_t **tbl,
     502             :                 hash_delete_callback *delete_callback,
     503             :                 void *delete_private_data)
     504             : {
     505           1 :     return hash_create_ex(count, tbl, 0, 0, 0, 0,
     506             :                           NULL, NULL, NULL,
     507             :                           delete_callback,
     508             :                           delete_private_data);
     509             : }
     510             : 
     511           2 : int hash_create_ex(unsigned long count, hash_table_t **tbl,
     512             :                    unsigned int directory_bits,
     513             :                    unsigned int segment_bits,
     514             :                    unsigned long min_load_factor,
     515             :                    unsigned long max_load_factor,
     516             :                    hash_alloc_func *alloc_func,
     517             :                    hash_free_func *free_func,
     518             :                    void *alloc_private_data,
     519             :                    hash_delete_callback *delete_callback,
     520             :                    void *delete_private_data) {
     521             :     unsigned long i;
     522             :     unsigned int n_addr_bits, requested_bits;
     523             :     unsigned int requested_directory_bits;
     524             :     unsigned int requested_segment_bits;
     525             :     address_t addr;
     526           2 :     hash_table_t *table = NULL;
     527             : 
     528             :     /* Initialize to NULL in case of an early return due to an error */
     529           2 :     *tbl = NULL;
     530             : 
     531           2 :     if (alloc_func == NULL) alloc_func = sys_malloc_wrapper;
     532           2 :     if (free_func == NULL) free_func = sys_free_wrapper;
     533             : 
     534             :     /* Compute directory and segment parameters */
     535             : 
     536             :     /* compute power of 2 >= count; it's the number of requested buckets */
     537           2 :     if (count > 1) {
     538           4 :         for (requested_bits = 0; (1 << requested_bits) < count; requested_bits++);
     539             :     } else {
     540             :         requested_bits = 1;
     541             :     }
     542             : 
     543             :     /*
     544             :      * If caller didn't specify directory & segment allocation then
     545             :      * compute it from the requested count.
     546             :      */
     547           2 :     if (directory_bits == 0 || segment_bits == 0) {
     548             :         /* Equally divide buckets between the directory and segments */
     549           2 :         requested_directory_bits = MAX(requested_bits >> 1, 1);
     550           2 :         requested_segment_bits = MAX(requested_bits - requested_directory_bits, 1);
     551             : 
     552             :         /* If the requested count wasn't specified pick a default */
     553           2 :         if (count == 0) {
     554           1 :             directory_bits = MAX(requested_directory_bits, HASH_DEFAULT_DIRECTORY_BITS);
     555           1 :             segment_bits = MAX(requested_segment_bits, HASH_DEFAULT_SEGMENT_BITS);
     556             :         } else {
     557             :             directory_bits = requested_directory_bits;
     558             :             segment_bits = requested_segment_bits;
     559             :         }
     560             :     }
     561             : 
     562           2 :     for (addr = ~0, n_addr_bits = 0; addr; addr >>= 1, n_addr_bits++);
     563             : 
     564           2 :     if (directory_bits + segment_bits > n_addr_bits) return EINVAL;
     565             : 
     566           2 :     table = (hash_table_t *)alloc_func(sizeof(hash_table_t),
     567             :                                        alloc_private_data);
     568           2 :     if (table == NULL) {
     569             :         return HASH_ERROR_NO_MEMORY;
     570             :     }
     571           2 :     memset(table, 0, sizeof(hash_table_t));
     572           2 :     table->halloc = alloc_func;
     573           2 :     table->hfree = free_func;
     574           2 :     table->halloc_pvt = alloc_private_data;
     575             : 
     576           2 :     table->directory_size_shift = directory_bits;
     577           2 :     table->directory_size = directory_bits ? 1 << directory_bits : 0;
     578             : 
     579           2 :     table->segment_size_shift = segment_bits;
     580           2 :     table->segment_size = segment_bits ? 1 << segment_bits : 0;
     581             : 
     582             :     /* Allocate directory */
     583           2 :     table->directory = (segment_t **)halloc(table, table->directory_size * sizeof(segment_t *));
     584           2 :     if (table->directory == NULL) {
     585           0 :         hash_destroy(table);
     586           0 :         return HASH_ERROR_NO_MEMORY;
     587             :     }
     588           2 :     memset(table->directory, 0, table->directory_size * sizeof(segment_t *));
     589             : 
     590             :     /*
     591             :      * If one wanted to pre-allocate all the buckets necessary to meet the needs
     592             :      * of the requested count it would be done like this:
     593             :      *
     594             :      * table->segment_count = MIN((count + table->segment_size-1) / table->segment_size,
     595             :      *                            table->directory_size);
     596             :      *
     597             :      * But with dynamic hash tables there is little point to this, the whole idea is to
     598             :      * allow the table to grow/shrink dynamically, therefore we start with just one
     599             :      * segment of buckets, the minimum necessary.
     600             :      */
     601           2 :     table->segment_count = 1;
     602           2 :     table->p = 0;
     603           2 :     table->entry_count = 0;
     604           2 :     table->delete_callback = delete_callback;
     605           2 :     table->delete_pvt = delete_private_data;
     606             : 
     607             :     /*
     608             :      * Allocate initial segments of buckets
     609             :      */
     610           4 :     for (i = 0; i < table->segment_count; i++) {
     611           2 :         table->directory[i] = (segment_t *)halloc(table, table->segment_size * sizeof(segment_t));
     612           2 :         if (table->directory[i] == NULL) {
     613           0 :             hash_destroy(table);
     614           0 :             return HASH_ERROR_NO_MEMORY;
     615             :         }
     616           2 :         memset(table->directory[i], 0, table->segment_size * sizeof(segment_t));
     617             :     }
     618           2 :     table->bucket_count = table->segment_count << table->segment_size_shift;
     619           2 :     table->maxp = table->bucket_count;
     620           2 :     table->min_load_factor = min_load_factor == 0 ? HASH_DEFAULT_MIN_LOAD_FACTOR : min_load_factor;
     621           2 :     table->max_load_factor = max_load_factor == 0 ? HASH_DEFAULT_MAX_LOAD_FACTOR : max_load_factor;
     622             : 
     623             : #ifdef DEBUG
     624             :     if (debug_level >= 1) {
     625             :         fprintf(stderr, "hash_create_ex: count=%lu available buckets=%lu bucket_count=%lu maxp=%lu\n",
     626             :                 count, table->directory_size*table->segment_size, table->bucket_count, table->maxp);
     627             :         fprintf(stderr, "                directory_bits=%u segment_bits=%u requested_bits=%u\n",
     628             :                 directory_bits, segment_bits, requested_bits);
     629             :         fprintf(stderr, "                directory_size=%lu segment_size=%lu segment_count=%lu\n",
     630             :                 table->directory_size, table->segment_size, table->segment_count);
     631             :         fprintf(stderr, "                min_load_factor=%lu max_load_factor=%lu\n",
     632             :                 table->min_load_factor, table->max_load_factor);
     633             :     }
     634             : #endif
     635             : #ifdef HASH_STATISTICS
     636           2 :     memset(&table->statistics, 0, sizeof(table->statistics));
     637             : #endif
     638             : 
     639           2 :     *tbl = table;
     640           2 :     return HASH_SUCCESS;
     641             : }
     642             : 
     643             : #ifdef HASH_STATISTICS
     644           1 : int hash_get_statistics(hash_table_t *table, hash_statistics_t *statistics)
     645             : {
     646           1 :     if (!table) return HASH_ERROR_BAD_TABLE;
     647           1 :     if (!statistics) return EINVAL;
     648             : 
     649           1 :     *statistics = table->statistics;
     650             : 
     651           1 :     return HASH_SUCCESS;
     652             : }
     653             : #endif
     654             : 
     655           2 : int hash_destroy(hash_table_t *table)
     656             : {
     657             :     unsigned long i, j;
     658             :     segment_t *s;
     659             :     element_t *p, *q;
     660             : 
     661           2 :     if (!table) return HASH_ERROR_BAD_TABLE;
     662             : 
     663           2 :     if (table != NULL) {
     664           2 :         if (table->directory) {
     665           2 :             for (i = 0; i < table->segment_count; i++) {
     666             :                 /* test probably unnecessary */
     667           2 :                 if ((s = table->directory[i]) != NULL) {
     668          36 :                     for (j = 0; j < table->segment_size; j++) {
     669          36 :                         p = s[j];
     670          72 :                         while (p != NULL) {
     671           0 :                             q = p->next;
     672           0 :                             hdelete_callback(table, HASH_TABLE_DESTROY, &p->entry);
     673           0 :                             if (p->entry.key.type == HASH_KEY_STRING) {
     674           0 :                                 hfree(table, (char *)p->entry.key.str);
     675             :                             }
     676           0 :                             hfree(table, (char *)p);
     677           0 :                             p = q;
     678             :                         }
     679             :                     }
     680           2 :                     hfree(table, s);
     681             :                 }
     682             :             }
     683           2 :             hfree(table, table->directory);
     684             :         }
     685           2 :         hfree(table, table);
     686           2 :         table = NULL;
     687             :     }
     688             :     return HASH_SUCCESS;
     689             : }
     690             : 
     691           6 : int hash_iterate(hash_table_t *table, hash_iterate_callback callback, void *user_data)
     692             : {
     693             :     unsigned long i, j;
     694             :     segment_t *s;
     695             :     element_t *p;
     696             : 
     697           6 :     if (!table) return HASH_ERROR_BAD_TABLE;
     698             : 
     699           6 :     if (table != NULL) {
     700          14 :         for (i = 0; i < table->segment_count; i++) {
     701             :             /* test probably unnecessary */
     702          14 :             if ((s = table->directory[i]) != NULL) {
     703         392 :                 for (j = 0; j < table->segment_size; j++) {
     704         392 :                     p = s[j];
     705        2786 :                     while (p != NULL) {
     706        2002 :                         if(!(*callback)(&p->entry, user_data)) return HASH_SUCCESS;
     707        2002 :                         p = p->next;
     708             :                     }
     709             :                 }
     710             :             }
     711             :         }
     712             :     }
     713             :     return HASH_SUCCESS;
     714             : }
     715             : 
     716         503 : static hash_entry_t *hash_iter_next(struct hash_iter_context_t *iter_arg)
     717             : {
     718         503 :     struct _hash_iter_context_t *iter = (struct _hash_iter_context_t *) iter_arg;
     719         503 :     hash_entry_t *entry = NULL;
     720         503 :     enum hash_iter_state state = HI_STATE_3A;
     721             : 
     722         503 :     if (iter->table == NULL) return NULL;
     723             : 
     724        1208 :     while (state != HI_STATE_0) {
     725             : 
     726         707 :         switch (state) {
     727             :             case HI_STATE_1:
     728           4 :                 iter->i++;
     729           4 :                 if(iter->i >= iter->table->segment_count) return NULL;
     730             :                 /* test probably unnecessary */
     731           2 :                 iter->s = iter->table->directory[iter->i];
     732           2 :                 if (iter->s == NULL) {
     733             :                     state = HI_STATE_1;
     734             :                     break;
     735             :                 }
     736           2 :                 iter->j = 0;
     737           2 :                 state = HI_STATE_2;
     738             : 
     739             :             case HI_STATE_2:
     740         102 :                 if (iter->j >= iter->table->segment_size) {
     741             :                     state = HI_STATE_1;
     742             :                     break;
     743             :                 }
     744          98 :                 iter->p = iter->s[iter->j];
     745          98 :                 state = HI_STATE_3A;
     746             : 
     747             :             case HI_STATE_3A:
     748         601 :                 if (iter->p == NULL) {
     749             :                     state = HI_STATE_3B;
     750             :                     break;
     751             :                 }
     752         501 :                 entry = &iter->p->entry;
     753         501 :                 iter->p = iter->p->next;
     754         501 :                 state = HI_STATE_0;
     755         501 :                 break;
     756             : 
     757             :             case HI_STATE_3B:
     758         100 :                 iter->j++;
     759         100 :                 state = HI_STATE_2;
     760         100 :                 break;
     761             : 
     762             :             default:
     763             :                 /* Should never reach here */
     764           0 :                 fprintf(stderr, "ERROR hash_iter_next reached invalid state\n");
     765           0 :                 return NULL;
     766             :                 break;
     767             :         }
     768             :     }
     769             : 
     770             :     return entry;
     771             : }
     772             : 
     773           2 : struct hash_iter_context_t *new_hash_iter_context(hash_table_t *table)
     774             : {
     775             :     struct _hash_iter_context_t *iter;
     776             : 
     777           2 :     if (!table) return NULL;;
     778             : 
     779           2 :     iter = halloc(table, sizeof(struct _hash_iter_context_t));
     780           2 :     if (iter == NULL) {
     781             :         return NULL;
     782             :     }
     783             : 
     784             : 
     785           2 :     iter->iter.next = (hash_iter_next_t) hash_iter_next;
     786             : 
     787           2 :     iter->table = table;
     788           2 :     iter->i = 0;
     789           2 :     iter->j = 0;
     790           2 :     iter->s = table->directory[iter->i];
     791           2 :     iter->p = iter->s[iter->j];
     792             : 
     793           2 :     return (struct hash_iter_context_t *)iter;
     794             : }
     795             : 
     796           7 : unsigned long hash_count(hash_table_t *table)
     797             : {
     798           7 :     return table->entry_count;
     799             : }
     800             : 
     801             : 
     802           2 : int hash_keys(hash_table_t *table, unsigned long *count_arg, hash_key_t **keys_arg)
     803             : {
     804             :     unsigned long count;
     805             :     hash_key_t *keys;
     806             :     hash_keys_callback_data_t data;
     807             : 
     808           2 :     if (!table) return HASH_ERROR_BAD_TABLE;
     809             : 
     810           2 :     count = table->entry_count;
     811           2 :     if (count == 0) {
     812           0 :         *count_arg = 0;
     813           0 :         *keys_arg = NULL;
     814           0 :         return HASH_SUCCESS;
     815             :     }
     816             : 
     817           2 :     keys = halloc(table, sizeof(hash_key_t) * count);
     818           2 :     if (keys == NULL) {
     819           0 :         *count_arg = -1;
     820           0 :         *keys_arg = NULL;
     821           0 :         return HASH_ERROR_NO_MEMORY;
     822             :     }
     823             : 
     824           2 :     data.index = 0;
     825           2 :     data.keys = keys;
     826             : 
     827           2 :     hash_iterate(table, hash_keys_callback, &data);
     828             : 
     829           2 :     *count_arg = count;
     830           2 :     *keys_arg = keys;
     831           2 :     return HASH_SUCCESS;
     832             : }
     833             : 
     834           1 : int hash_values(hash_table_t *table, unsigned long *count_arg, hash_value_t **values_arg)
     835             : {
     836             :     unsigned long count;
     837             :     hash_value_t *values;
     838             :     hash_values_callback_data_t data;
     839             : 
     840           1 :     if (!table) return HASH_ERROR_BAD_TABLE;
     841             : 
     842           1 :     count = table->entry_count;
     843           1 :     if (count == 0) {
     844           0 :         *count_arg = 0;
     845           0 :         *values_arg = NULL;
     846           0 :         return HASH_SUCCESS;
     847             :     }
     848             : 
     849           1 :     values = halloc(table, sizeof(hash_value_t) * count);
     850           1 :     if (values == NULL) {
     851           0 :         *count_arg = -1;
     852           0 :         *values_arg = NULL;
     853           0 :         return HASH_ERROR_NO_MEMORY;
     854             :     }
     855             : 
     856           1 :     data.index = 0;
     857           1 :     data.values = values;
     858             : 
     859           1 :     hash_iterate(table, hash_values_callback, &data);
     860             : 
     861           1 :     *count_arg = count;
     862           1 :     *values_arg = values;
     863           1 :     return HASH_SUCCESS;
     864             : }
     865             : 
     866             : typedef struct hash_entries_callback_data_t {
     867             :     unsigned long index;
     868             :     hash_entry_t *entries;
     869             : } hash_entries_callback_data_t;
     870             : 
     871         500 : static bool hash_entries_callback(hash_entry_t *item, void *user_data)
     872             : {
     873         500 :     hash_entries_callback_data_t *data = (hash_entries_callback_data_t *)user_data;
     874             : 
     875         500 :     data->entries[data->index++] = *item;
     876         500 :     return true;
     877             : }
     878             : 
     879           1 : int hash_entries(hash_table_t *table, unsigned long *count_arg, hash_entry_t **entries_arg)
     880             : {
     881             :     unsigned long count;
     882             :     hash_entry_t *entries;
     883             :     hash_entries_callback_data_t data;
     884             : 
     885           1 :     if (!table) return HASH_ERROR_BAD_TABLE;
     886             : 
     887           1 :     count = table->entry_count;
     888           1 :     if (count == 0) {
     889           0 :         *count_arg = 0;
     890           0 :         *entries_arg = NULL;
     891           0 :         return HASH_SUCCESS;
     892             :     }
     893             : 
     894           1 :     entries = halloc(table, sizeof(hash_entry_t) * count);
     895           1 :     if (entries == NULL) {
     896           0 :         *count_arg = -1;
     897           0 :         *entries_arg = NULL;
     898           0 :         return HASH_ERROR_NO_MEMORY;
     899             :     }
     900             : 
     901           1 :     data.index = 0;
     902           1 :     data.entries = entries;
     903             : 
     904           1 :     hash_iterate(table, hash_entries_callback, &data);
     905             : 
     906           1 :     *count_arg = count;
     907           1 :     *entries_arg = entries;
     908           1 :     return HASH_SUCCESS;
     909             : }
     910             : 
     911         501 : bool hash_has_key(hash_table_t *table, hash_key_t *key)
     912             : {
     913             :     hash_value_t value;
     914             : 
     915         501 :     if (hash_lookup(table, key, &value) == HASH_SUCCESS)
     916             :         return true;
     917             :     else
     918         501 :         return false;
     919             : }
     920             : 
     921             : 
     922           0 : int hash_get_default(hash_table_t *table, hash_key_t *key, hash_value_t *value, hash_value_t *default_value)
     923             : {
     924             :     int error;
     925             : 
     926           0 :     if (!table) return HASH_ERROR_BAD_TABLE;
     927             : 
     928           0 :     error = hash_lookup(table, key, value);
     929           0 :     if (error == HASH_ERROR_KEY_NOT_FOUND) {
     930             : 
     931           0 :         error = hash_enter(table, key, default_value);
     932           0 :         if (error != HASH_SUCCESS) {
     933             :             return error;
     934             :         }
     935           0 :         *value = *default_value;
     936           0 :         return HASH_SUCCESS;
     937             :     } else {
     938           0 :         if (error != HASH_SUCCESS) return error;
     939             :     }
     940             : 
     941             :     return HASH_SUCCESS;
     942             : }
     943             : 
     944         503 : int hash_enter(hash_table_t *table, hash_key_t *key, hash_value_t *value)
     945             : {
     946             :     int error;
     947             :     segment_t element, *chain;
     948             :     size_t len;
     949             : 
     950         503 :     if (!table) return HASH_ERROR_BAD_TABLE;
     951             : 
     952        1006 :     if (!is_valid_key_type(key->type))
     953             :         return HASH_ERROR_BAD_KEY_TYPE;
     954             : 
     955        1006 :     if (!is_valid_value_type(value->type))
     956             :         return HASH_ERROR_BAD_VALUE_TYPE;
     957             : 
     958         503 :     lookup(table, key, &element, &chain);
     959             : 
     960         503 :     if (element == NULL) {                    /* not found */
     961         501 :         element = (element_t *)halloc(table, sizeof(element_t));
     962         501 :         if (element == NULL) {
     963             :             /* Allocation failed, return NULL */
     964             :             return HASH_ERROR_NO_MEMORY;
     965             :         }
     966         501 :         memset(element, 0, sizeof(element_t));
     967             :         /*
     968             :          * Initialize new element
     969             :          */
     970         501 :         switch(element->entry.key.type = key->type) {
     971             :         case HASH_KEY_ULONG:
     972         254 :             element->entry.key.ul = key->ul;
     973         254 :             break;
     974             :         case HASH_KEY_STRING:
     975         247 :             len = strlen(key->str)+1;
     976         247 :             element->entry.key.str = halloc(table, len);
     977         247 :             if (element->entry.key.str == NULL) {
     978           0 :                 hfree(table, element);
     979           0 :                 return HASH_ERROR_NO_MEMORY;
     980             :             }
     981         247 :             memcpy((void *)element->entry.key.str, key->str, len);
     982         247 :             break;
     983             :         }
     984             : 
     985         501 :         *chain = element;             /* link into chain */
     986         501 :         element->next = NULL;
     987             : 
     988             :         /*
     989             :          * Table over-full?
     990             :          */
     991         501 :         if (++table->entry_count / table->bucket_count > table->max_load_factor) {
     992          52 :             if ((error = expand_table(table)) != HASH_SUCCESS) { /* doesn't affect element */
     993             :                 return error;
     994             :             }
     995             :         }
     996             : 
     997             :     } else {
     998           2 :         hdelete_callback(table, HASH_ENTRY_DESTROY, &element->entry);
     999             :     }
    1000             : 
    1001         503 :     switch(element->entry.value.type = value->type) {
    1002             :     case HASH_VALUE_UNDEF:
    1003           0 :         element->entry.value.ul = 0;
    1004           0 :         break;
    1005             :     case HASH_VALUE_PTR:
    1006         248 :         element->entry.value.ptr = value->ptr;
    1007         248 :         break;
    1008             :     case HASH_VALUE_INT:
    1009           0 :         element->entry.value.i = value->i;
    1010           0 :         break;
    1011             :     case HASH_VALUE_UINT:
    1012           0 :         element->entry.value.ui = value->ui;
    1013           0 :         break;
    1014             :     case HASH_VALUE_LONG:
    1015         255 :         element->entry.value.l = value->l;
    1016         255 :         break;
    1017             :     case HASH_VALUE_ULONG:
    1018           0 :         element->entry.value.ul = value->ul;
    1019           0 :         break;
    1020             :     case HASH_VALUE_FLOAT:
    1021           0 :         element->entry.value.f = value->f;
    1022           0 :         break;
    1023             :     case HASH_VALUE_DOUBLE:
    1024           0 :         element->entry.value.d = value->d;
    1025           0 :         break;
    1026             :     }
    1027             : 
    1028             :     return HASH_SUCCESS;
    1029             : }
    1030             : 
    1031      126754 : int hash_lookup(hash_table_t *table, hash_key_t *key, hash_value_t *value)
    1032             : {
    1033             :     segment_t element, *chain;
    1034             : 
    1035      126754 :     if (!table) return HASH_ERROR_BAD_TABLE;
    1036             : 
    1037      253508 :     if (!is_valid_key_type(key->type))
    1038             :         return HASH_ERROR_BAD_KEY_TYPE;
    1039             : 
    1040      126754 :     lookup(table, key, &element, &chain);
    1041             : 
    1042      126754 :     if (element) {
    1043      125753 :         *value = element->entry.value;
    1044      125753 :         return HASH_SUCCESS;
    1045             :     } else {
    1046             :         return HASH_ERROR_KEY_NOT_FOUND;
    1047             :     }
    1048             : }
    1049             : 
    1050         501 : int hash_delete(hash_table_t *table, hash_key_t *key)
    1051             : {
    1052             :     int error;
    1053             :     segment_t element, *chain;
    1054             : 
    1055         501 :     if (!table) return HASH_ERROR_BAD_TABLE;
    1056             : 
    1057        1002 :     if (!is_valid_key_type(key->type))
    1058             :         return HASH_ERROR_BAD_KEY_TYPE;
    1059             : 
    1060         501 :     lookup(table, key, &element, &chain);
    1061             : 
    1062         501 :     if (element) {
    1063         501 :         hdelete_callback(table, HASH_ENTRY_DESTROY, &element->entry);
    1064         501 :         *chain = element->next; /* remove from chain */
    1065             :         /*
    1066             :          * Table too sparse?
    1067             :          */
    1068         501 :         if (--table->entry_count / table->bucket_count < table->min_load_factor) {
    1069          85 :             if ((error = contract_table(table)) != HASH_SUCCESS) { /* doesn't affect element */
    1070             :                 return error;
    1071             :             }
    1072             :         }
    1073         501 :         if (element->entry.key.type == HASH_KEY_STRING) {
    1074         247 :             hfree(table, (char *)element->entry.key.str);
    1075             :         }
    1076         501 :         hfree(table, element);
    1077         501 :         return HASH_SUCCESS;
    1078             :     } else {
    1079             :         return HASH_ERROR_KEY_NOT_FOUND;
    1080             :     }
    1081             : }
    1082             : 
    1083             : 

Generated by: LCOV version 1.10