xref: /openbsd/gnu/usr.bin/binutils-2.17/gas/hash.c (revision 3d8817e4)
1*3d8817e4Smiod /* hash.c -- gas hash table code
2*3d8817e4Smiod    Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999,
3*3d8817e4Smiod    2000, 2001, 2002, 2003, 2005
4*3d8817e4Smiod    Free Software Foundation, Inc.
5*3d8817e4Smiod 
6*3d8817e4Smiod    This file is part of GAS, the GNU Assembler.
7*3d8817e4Smiod 
8*3d8817e4Smiod    GAS is free software; you can redistribute it and/or modify
9*3d8817e4Smiod    it under the terms of the GNU General Public License as published by
10*3d8817e4Smiod    the Free Software Foundation; either version 2, or (at your option)
11*3d8817e4Smiod    any later version.
12*3d8817e4Smiod 
13*3d8817e4Smiod    GAS is distributed in the hope that it will be useful,
14*3d8817e4Smiod    but WITHOUT ANY WARRANTY; without even the implied warranty of
15*3d8817e4Smiod    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16*3d8817e4Smiod    GNU General Public License for more details.
17*3d8817e4Smiod 
18*3d8817e4Smiod    You should have received a copy of the GNU General Public License
19*3d8817e4Smiod    along with GAS; see the file COPYING.  If not, write to the Free
20*3d8817e4Smiod    Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
21*3d8817e4Smiod    02110-1301, USA.  */
22*3d8817e4Smiod 
23*3d8817e4Smiod /* This version of the hash table code is a wholescale replacement of
24*3d8817e4Smiod    the old hash table code, which was fairly bad.  This is based on
25*3d8817e4Smiod    the hash table code in BFD, but optimized slightly for the
26*3d8817e4Smiod    assembler.  The assembler does not need to derive structures that
27*3d8817e4Smiod    are stored in the hash table.  Instead, it always stores a pointer.
28*3d8817e4Smiod    The assembler uses the hash table mostly to store symbols, and we
29*3d8817e4Smiod    don't need to confuse the symbol structure with a hash table
30*3d8817e4Smiod    structure.  */
31*3d8817e4Smiod 
32*3d8817e4Smiod #include "as.h"
33*3d8817e4Smiod #include "safe-ctype.h"
34*3d8817e4Smiod #include "obstack.h"
35*3d8817e4Smiod 
36*3d8817e4Smiod /* An entry in a hash table.  */
37*3d8817e4Smiod 
38*3d8817e4Smiod struct hash_entry {
39*3d8817e4Smiod   /* Next entry for this hash code.  */
40*3d8817e4Smiod   struct hash_entry *next;
41*3d8817e4Smiod   /* String being hashed.  */
42*3d8817e4Smiod   const char *string;
43*3d8817e4Smiod   /* Hash code.  This is the full hash code, not the index into the
44*3d8817e4Smiod      table.  */
45*3d8817e4Smiod   unsigned long hash;
46*3d8817e4Smiod   /* Pointer being stored in the hash table.  */
47*3d8817e4Smiod   PTR data;
48*3d8817e4Smiod };
49*3d8817e4Smiod 
50*3d8817e4Smiod /* A hash table.  */
51*3d8817e4Smiod 
52*3d8817e4Smiod struct hash_control {
53*3d8817e4Smiod   /* The hash array.  */
54*3d8817e4Smiod   struct hash_entry **table;
55*3d8817e4Smiod   /* The number of slots in the hash table.  */
56*3d8817e4Smiod   unsigned int size;
57*3d8817e4Smiod   /* An obstack for this hash table.  */
58*3d8817e4Smiod   struct obstack memory;
59*3d8817e4Smiod 
60*3d8817e4Smiod #ifdef HASH_STATISTICS
61*3d8817e4Smiod   /* Statistics.  */
62*3d8817e4Smiod   unsigned long lookups;
63*3d8817e4Smiod   unsigned long hash_compares;
64*3d8817e4Smiod   unsigned long string_compares;
65*3d8817e4Smiod   unsigned long insertions;
66*3d8817e4Smiod   unsigned long replacements;
67*3d8817e4Smiod   unsigned long deletions;
68*3d8817e4Smiod #endif /* HASH_STATISTICS */
69*3d8817e4Smiod };
70*3d8817e4Smiod 
71*3d8817e4Smiod /* The default number of entries to use when creating a hash table.
72*3d8817e4Smiod    Note this value can be reduced to 4051 by using the command line
73*3d8817e4Smiod    switch --reduce-memory-overheads, or set to other values by using
74*3d8817e4Smiod    the --hash-size=<NUMBER> switch.  */
75*3d8817e4Smiod 
76*3d8817e4Smiod static unsigned long gas_hash_table_size = 65537;
77*3d8817e4Smiod 
78*3d8817e4Smiod void
set_gas_hash_table_size(unsigned long size)79*3d8817e4Smiod set_gas_hash_table_size (unsigned long size)
80*3d8817e4Smiod {
81*3d8817e4Smiod   gas_hash_table_size = size;
82*3d8817e4Smiod }
83*3d8817e4Smiod 
84*3d8817e4Smiod /* FIXME: This function should be amalgmated with bfd/hash.c:bfd_hash_set_default_size().  */
85*3d8817e4Smiod static unsigned long
get_gas_hash_table_size(void)86*3d8817e4Smiod get_gas_hash_table_size (void)
87*3d8817e4Smiod {
88*3d8817e4Smiod   /* Extend this prime list if you want more granularity of hash table size.  */
89*3d8817e4Smiod   static const unsigned long hash_size_primes[] =
90*3d8817e4Smiod     {
91*3d8817e4Smiod       1021, 4051, 8599, 16699, 65537
92*3d8817e4Smiod     };
93*3d8817e4Smiod   unsigned int index;
94*3d8817e4Smiod 
95*3d8817e4Smiod   /* Work out the best prime number near the hash_size.
96*3d8817e4Smiod      FIXME: This could be a more sophisticated algorithm,
97*3d8817e4Smiod      but is it really worth implementing it ?   */
98*3d8817e4Smiod   for (index = 0; index < ARRAY_SIZE (hash_size_primes) - 1; ++index)
99*3d8817e4Smiod     if (gas_hash_table_size <= hash_size_primes[index])
100*3d8817e4Smiod       break;
101*3d8817e4Smiod 
102*3d8817e4Smiod   return hash_size_primes[index];
103*3d8817e4Smiod }
104*3d8817e4Smiod 
105*3d8817e4Smiod /* Create a hash table.  This return a control block.  */
106*3d8817e4Smiod 
107*3d8817e4Smiod struct hash_control *
hash_new(void)108*3d8817e4Smiod hash_new (void)
109*3d8817e4Smiod {
110*3d8817e4Smiod   unsigned long size;
111*3d8817e4Smiod   unsigned long alloc;
112*3d8817e4Smiod   struct hash_control *ret;
113*3d8817e4Smiod 
114*3d8817e4Smiod   size = get_gas_hash_table_size ();
115*3d8817e4Smiod 
116*3d8817e4Smiod   ret = xmalloc (sizeof *ret);
117*3d8817e4Smiod   obstack_begin (&ret->memory, chunksize);
118*3d8817e4Smiod   alloc = size * sizeof (struct hash_entry *);
119*3d8817e4Smiod   ret->table = obstack_alloc (&ret->memory, alloc);
120*3d8817e4Smiod   memset (ret->table, 0, alloc);
121*3d8817e4Smiod   ret->size = size;
122*3d8817e4Smiod 
123*3d8817e4Smiod #ifdef HASH_STATISTICS
124*3d8817e4Smiod   ret->lookups = 0;
125*3d8817e4Smiod   ret->hash_compares = 0;
126*3d8817e4Smiod   ret->string_compares = 0;
127*3d8817e4Smiod   ret->insertions = 0;
128*3d8817e4Smiod   ret->replacements = 0;
129*3d8817e4Smiod   ret->deletions = 0;
130*3d8817e4Smiod #endif
131*3d8817e4Smiod 
132*3d8817e4Smiod   return ret;
133*3d8817e4Smiod }
134*3d8817e4Smiod 
135*3d8817e4Smiod /* Delete a hash table, freeing all allocated memory.  */
136*3d8817e4Smiod 
137*3d8817e4Smiod void
hash_die(struct hash_control * table)138*3d8817e4Smiod hash_die (struct hash_control *table)
139*3d8817e4Smiod {
140*3d8817e4Smiod   obstack_free (&table->memory, 0);
141*3d8817e4Smiod   free (table);
142*3d8817e4Smiod }
143*3d8817e4Smiod 
144*3d8817e4Smiod /* Look up a string in a hash table.  This returns a pointer to the
145*3d8817e4Smiod    hash_entry, or NULL if the string is not in the table.  If PLIST is
146*3d8817e4Smiod    not NULL, this sets *PLIST to point to the start of the list which
147*3d8817e4Smiod    would hold this hash entry.  If PHASH is not NULL, this sets *PHASH
148*3d8817e4Smiod    to the hash code for KEY.
149*3d8817e4Smiod 
150*3d8817e4Smiod    Each time we look up a string, we move it to the start of the list
151*3d8817e4Smiod    for its hash code, to take advantage of referential locality.  */
152*3d8817e4Smiod 
153*3d8817e4Smiod static struct hash_entry *
hash_lookup(struct hash_control * table,const char * key,size_t len,struct hash_entry *** plist,unsigned long * phash)154*3d8817e4Smiod hash_lookup (struct hash_control *table, const char *key, size_t len,
155*3d8817e4Smiod 	     struct hash_entry ***plist, unsigned long *phash)
156*3d8817e4Smiod {
157*3d8817e4Smiod   unsigned long hash;
158*3d8817e4Smiod   size_t n;
159*3d8817e4Smiod   unsigned int c;
160*3d8817e4Smiod   unsigned int index;
161*3d8817e4Smiod   struct hash_entry **list;
162*3d8817e4Smiod   struct hash_entry *p;
163*3d8817e4Smiod   struct hash_entry *prev;
164*3d8817e4Smiod 
165*3d8817e4Smiod #ifdef HASH_STATISTICS
166*3d8817e4Smiod   ++table->lookups;
167*3d8817e4Smiod #endif
168*3d8817e4Smiod 
169*3d8817e4Smiod   hash = 0;
170*3d8817e4Smiod   for (n = 0; n < len; n++)
171*3d8817e4Smiod     {
172*3d8817e4Smiod       c = key[n];
173*3d8817e4Smiod       hash += c + (c << 17);
174*3d8817e4Smiod       hash ^= hash >> 2;
175*3d8817e4Smiod     }
176*3d8817e4Smiod   hash += len + (len << 17);
177*3d8817e4Smiod   hash ^= hash >> 2;
178*3d8817e4Smiod 
179*3d8817e4Smiod   if (phash != NULL)
180*3d8817e4Smiod     *phash = hash;
181*3d8817e4Smiod 
182*3d8817e4Smiod   index = hash % table->size;
183*3d8817e4Smiod   list = table->table + index;
184*3d8817e4Smiod 
185*3d8817e4Smiod   if (plist != NULL)
186*3d8817e4Smiod     *plist = list;
187*3d8817e4Smiod 
188*3d8817e4Smiod   prev = NULL;
189*3d8817e4Smiod   for (p = *list; p != NULL; p = p->next)
190*3d8817e4Smiod     {
191*3d8817e4Smiod #ifdef HASH_STATISTICS
192*3d8817e4Smiod       ++table->hash_compares;
193*3d8817e4Smiod #endif
194*3d8817e4Smiod 
195*3d8817e4Smiod       if (p->hash == hash)
196*3d8817e4Smiod 	{
197*3d8817e4Smiod #ifdef HASH_STATISTICS
198*3d8817e4Smiod 	  ++table->string_compares;
199*3d8817e4Smiod #endif
200*3d8817e4Smiod 
201*3d8817e4Smiod 	  if (strncmp (p->string, key, len) == 0 && p->string[len] == '\0')
202*3d8817e4Smiod 	    {
203*3d8817e4Smiod 	      if (prev != NULL)
204*3d8817e4Smiod 		{
205*3d8817e4Smiod 		  prev->next = p->next;
206*3d8817e4Smiod 		  p->next = *list;
207*3d8817e4Smiod 		  *list = p;
208*3d8817e4Smiod 		}
209*3d8817e4Smiod 
210*3d8817e4Smiod 	      return p;
211*3d8817e4Smiod 	    }
212*3d8817e4Smiod 	}
213*3d8817e4Smiod 
214*3d8817e4Smiod       prev = p;
215*3d8817e4Smiod     }
216*3d8817e4Smiod 
217*3d8817e4Smiod   return NULL;
218*3d8817e4Smiod }
219*3d8817e4Smiod 
220*3d8817e4Smiod /* Insert an entry into a hash table.  This returns NULL on success.
221*3d8817e4Smiod    On error, it returns a printable string indicating the error.  It
222*3d8817e4Smiod    is considered to be an error if the entry already exists in the
223*3d8817e4Smiod    hash table.  */
224*3d8817e4Smiod 
225*3d8817e4Smiod const char *
hash_insert(struct hash_control * table,const char * key,PTR value)226*3d8817e4Smiod hash_insert (struct hash_control *table, const char *key, PTR value)
227*3d8817e4Smiod {
228*3d8817e4Smiod   struct hash_entry *p;
229*3d8817e4Smiod   struct hash_entry **list;
230*3d8817e4Smiod   unsigned long hash;
231*3d8817e4Smiod 
232*3d8817e4Smiod   p = hash_lookup (table, key, strlen (key), &list, &hash);
233*3d8817e4Smiod   if (p != NULL)
234*3d8817e4Smiod     return "exists";
235*3d8817e4Smiod 
236*3d8817e4Smiod #ifdef HASH_STATISTICS
237*3d8817e4Smiod   ++table->insertions;
238*3d8817e4Smiod #endif
239*3d8817e4Smiod 
240*3d8817e4Smiod   p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p));
241*3d8817e4Smiod   p->string = key;
242*3d8817e4Smiod   p->hash = hash;
243*3d8817e4Smiod   p->data = value;
244*3d8817e4Smiod 
245*3d8817e4Smiod   p->next = *list;
246*3d8817e4Smiod   *list = p;
247*3d8817e4Smiod 
248*3d8817e4Smiod   return NULL;
249*3d8817e4Smiod }
250*3d8817e4Smiod 
251*3d8817e4Smiod /* Insert or replace an entry in a hash table.  This returns NULL on
252*3d8817e4Smiod    success.  On error, it returns a printable string indicating the
253*3d8817e4Smiod    error.  If an entry already exists, its value is replaced.  */
254*3d8817e4Smiod 
255*3d8817e4Smiod const char *
hash_jam(struct hash_control * table,const char * key,PTR value)256*3d8817e4Smiod hash_jam (struct hash_control *table, const char *key, PTR value)
257*3d8817e4Smiod {
258*3d8817e4Smiod   struct hash_entry *p;
259*3d8817e4Smiod   struct hash_entry **list;
260*3d8817e4Smiod   unsigned long hash;
261*3d8817e4Smiod 
262*3d8817e4Smiod   p = hash_lookup (table, key, strlen (key), &list, &hash);
263*3d8817e4Smiod   if (p != NULL)
264*3d8817e4Smiod     {
265*3d8817e4Smiod #ifdef HASH_STATISTICS
266*3d8817e4Smiod       ++table->replacements;
267*3d8817e4Smiod #endif
268*3d8817e4Smiod 
269*3d8817e4Smiod       p->data = value;
270*3d8817e4Smiod     }
271*3d8817e4Smiod   else
272*3d8817e4Smiod     {
273*3d8817e4Smiod #ifdef HASH_STATISTICS
274*3d8817e4Smiod       ++table->insertions;
275*3d8817e4Smiod #endif
276*3d8817e4Smiod 
277*3d8817e4Smiod       p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p));
278*3d8817e4Smiod       p->string = key;
279*3d8817e4Smiod       p->hash = hash;
280*3d8817e4Smiod       p->data = value;
281*3d8817e4Smiod 
282*3d8817e4Smiod       p->next = *list;
283*3d8817e4Smiod       *list = p;
284*3d8817e4Smiod     }
285*3d8817e4Smiod 
286*3d8817e4Smiod   return NULL;
287*3d8817e4Smiod }
288*3d8817e4Smiod 
289*3d8817e4Smiod /* Replace an existing entry in a hash table.  This returns the old
290*3d8817e4Smiod    value stored for the entry.  If the entry is not found in the hash
291*3d8817e4Smiod    table, this does nothing and returns NULL.  */
292*3d8817e4Smiod 
293*3d8817e4Smiod PTR
hash_replace(struct hash_control * table,const char * key,PTR value)294*3d8817e4Smiod hash_replace (struct hash_control *table, const char *key, PTR value)
295*3d8817e4Smiod {
296*3d8817e4Smiod   struct hash_entry *p;
297*3d8817e4Smiod   PTR ret;
298*3d8817e4Smiod 
299*3d8817e4Smiod   p = hash_lookup (table, key, strlen (key), NULL, NULL);
300*3d8817e4Smiod   if (p == NULL)
301*3d8817e4Smiod     return NULL;
302*3d8817e4Smiod 
303*3d8817e4Smiod #ifdef HASH_STATISTICS
304*3d8817e4Smiod   ++table->replacements;
305*3d8817e4Smiod #endif
306*3d8817e4Smiod 
307*3d8817e4Smiod   ret = p->data;
308*3d8817e4Smiod 
309*3d8817e4Smiod   p->data = value;
310*3d8817e4Smiod 
311*3d8817e4Smiod   return ret;
312*3d8817e4Smiod }
313*3d8817e4Smiod 
314*3d8817e4Smiod /* Find an entry in a hash table, returning its value.  Returns NULL
315*3d8817e4Smiod    if the entry is not found.  */
316*3d8817e4Smiod 
317*3d8817e4Smiod PTR
hash_find(struct hash_control * table,const char * key)318*3d8817e4Smiod hash_find (struct hash_control *table, const char *key)
319*3d8817e4Smiod {
320*3d8817e4Smiod   struct hash_entry *p;
321*3d8817e4Smiod 
322*3d8817e4Smiod   p = hash_lookup (table, key, strlen (key), NULL, NULL);
323*3d8817e4Smiod   if (p == NULL)
324*3d8817e4Smiod     return NULL;
325*3d8817e4Smiod 
326*3d8817e4Smiod   return p->data;
327*3d8817e4Smiod }
328*3d8817e4Smiod 
329*3d8817e4Smiod /* As hash_find, but KEY is of length LEN and is not guaranteed to be
330*3d8817e4Smiod    NUL-terminated.  */
331*3d8817e4Smiod 
332*3d8817e4Smiod PTR
hash_find_n(struct hash_control * table,const char * key,size_t len)333*3d8817e4Smiod hash_find_n (struct hash_control *table, const char *key, size_t len)
334*3d8817e4Smiod {
335*3d8817e4Smiod   struct hash_entry *p;
336*3d8817e4Smiod 
337*3d8817e4Smiod   p = hash_lookup (table, key, len, NULL, NULL);
338*3d8817e4Smiod   if (p == NULL)
339*3d8817e4Smiod     return NULL;
340*3d8817e4Smiod 
341*3d8817e4Smiod   return p->data;
342*3d8817e4Smiod }
343*3d8817e4Smiod 
344*3d8817e4Smiod /* Delete an entry from a hash table.  This returns the value stored
345*3d8817e4Smiod    for that entry, or NULL if there is no such entry.  */
346*3d8817e4Smiod 
347*3d8817e4Smiod PTR
hash_delete(struct hash_control * table,const char * key)348*3d8817e4Smiod hash_delete (struct hash_control *table, const char *key)
349*3d8817e4Smiod {
350*3d8817e4Smiod   struct hash_entry *p;
351*3d8817e4Smiod   struct hash_entry **list;
352*3d8817e4Smiod 
353*3d8817e4Smiod   p = hash_lookup (table, key, strlen (key), &list, NULL);
354*3d8817e4Smiod   if (p == NULL)
355*3d8817e4Smiod     return NULL;
356*3d8817e4Smiod 
357*3d8817e4Smiod   if (p != *list)
358*3d8817e4Smiod     abort ();
359*3d8817e4Smiod 
360*3d8817e4Smiod #ifdef HASH_STATISTICS
361*3d8817e4Smiod   ++table->deletions;
362*3d8817e4Smiod #endif
363*3d8817e4Smiod 
364*3d8817e4Smiod   *list = p->next;
365*3d8817e4Smiod 
366*3d8817e4Smiod   /* Note that we never reclaim the memory for this entry.  If gas
367*3d8817e4Smiod      ever starts deleting hash table entries in a big way, this will
368*3d8817e4Smiod      have to change.  */
369*3d8817e4Smiod 
370*3d8817e4Smiod   return p->data;
371*3d8817e4Smiod }
372*3d8817e4Smiod 
373*3d8817e4Smiod /* Traverse a hash table.  Call the function on every entry in the
374*3d8817e4Smiod    hash table.  */
375*3d8817e4Smiod 
376*3d8817e4Smiod void
hash_traverse(struct hash_control * table,void (* pfn)(const char * key,PTR value))377*3d8817e4Smiod hash_traverse (struct hash_control *table,
378*3d8817e4Smiod 	       void (*pfn) (const char *key, PTR value))
379*3d8817e4Smiod {
380*3d8817e4Smiod   unsigned int i;
381*3d8817e4Smiod 
382*3d8817e4Smiod   for (i = 0; i < table->size; ++i)
383*3d8817e4Smiod     {
384*3d8817e4Smiod       struct hash_entry *p;
385*3d8817e4Smiod 
386*3d8817e4Smiod       for (p = table->table[i]; p != NULL; p = p->next)
387*3d8817e4Smiod 	(*pfn) (p->string, p->data);
388*3d8817e4Smiod     }
389*3d8817e4Smiod }
390*3d8817e4Smiod 
391*3d8817e4Smiod /* Print hash table statistics on the specified file.  NAME is the
392*3d8817e4Smiod    name of the hash table, used for printing a header.  */
393*3d8817e4Smiod 
394*3d8817e4Smiod void
hash_print_statistics(FILE * f ATTRIBUTE_UNUSED,const char * name ATTRIBUTE_UNUSED,struct hash_control * table ATTRIBUTE_UNUSED)395*3d8817e4Smiod hash_print_statistics (FILE *f ATTRIBUTE_UNUSED,
396*3d8817e4Smiod 		       const char *name ATTRIBUTE_UNUSED,
397*3d8817e4Smiod 		       struct hash_control *table ATTRIBUTE_UNUSED)
398*3d8817e4Smiod {
399*3d8817e4Smiod #ifdef HASH_STATISTICS
400*3d8817e4Smiod   unsigned int i;
401*3d8817e4Smiod   unsigned long total;
402*3d8817e4Smiod   unsigned long empty;
403*3d8817e4Smiod 
404*3d8817e4Smiod   fprintf (f, "%s hash statistics:\n", name);
405*3d8817e4Smiod   fprintf (f, "\t%lu lookups\n", table->lookups);
406*3d8817e4Smiod   fprintf (f, "\t%lu hash comparisons\n", table->hash_compares);
407*3d8817e4Smiod   fprintf (f, "\t%lu string comparisons\n", table->string_compares);
408*3d8817e4Smiod   fprintf (f, "\t%lu insertions\n", table->insertions);
409*3d8817e4Smiod   fprintf (f, "\t%lu replacements\n", table->replacements);
410*3d8817e4Smiod   fprintf (f, "\t%lu deletions\n", table->deletions);
411*3d8817e4Smiod 
412*3d8817e4Smiod   total = 0;
413*3d8817e4Smiod   empty = 0;
414*3d8817e4Smiod   for (i = 0; i < table->size; ++i)
415*3d8817e4Smiod     {
416*3d8817e4Smiod       struct hash_entry *p;
417*3d8817e4Smiod 
418*3d8817e4Smiod       if (table->table[i] == NULL)
419*3d8817e4Smiod 	++empty;
420*3d8817e4Smiod       else
421*3d8817e4Smiod 	{
422*3d8817e4Smiod 	  for (p = table->table[i]; p != NULL; p = p->next)
423*3d8817e4Smiod 	    ++total;
424*3d8817e4Smiod 	}
425*3d8817e4Smiod     }
426*3d8817e4Smiod 
427*3d8817e4Smiod   fprintf (f, "\t%g average chain length\n", (double) total / table->size);
428*3d8817e4Smiod   fprintf (f, "\t%lu empty slots\n", empty);
429*3d8817e4Smiod #endif
430*3d8817e4Smiod }
431*3d8817e4Smiod 
432*3d8817e4Smiod #ifdef TEST
433*3d8817e4Smiod 
434*3d8817e4Smiod /* This test program is left over from the old hash table code.  */
435*3d8817e4Smiod 
436*3d8817e4Smiod /* Number of hash tables to maintain (at once) in any testing.  */
437*3d8817e4Smiod #define TABLES (6)
438*3d8817e4Smiod 
439*3d8817e4Smiod /* We can have 12 statistics.  */
440*3d8817e4Smiod #define STATBUFSIZE (12)
441*3d8817e4Smiod 
442*3d8817e4Smiod /* Display statistics here.  */
443*3d8817e4Smiod int statbuf[STATBUFSIZE];
444*3d8817e4Smiod 
445*3d8817e4Smiod /* Human farts here.  */
446*3d8817e4Smiod char answer[100];
447*3d8817e4Smiod 
448*3d8817e4Smiod /* We test many hash tables at once.  */
449*3d8817e4Smiod char *hashtable[TABLES];
450*3d8817e4Smiod 
451*3d8817e4Smiod /* Points to current hash_control.  */
452*3d8817e4Smiod char *h;
453*3d8817e4Smiod char **pp;
454*3d8817e4Smiod char *p;
455*3d8817e4Smiod char *name;
456*3d8817e4Smiod char *value;
457*3d8817e4Smiod int size;
458*3d8817e4Smiod int used;
459*3d8817e4Smiod char command;
460*3d8817e4Smiod 
461*3d8817e4Smiod /* Number 0:TABLES-1 of current hashed symbol table.  */
462*3d8817e4Smiod int number;
463*3d8817e4Smiod 
464*3d8817e4Smiod int
main()465*3d8817e4Smiod main ()
466*3d8817e4Smiod {
467*3d8817e4Smiod   void applicatee ();
468*3d8817e4Smiod   void destroy ();
469*3d8817e4Smiod   char *what ();
470*3d8817e4Smiod   int *ip;
471*3d8817e4Smiod 
472*3d8817e4Smiod   number = 0;
473*3d8817e4Smiod   h = 0;
474*3d8817e4Smiod   printf ("type h <RETURN> for help\n");
475*3d8817e4Smiod   for (;;)
476*3d8817e4Smiod     {
477*3d8817e4Smiod       printf ("hash_test command: ");
478*3d8817e4Smiod       gets (answer);
479*3d8817e4Smiod       command = answer[0];
480*3d8817e4Smiod       command = TOLOWER (command);	/* Ecch!  */
481*3d8817e4Smiod       switch (command)
482*3d8817e4Smiod 	{
483*3d8817e4Smiod 	case '#':
484*3d8817e4Smiod 	  printf ("old hash table #=%d.\n", number);
485*3d8817e4Smiod 	  whattable ();
486*3d8817e4Smiod 	  break;
487*3d8817e4Smiod 	case '?':
488*3d8817e4Smiod 	  for (pp = hashtable; pp < hashtable + TABLES; pp++)
489*3d8817e4Smiod 	    {
490*3d8817e4Smiod 	      printf ("address of hash table #%d control block is %xx\n",
491*3d8817e4Smiod 		      pp - hashtable, *pp);
492*3d8817e4Smiod 	    }
493*3d8817e4Smiod 	  break;
494*3d8817e4Smiod 	case 'a':
495*3d8817e4Smiod 	  hash_traverse (h, applicatee);
496*3d8817e4Smiod 	  break;
497*3d8817e4Smiod 	case 'd':
498*3d8817e4Smiod 	  hash_traverse (h, destroy);
499*3d8817e4Smiod 	  hash_die (h);
500*3d8817e4Smiod 	  break;
501*3d8817e4Smiod 	case 'f':
502*3d8817e4Smiod 	  p = hash_find (h, name = what ("symbol"));
503*3d8817e4Smiod 	  printf ("value of \"%s\" is \"%s\"\n", name, p ? p : "NOT-PRESENT");
504*3d8817e4Smiod 	  break;
505*3d8817e4Smiod 	case 'h':
506*3d8817e4Smiod 	  printf ("# show old, select new default hash table number\n");
507*3d8817e4Smiod 	  printf ("? display all hashtable control block addresses\n");
508*3d8817e4Smiod 	  printf ("a apply a simple display-er to each symbol in table\n");
509*3d8817e4Smiod 	  printf ("d die: destroy hashtable\n");
510*3d8817e4Smiod 	  printf ("f find value of nominated symbol\n");
511*3d8817e4Smiod 	  printf ("h this help\n");
512*3d8817e4Smiod 	  printf ("i insert value into symbol\n");
513*3d8817e4Smiod 	  printf ("j jam value into symbol\n");
514*3d8817e4Smiod 	  printf ("n new hashtable\n");
515*3d8817e4Smiod 	  printf ("r replace a value with another\n");
516*3d8817e4Smiod 	  printf ("s say what %% of table is used\n");
517*3d8817e4Smiod 	  printf ("q exit this program\n");
518*3d8817e4Smiod 	  printf ("x delete a symbol from table, report its value\n");
519*3d8817e4Smiod 	  break;
520*3d8817e4Smiod 	case 'i':
521*3d8817e4Smiod 	  p = hash_insert (h, name = what ("symbol"), value = what ("value"));
522*3d8817e4Smiod 	  if (p)
523*3d8817e4Smiod 	    {
524*3d8817e4Smiod 	      printf ("symbol=\"%s\"  value=\"%s\"  error=%s\n", name, value,
525*3d8817e4Smiod 		      p);
526*3d8817e4Smiod 	    }
527*3d8817e4Smiod 	  break;
528*3d8817e4Smiod 	case 'j':
529*3d8817e4Smiod 	  p = hash_jam (h, name = what ("symbol"), value = what ("value"));
530*3d8817e4Smiod 	  if (p)
531*3d8817e4Smiod 	    {
532*3d8817e4Smiod 	      printf ("symbol=\"%s\"  value=\"%s\"  error=%s\n", name, value, p);
533*3d8817e4Smiod 	    }
534*3d8817e4Smiod 	  break;
535*3d8817e4Smiod 	case 'n':
536*3d8817e4Smiod 	  h = hashtable[number] = (char *) hash_new ();
537*3d8817e4Smiod 	  break;
538*3d8817e4Smiod 	case 'q':
539*3d8817e4Smiod 	  exit (EXIT_SUCCESS);
540*3d8817e4Smiod 	case 'r':
541*3d8817e4Smiod 	  p = hash_replace (h, name = what ("symbol"), value = what ("value"));
542*3d8817e4Smiod 	  printf ("old value was \"%s\"\n", p ? p : "{}");
543*3d8817e4Smiod 	  break;
544*3d8817e4Smiod 	case 's':
545*3d8817e4Smiod 	  hash_say (h, statbuf, STATBUFSIZE);
546*3d8817e4Smiod 	  for (ip = statbuf; ip < statbuf + STATBUFSIZE; ip++)
547*3d8817e4Smiod 	    {
548*3d8817e4Smiod 	      printf ("%d ", *ip);
549*3d8817e4Smiod 	    }
550*3d8817e4Smiod 	  printf ("\n");
551*3d8817e4Smiod 	  break;
552*3d8817e4Smiod 	case 'x':
553*3d8817e4Smiod 	  p = hash_delete (h, name = what ("symbol"));
554*3d8817e4Smiod 	  printf ("old value was \"%s\"\n", p ? p : "{}");
555*3d8817e4Smiod 	  break;
556*3d8817e4Smiod 	default:
557*3d8817e4Smiod 	  printf ("I can't understand command \"%c\"\n", command);
558*3d8817e4Smiod 	  break;
559*3d8817e4Smiod 	}
560*3d8817e4Smiod     }
561*3d8817e4Smiod }
562*3d8817e4Smiod 
563*3d8817e4Smiod char *
what(description)564*3d8817e4Smiod what (description)
565*3d8817e4Smiod      char *description;
566*3d8817e4Smiod {
567*3d8817e4Smiod   printf ("   %s : ", description);
568*3d8817e4Smiod   gets (answer);
569*3d8817e4Smiod   return xstrdup (answer);
570*3d8817e4Smiod }
571*3d8817e4Smiod 
572*3d8817e4Smiod void
destroy(string,value)573*3d8817e4Smiod destroy (string, value)
574*3d8817e4Smiod      char *string;
575*3d8817e4Smiod      char *value;
576*3d8817e4Smiod {
577*3d8817e4Smiod   free (string);
578*3d8817e4Smiod   free (value);
579*3d8817e4Smiod }
580*3d8817e4Smiod 
581*3d8817e4Smiod void
applicatee(string,value)582*3d8817e4Smiod applicatee (string, value)
583*3d8817e4Smiod      char *string;
584*3d8817e4Smiod      char *value;
585*3d8817e4Smiod {
586*3d8817e4Smiod   printf ("%.20s-%.20s\n", string, value);
587*3d8817e4Smiod }
588*3d8817e4Smiod 
589*3d8817e4Smiod /* Determine number: what hash table to use.
590*3d8817e4Smiod    Also determine h: points to hash_control.  */
591*3d8817e4Smiod 
592*3d8817e4Smiod void
whattable()593*3d8817e4Smiod whattable ()
594*3d8817e4Smiod {
595*3d8817e4Smiod   for (;;)
596*3d8817e4Smiod     {
597*3d8817e4Smiod       printf ("   what hash table (%d:%d) ?  ", 0, TABLES - 1);
598*3d8817e4Smiod       gets (answer);
599*3d8817e4Smiod       sscanf (answer, "%d", &number);
600*3d8817e4Smiod       if (number >= 0 && number < TABLES)
601*3d8817e4Smiod 	{
602*3d8817e4Smiod 	  h = hashtable[number];
603*3d8817e4Smiod 	  if (!h)
604*3d8817e4Smiod 	    {
605*3d8817e4Smiod 	      printf ("warning: current hash-table-#%d. has no hash-control\n", number);
606*3d8817e4Smiod 	    }
607*3d8817e4Smiod 	  return;
608*3d8817e4Smiod 	}
609*3d8817e4Smiod       else
610*3d8817e4Smiod 	{
611*3d8817e4Smiod 	  printf ("invalid hash table number: %d\n", number);
612*3d8817e4Smiod 	}
613*3d8817e4Smiod     }
614*3d8817e4Smiod }
615*3d8817e4Smiod 
616*3d8817e4Smiod #endif /* TEST */
617