Added the $HOME variable to be used in AFMPATH
[enscript.git] / afmlib / strhash.c
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 }