strhash.c - enscript - GNU Enscript
 (HTM) git clone git://thinkerwim.org/enscript.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       strhash.c (7717B)
       ---
            1 /*
            2  * String hash table.
            3  * Copyright (c) 1995-1999 Markku Rossi.
            4  *
            5  * Author: Markku Rossi <mtr@iki.fi>
            6  */
            7 
            8 /*
            9  * Enscript is free software: you can redistribute it and/or modify
           10  * it under the terms of the GNU General Public License as published by
           11  * the Free Software Foundation, either version 3 of the License, or
           12  * (at your option) any later version.
           13  *
           14  * Enscript is distributed in the hope that it will be useful,
           15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
           16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
           17  * GNU General Public License for more details.
           18  *
           19  * You should have received a copy of the GNU General Public License
           20  * along with Enscript.  If not, see <http://www.gnu.org/licenses/>.
           21  */
           22 
           23 #include <afmint.h>
           24 #include <strhash.h>
           25 
           26 /*
           27  * Types and definitions.
           28  */
           29 
           30 #define STRHASH_DEBUG 0
           31 
           32 #define HASH_SIZE 8192
           33 
           34 struct hash_list_st
           35 {
           36   struct hash_list_st *next;
           37   char *key;                        /* malloc()ated copy of key. */
           38   int keylen;
           39   void *data;
           40 };
           41 
           42 typedef struct hash_list_st HashList;
           43 
           44 typedef HashList *HashTable;
           45 
           46 typedef struct stringhash_st
           47 {
           48   HashTable *hash_table;
           49 
           50   /* Scan support. */
           51   unsigned int next_idx;
           52   HashList *next_item;
           53 
           54 #if STRHASH_DEBUG
           55   int items_in_hash;
           56 #endif /* STRHASH_DEBUG */
           57 } *hash_t;
           58 
           59 
           60 /*
           61  * Prototypes for static functions.
           62  */
           63 
           64 static int count_hash ___P ((const char *key, int keylen));
           65 
           66 
           67 /*
           68  * Global functions.
           69  */
           70 
           71 StringHashPtr
           72 strhash_init ()
           73 {
           74   StringHashPtr tmp;
           75 
           76   tmp = (StringHashPtr) calloc (1, sizeof (*tmp));
           77   if (!tmp)
           78     return NULL;
           79 
           80   tmp->hash_table = (HashTable *) calloc (HASH_SIZE, sizeof (HashTable));
           81   if (!tmp->hash_table)
           82     {
           83       free (tmp);
           84       return NULL;
           85     }
           86 
           87 #if STRHASH_DEBUG
           88   tmp->items_in_hash = 0;
           89 #endif /* STRHASH_DEBUG */
           90   return tmp;
           91 }
           92 
           93 
           94 void
           95 strhash_free (StringHashPtr hash)
           96 {
           97   HashList *list, *list_next;
           98   int i;
           99 
          100   if (!hash)
          101     return;
          102 
          103   /* Free chains. */
          104   for (i = 0; i < HASH_SIZE; i++)
          105     for (list = hash->hash_table[i]; list; list = list_next)
          106       {
          107         list_next = list->next;
          108         free (list->key);
          109         free (list);
          110       }
          111 
          112   /* Free hash. */
          113   free (hash->hash_table);
          114   free (hash);
          115 }
          116 
          117 
          118 int
          119 strhash_put (StringHashPtr hash, char *key, int keylen, void *data,
          120              void **old_data)
          121 {
          122   HashList *list, *prev = NULL;
          123   int pos, cmp_val;
          124 
          125   if (!hash || !key || keylen <= 0)
          126     return 0;
          127 
          128   if (old_data)
          129     *old_data = NULL;
          130   pos = count_hash (key, keylen);
          131 
          132   /* Is it already here? */
          133   for (list = hash->hash_table[pos]; list; prev = list, list = list->next)
          134     if (list->keylen == keylen)
          135       {
          136         cmp_val = memcmp (key, list->key, keylen);
          137         if (cmp_val == 0)
          138           {
          139             /* We had an old occurence. */
          140             if (old_data)
          141               *old_data = list->data;
          142             list->data = data;
          143             return 1;
          144           }
          145         else if (cmp_val < 0)
          146           {
          147             /* Run over. Correct position is prev->next. */
          148             break;
          149           }
          150       }
          151     else if (list->keylen > keylen)
          152       /* Lists are kept sorted so that smallest keys are at the head and
          153          keys with equal length are in normal sorted order. */
          154       break;
          155 
          156   /* No old data. */
          157   list = (HashList *) calloc (1, sizeof (HashList));
          158   if (!list)
          159     return 0;
          160   list->key = (char *) malloc (keylen);
          161   if (!list->key)
          162     {
          163       free (list);
          164       return 0;
          165     }
          166 
          167   memcpy (list->key, key, keylen);
          168   list->keylen = keylen;
          169   list->data = data;
          170 
          171   /* Insert list to the correct position. */
          172   if (!prev)
          173     {
          174       list->next = hash->hash_table[pos];
          175       hash->hash_table[pos] = list;
          176     }
          177   else
          178     {
          179       list->next = prev->next;
          180       prev->next = list;
          181     }
          182 #if STRHASH_DEBUG
          183   hash->items_in_hash++;
          184 #endif /* STRHASH_DEBUG */
          185   return 1;
          186 }
          187 
          188 
          189 int
          190 strhash_get (StringHashPtr hash, const char *key, int keylen, void **data)
          191 {
          192   HashList *list;
          193   int pos, cmp_val;
          194 
          195   if (!hash || !key || keylen <= 0 || !data)
          196     return 0;
          197 
          198   *data = NULL;
          199   pos = count_hash (key, keylen);
          200   for (list = hash->hash_table[pos]; list; list = list->next)
          201     if (list->keylen == keylen)
          202       {
          203         cmp_val = memcmp (key, list->key, keylen);
          204         if (cmp_val == 0)
          205           {
          206             *data = list->data;
          207             return 1;
          208           }
          209         else if (cmp_val < 0)
          210           /* Run over. */
          211           break;
          212       }
          213     else if (list->keylen > keylen)
          214       /* Run over. */
          215       break;
          216 
          217   return 0;
          218 }
          219 
          220 
          221 int
          222 strhash_delete (StringHashPtr hash, const char *key, int keylen, void **data)
          223 {
          224   HashList *list, *prev = NULL;
          225   int pos, cmp_val;
          226 
          227   if (!hash || !key || keylen <= 0 || !data)
          228     return 0;
          229 
          230   *data = NULL;
          231   pos = count_hash (key, keylen);
          232   for (list = hash->hash_table[pos]; list; prev = list, list = list->next)
          233     if (list->keylen == keylen)
          234       {
          235         cmp_val = memcmp (key, list->key, keylen);
          236         if (cmp_val == 0)
          237           {
          238             /* Value found. */
          239             if (prev == NULL)
          240               hash->hash_table[pos] = list->next;
          241             else
          242               prev->next = list->next;
          243 
          244             *data = list->data;
          245             free (list->key);
          246             free (list);
          247 
          248             /* Init scan. */
          249             hash->next_idx = 0;
          250             hash->next_item = NULL;
          251 
          252 #if STRHASH_DEBUG
          253             hash->items_in_hash--;
          254 #endif /* STRHASH_DEBUG */
          255             return 1;
          256           }
          257         else if (cmp_val < 0)
          258           /* Not found. */
          259           break;
          260       }
          261     else if (list->keylen > keylen)
          262       /* Run over. */
          263       break;
          264 
          265   return 0;
          266 }
          267 
          268 
          269 int
          270 strhash_get_first (StringHashPtr hash, char **key_return,
          271                    int *keylen_return, void **data_return)
          272 {
          273   if (!hash || !key_return || !keylen_return || !data_return)
          274     return 0;
          275 
          276   for (hash->next_idx = 0; hash->next_idx < HASH_SIZE; hash->next_idx++)
          277     {
          278       hash->next_item = hash->hash_table[hash->next_idx];
          279       if (hash->next_item)
          280         {
          281           *key_return = hash->next_item->key;
          282           *keylen_return = hash->next_item->keylen;
          283           *data_return = hash->next_item->data;
          284           return 1;
          285         }
          286     }
          287   return 0;
          288 }
          289 
          290 
          291 int
          292 strhash_get_next (StringHashPtr hash, char **key_return,
          293                   int *keylen_return, void **data_return)
          294 {
          295   if (!hash || !key_return || !keylen_return || !data_return)
          296     return 0;
          297 
          298   for (; hash->next_idx < HASH_SIZE; hash->next_idx++)
          299     {
          300       if (hash->next_item == NULL)
          301         hash->next_item = hash->hash_table[hash->next_idx];
          302       else
          303         hash->next_item = hash->next_item->next;
          304 
          305       if (hash->next_item)
          306         {
          307           *key_return = hash->next_item->key;
          308           *keylen_return = hash->next_item->keylen;
          309           *data_return = hash->next_item->data;
          310           return 1;
          311         }
          312     }
          313   return 0;
          314 }
          315 
          316 
          317 #if STRHASH_DEBUG
          318 void
          319 strhash_debug (StringHashPtr hash)
          320 {
          321   int i, count = 0, max = 0;
          322   HashList *tmp;
          323 
          324   if (!hash)
          325     {
          326       fprintf (stderr, "Invalid hash handle!\n");
          327       return;
          328     }
          329   fprintf (stderr, "hash_size\t%d\n", HASH_SIZE);
          330   fprintf (stderr, "items_in_hash\t%d\n", hash->items_in_hash);
          331 
          332   for (i = 0; i < HASH_SIZE; i++)
          333     if (hash->hash_table[i] == NULL)
          334       count++;
          335   fprintf (stderr, "empty entries\t%d\n", count);
          336 
          337   count = 0;
          338   for (i = 0; i < HASH_SIZE; i++)
          339     {
          340       for (tmp = hash->hash_table[i]; tmp; tmp = tmp->next)
          341         count++;
          342       max = count > max ? count : max;
          343       count = 0;
          344     }
          345   fprintf (stderr, "longest list\t%d\n", max);
          346 
          347   if (max > 0)
          348     {
          349       /* Print the first longest list. */
          350       for (i = 0; i < HASH_SIZE; i++)
          351         {
          352           count = 0;
          353           for (tmp = hash->hash_table[i]; tmp; tmp = tmp->next)
          354             count++;
          355           if (count == max)
          356             {
          357               for (count = 0, tmp = hash->hash_table[i]; tmp;
          358                    tmp = tmp->next, count++)
          359                 {
          360                   fprintf (stderr, "%d\t", count);
          361                   for (i = 0; i < tmp->keylen; i++)
          362                     fprintf (stderr, "%c", tmp->key[i]);
          363                 }
          364               break;
          365             }
          366         }
          367     }
          368 }
          369 #endif /* STRHASH_DEBUG */
          370 
          371 
          372 /*
          373  * Static functions.
          374  */
          375 
          376 static int
          377 count_hash (const char *key, int keylen)
          378 {
          379   unsigned int val = 0;
          380   int i;
          381 
          382   for (i = 0; i < keylen; i++)
          383     val = (val << 5) ^ (unsigned char) key[i]
          384       ^ (val >> 16) ^ (val >> 7);
          385   return val % HASH_SIZE;
          386 }