Home | History | Annotate | Download | only in lib
      1 /* Fixed size hash table with internal linking.
      2    Copyright (C) 2000, 2001, 2002, 2004 Red Hat, Inc.
      3    Written by Ulrich Drepper <drepper (at) redhat.com>, 2000.
      4 
      5    This program is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation, version 2.
      8 
      9    This program is distributed in the hope that it will be useful,
     10    but WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12    GNU General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License
     15    along with this program; if not, write to the Free Software Foundation,
     16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
     17 
     18 #include <errno.h>
     19 #include <stdlib.h>
     20 #include <string.h>
     21 #include <sys/cdefs.h>
     22 #include <sys/param.h>
     23 
     24 #include <system.h>
     25 
     26 #define CONCAT(t1,t2) __CONCAT (t1,t2)
     27 
     28 /* Before including this file the following macros must be defined:
     29 
     30    TYPE           data type of the hash table entries
     31    HASHFCT        name of the hashing function to use
     32    HASHTYPE       type used for the hashing value
     33    COMPARE        comparison function taking two pointers to TYPE objects
     34    CLASS          can be defined to `static' to avoid exporting the functions
     35    PREFIX         prefix to be used for function and data type names
     36    STORE_POINTER  if defined the table stores a pointer and not an element
     37                   of type TYPE
     38    INSERT_HASH    if defined alternate insert function which takes a hash
     39                   value is defined
     40    NO_FINI_FCT    if defined the fini function is not defined
     41 */
     42 
     43 
     44 /* Defined separately.  */
     45 extern size_t next_prime (size_t seed);
     46 
     47 
     48 /* Set default values.  */
     49 #ifndef HASHTYPE
     50 # define HASHTYPE size_t
     51 #endif
     52 
     53 #ifndef CLASS
     54 # define CLASS
     55 #endif
     56 
     57 #ifndef PREFIX
     58 # define PREFIX
     59 #endif
     60 
     61 
     62 /* The data structure.  */
     63 struct CONCAT(PREFIX,fshash)
     64 {
     65   size_t nslots;
     66   struct CONCAT(PREFIX,fshashent)
     67   {
     68     HASHTYPE hval;
     69 #ifdef STORE_POINTER
     70 # define ENTRYP(el) (el).entry
     71     TYPE *entry;
     72 #else
     73 # define ENTRYP(el) &(el).entry
     74     TYPE entry;
     75 #endif
     76   } table[0];
     77 };
     78 
     79 
     80 /* Constructor for the hashing table.  */
     81 CLASS struct CONCAT(PREFIX,fshash) *
     82 CONCAT(PREFIX,fshash_init) (size_t nelems)
     83 {
     84   struct CONCAT(PREFIX,fshash) *result;
     85   /* We choose a size for the hashing table 150% over the number of
     86      entries.  This will guarantee short medium search lengths.  */
     87   const size_t max_size_t = ~((size_t) 0);
     88 
     89   if (nelems >= (max_size_t / 3) * 2)
     90     {
     91       errno = EINVAL;
     92       return NULL;
     93     }
     94 
     95   /* Adjust the size to be used for the hashing table.  */
     96   nelems = next_prime (MAX ((nelems * 3) / 2, 10));
     97 
     98   /* Allocate the data structure for the result.  */
     99   result = (struct CONCAT(PREFIX,fshash) *)
    100     xcalloc (sizeof (struct CONCAT(PREFIX,fshash))
    101 	     + (nelems + 1) * sizeof (struct CONCAT(PREFIX,fshashent)), 1);
    102   if (result == NULL)
    103     return NULL;
    104 
    105   result->nslots = nelems;
    106 
    107   return result;
    108 }
    109 
    110 
    111 #ifndef NO_FINI_FCT
    112 CLASS void
    113 CONCAT(PREFIX,fshash_fini) (struct CONCAT(PREFIX,fshash) *htab)
    114 {
    115   free (htab);
    116 }
    117 #endif
    118 
    119 
    120 static struct CONCAT(PREFIX,fshashent) *
    121 CONCAT(PREFIX,fshash_lookup) (struct CONCAT(PREFIX,fshash) *htab,
    122 			      HASHTYPE hval, TYPE *data)
    123 {
    124   size_t idx = 1 + hval % htab->nslots;
    125 
    126   if (htab->table[idx].hval != 0)
    127     {
    128       HASHTYPE hash;
    129 
    130       /* See whether this is the same entry.  */
    131       if (htab->table[idx].hval == hval
    132 	  && COMPARE (data, ENTRYP (htab->table[idx])) == 0)
    133 	return &htab->table[idx];
    134 
    135       /* Second hash function as suggested in [Knuth].  */
    136       hash = 1 + hval % (htab->nslots - 2);
    137 
    138       do
    139 	{
    140 	  if (idx <= hash)
    141 	    idx = htab->nslots + idx - hash;
    142 	  else
    143 	    idx -= hash;
    144 
    145 	  if (htab->table[idx].hval == hval
    146 	      && COMPARE (data, ENTRYP(htab->table[idx])) == 0)
    147 	    return &htab->table[idx];
    148 	}
    149       while (htab->table[idx].hval != 0);
    150     }
    151 
    152   return &htab->table[idx];
    153 }
    154 
    155 
    156 CLASS int
    157 __attribute__ ((unused))
    158 CONCAT(PREFIX,fshash_insert) (struct CONCAT(PREFIX,fshash) *htab,
    159 			      const char *str, size_t len, TYPE *data)
    160 {
    161   HASHTYPE hval = HASHFCT (str, len ?: strlen (str));
    162   struct CONCAT(PREFIX,fshashent) *slot;
    163 
    164   slot = CONCAT(PREFIX,fshash_lookup) (htab, hval, data);
    165  if (slot->hval != 0)
    166     /* We don't want to overwrite the old value.  */
    167     return -1;
    168 
    169   slot->hval = hval;
    170 #ifdef STORE_POINTER
    171   slot->entry = data;
    172 #else
    173   slot->entry = *data;
    174 #endif
    175 
    176   return 0;
    177 }
    178 
    179 
    180 #ifdef INSERT_HASH
    181 CLASS int
    182 __attribute__ ((unused))
    183 CONCAT(PREFIX,fshash_insert_hash) (struct CONCAT(PREFIX,fshash) *htab,
    184 				   HASHTYPE hval, TYPE *data)
    185 {
    186   struct CONCAT(PREFIX,fshashent) *slot;
    187 
    188   slot = CONCAT(PREFIX,fshash_lookup) (htab, hval, data);
    189   if (slot->hval != 0)
    190     /* We don't want to overwrite the old value.  */
    191     return -1;
    192 
    193   slot->hval = hval;
    194 #ifdef STORE_POINTER
    195   slot->entry = data;
    196 #else
    197   slot->entry = *data;
    198 #endif
    199 
    200   return 0;
    201 }
    202 #endif
    203 
    204 
    205 CLASS int
    206 __attribute__ ((unused))
    207 CONCAT(PREFIX,fshash_overwrite) (struct CONCAT(PREFIX,fshash) *htab,
    208 				 const char *str, size_t len, TYPE *data)
    209 {
    210   HASHTYPE hval = HASHFCT (str, len ?: strlen (str));
    211   struct CONCAT(PREFIX,fshashent) *slot;
    212 
    213   slot = CONCAT(PREFIX,fshash_lookup) (htab, hval, data);
    214   slot->hval = hval;
    215 #ifdef STORE_POINTER
    216   slot->entry = data;
    217 #else
    218   slot->entry = *data;
    219 #endif
    220 
    221   return 0;
    222 }
    223 
    224 
    225 const CLASS TYPE *
    226 CONCAT(PREFIX,fshash_find) (const struct CONCAT(PREFIX,fshash) *htab,
    227 			    const char *str, size_t len, TYPE *data)
    228 {
    229   HASHTYPE hval = HASHFCT (str, len ?: strlen (str));
    230   struct CONCAT(PREFIX,fshashent) *slot;
    231 
    232   slot = CONCAT(PREFIX,fshash_lookup) ((struct CONCAT(PREFIX,fshash) *) htab,
    233 				       hval, data);
    234   if (slot->hval == 0)
    235     /* Not found.  */
    236     return NULL;
    237 
    238   return ENTRYP(*slot);
    239 }
    240 
    241 
    242 /* Unset the macros we expect.  */
    243 #undef TYPE
    244 #undef HASHFCT
    245 #undef HASHTYPE
    246 #undef COMPARE
    247 #undef CLASS
    248 #undef PREFIX
    249 #undef INSERT_HASH
    250 #undef STORE_POINTER
    251 #undef NO_FINI_FCT
    252