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