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