123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596 |
- /* hash.c -- gas hash table code
- Copyright (C) 1987-2015 Free Software Foundation, Inc.
- This file is part of GAS, the GNU Assembler.
- GAS is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
- GAS is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with GAS; see the file COPYING. If not, write to the Free
- Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
- 02110-1301, USA. */
- /* This version of the hash table code is a wholescale replacement of
- the old hash table code, which was fairly bad. This is based on
- the hash table code in BFD, but optimized slightly for the
- assembler. The assembler does not need to derive structures that
- are stored in the hash table. Instead, it always stores a pointer.
- The assembler uses the hash table mostly to store symbols, and we
- don't need to confuse the symbol structure with a hash table
- structure. */
- #include "as.h"
- #include "safe-ctype.h"
- #include "obstack.h"
- /* An entry in a hash table. */
- struct hash_entry {
- /* Next entry for this hash code. */
- struct hash_entry *next;
- /* String being hashed. */
- const char *string;
- /* Hash code. This is the full hash code, not the index into the
- table. */
- unsigned long hash;
- /* Pointer being stored in the hash table. */
- void *data;
- };
- /* A hash table. */
- struct hash_control {
- /* The hash array. */
- struct hash_entry **table;
- /* The number of slots in the hash table. */
- unsigned int size;
- /* An obstack for this hash table. */
- struct obstack memory;
- #ifdef HASH_STATISTICS
- /* Statistics. */
- unsigned long lookups;
- unsigned long hash_compares;
- unsigned long string_compares;
- unsigned long insertions;
- unsigned long replacements;
- unsigned long deletions;
- #endif /* HASH_STATISTICS */
- };
- /* The default number of entries to use when creating a hash table.
- Note this value can be reduced to 4051 by using the command line
- switch --reduce-memory-overheads, or set to other values by using
- the --hash-size=<NUMBER> switch. */
- static unsigned long gas_hash_table_size = 65537;
- void
- set_gas_hash_table_size (unsigned long size)
- {
- gas_hash_table_size = bfd_hash_set_default_size (size);
- }
- /* Create a hash table. This return a control block. */
- struct hash_control *
- hash_new_sized (unsigned long size)
- {
- unsigned long alloc;
- struct hash_control *ret;
- ret = (struct hash_control *) xmalloc (sizeof *ret);
- obstack_begin (&ret->memory, chunksize);
- alloc = size * sizeof (struct hash_entry *);
- ret->table = (struct hash_entry **) obstack_alloc (&ret->memory, alloc);
- memset (ret->table, 0, alloc);
- ret->size = size;
- #ifdef HASH_STATISTICS
- ret->lookups = 0;
- ret->hash_compares = 0;
- ret->string_compares = 0;
- ret->insertions = 0;
- ret->replacements = 0;
- ret->deletions = 0;
- #endif
- return ret;
- }
- struct hash_control *
- hash_new (void)
- {
- return hash_new_sized (gas_hash_table_size);
- }
- /* Delete a hash table, freeing all allocated memory. */
- void
- hash_die (struct hash_control *table)
- {
- obstack_free (&table->memory, 0);
- free (table);
- }
- /* Look up a string in a hash table. This returns a pointer to the
- hash_entry, or NULL if the string is not in the table. If PLIST is
- not NULL, this sets *PLIST to point to the start of the list which
- would hold this hash entry. If PHASH is not NULL, this sets *PHASH
- to the hash code for KEY.
- Each time we look up a string, we move it to the start of the list
- for its hash code, to take advantage of referential locality. */
- static struct hash_entry *
- hash_lookup (struct hash_control *table, const char *key, size_t len,
- struct hash_entry ***plist, unsigned long *phash)
- {
- unsigned long hash;
- size_t n;
- unsigned int c;
- unsigned int hindex;
- struct hash_entry **list;
- struct hash_entry *p;
- struct hash_entry *prev;
- #ifdef HASH_STATISTICS
- ++table->lookups;
- #endif
- hash = 0;
- for (n = 0; n < len; n++)
- {
- c = key[n];
- hash += c + (c << 17);
- hash ^= hash >> 2;
- }
- hash += len + (len << 17);
- hash ^= hash >> 2;
- if (phash != NULL)
- *phash = hash;
- hindex = hash % table->size;
- list = table->table + hindex;
- if (plist != NULL)
- *plist = list;
- prev = NULL;
- for (p = *list; p != NULL; p = p->next)
- {
- #ifdef HASH_STATISTICS
- ++table->hash_compares;
- #endif
- if (p->hash == hash)
- {
- #ifdef HASH_STATISTICS
- ++table->string_compares;
- #endif
- if (strncmp (p->string, key, len) == 0 && p->string[len] == '\0')
- {
- if (prev != NULL)
- {
- prev->next = p->next;
- p->next = *list;
- *list = p;
- }
- return p;
- }
- }
- prev = p;
- }
- return NULL;
- }
- /* Insert an entry into a hash table. This returns NULL on success.
- On error, it returns a printable string indicating the error. It
- is considered to be an error if the entry already exists in the
- hash table. */
- const char *
- hash_insert (struct hash_control *table, const char *key, void *val)
- {
- struct hash_entry *p;
- struct hash_entry **list;
- unsigned long hash;
- p = hash_lookup (table, key, strlen (key), &list, &hash);
- if (p != NULL)
- return "exists";
- #ifdef HASH_STATISTICS
- ++table->insertions;
- #endif
- p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p));
- p->string = key;
- p->hash = hash;
- p->data = val;
- p->next = *list;
- *list = p;
- return NULL;
- }
- /* Insert or replace an entry in a hash table. This returns NULL on
- success. On error, it returns a printable string indicating the
- error. If an entry already exists, its value is replaced. */
- const char *
- hash_jam (struct hash_control *table, const char *key, void *val)
- {
- struct hash_entry *p;
- struct hash_entry **list;
- unsigned long hash;
- p = hash_lookup (table, key, strlen (key), &list, &hash);
- if (p != NULL)
- {
- #ifdef HASH_STATISTICS
- ++table->replacements;
- #endif
- p->data = val;
- }
- else
- {
- #ifdef HASH_STATISTICS
- ++table->insertions;
- #endif
- p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p));
- p->string = key;
- p->hash = hash;
- p->data = val;
- p->next = *list;
- *list = p;
- }
- return NULL;
- }
- /* Replace an existing entry in a hash table. This returns the old
- value stored for the entry. If the entry is not found in the hash
- table, this does nothing and returns NULL. */
- void *
- hash_replace (struct hash_control *table, const char *key, void *value)
- {
- struct hash_entry *p;
- void *ret;
- p = hash_lookup (table, key, strlen (key), NULL, NULL);
- if (p == NULL)
- return NULL;
- #ifdef HASH_STATISTICS
- ++table->replacements;
- #endif
- ret = p->data;
- p->data = value;
- return ret;
- }
- /* Find an entry in a hash table, returning its value. Returns NULL
- if the entry is not found. */
- void *
- hash_find (struct hash_control *table, const char *key)
- {
- struct hash_entry *p;
- p = hash_lookup (table, key, strlen (key), NULL, NULL);
- if (p == NULL)
- return NULL;
- return p->data;
- }
- /* As hash_find, but KEY is of length LEN and is not guaranteed to be
- NUL-terminated. */
- void *
- hash_find_n (struct hash_control *table, const char *key, size_t len)
- {
- struct hash_entry *p;
- p = hash_lookup (table, key, len, NULL, NULL);
- if (p == NULL)
- return NULL;
- return p->data;
- }
- /* Delete an entry from a hash table. This returns the value stored
- for that entry, or NULL if there is no such entry. */
- void *
- hash_delete (struct hash_control *table, const char *key, int freeme)
- {
- struct hash_entry *p;
- struct hash_entry **list;
- p = hash_lookup (table, key, strlen (key), &list, NULL);
- if (p == NULL)
- return NULL;
- if (p != *list)
- abort ();
- #ifdef HASH_STATISTICS
- ++table->deletions;
- #endif
- *list = p->next;
- if (freeme)
- obstack_free (&table->memory, p);
- return p->data;
- }
- /* Traverse a hash table. Call the function on every entry in the
- hash table. */
- void
- hash_traverse (struct hash_control *table,
- void (*pfn) (const char *key, void *value))
- {
- unsigned int i;
- for (i = 0; i < table->size; ++i)
- {
- struct hash_entry *p;
- for (p = table->table[i]; p != NULL; p = p->next)
- (*pfn) (p->string, p->data);
- }
- }
- /* Print hash table statistics on the specified file. NAME is the
- name of the hash table, used for printing a header. */
- void
- hash_print_statistics (FILE *f ATTRIBUTE_UNUSED,
- const char *name ATTRIBUTE_UNUSED,
- struct hash_control *table ATTRIBUTE_UNUSED)
- {
- #ifdef HASH_STATISTICS
- unsigned int i;
- unsigned long total;
- unsigned long empty;
- fprintf (f, "%s hash statistics:\n", name);
- fprintf (f, "\t%lu lookups\n", table->lookups);
- fprintf (f, "\t%lu hash comparisons\n", table->hash_compares);
- fprintf (f, "\t%lu string comparisons\n", table->string_compares);
- fprintf (f, "\t%lu insertions\n", table->insertions);
- fprintf (f, "\t%lu replacements\n", table->replacements);
- fprintf (f, "\t%lu deletions\n", table->deletions);
- total = 0;
- empty = 0;
- for (i = 0; i < table->size; ++i)
- {
- struct hash_entry *p;
- if (table->table[i] == NULL)
- ++empty;
- else
- {
- for (p = table->table[i]; p != NULL; p = p->next)
- ++total;
- }
- }
- fprintf (f, "\t%g average chain length\n", (double) total / table->size);
- fprintf (f, "\t%lu empty slots\n", empty);
- #endif
- }
- #ifdef TEST
- /* This test program is left over from the old hash table code. */
- /* Number of hash tables to maintain (at once) in any testing. */
- #define TABLES (6)
- /* We can have 12 statistics. */
- #define STATBUFSIZE (12)
- /* Display statistics here. */
- int statbuf[STATBUFSIZE];
- /* Human farts here. */
- char answer[100];
- /* We test many hash tables at once. */
- char *hashtable[TABLES];
- /* Points to current hash_control. */
- char *h;
- char **pp;
- char *p;
- char *name;
- char *value;
- int size;
- int used;
- char command;
- /* Number 0:TABLES-1 of current hashed symbol table. */
- int number;
- int
- main ()
- {
- void applicatee ();
- void destroy ();
- char *what ();
- int *ip;
- number = 0;
- h = 0;
- printf ("type h <RETURN> for help\n");
- for (;;)
- {
- printf ("hash_test command: ");
- gets (answer);
- command = answer[0];
- command = TOLOWER (command); /* Ecch! */
- switch (command)
- {
- case '#':
- printf ("old hash table #=%d.\n", number);
- whattable ();
- break;
- case '?':
- for (pp = hashtable; pp < hashtable + TABLES; pp++)
- {
- printf ("address of hash table #%d control block is %xx\n",
- pp - hashtable, *pp);
- }
- break;
- case 'a':
- hash_traverse (h, applicatee);
- break;
- case 'd':
- hash_traverse (h, destroy);
- hash_die (h);
- break;
- case 'f':
- p = hash_find (h, name = what ("symbol"));
- printf ("value of \"%s\" is \"%s\"\n", name, p ? p : "NOT-PRESENT");
- break;
- case 'h':
- printf ("# show old, select new default hash table number\n");
- printf ("? display all hashtable control block addresses\n");
- printf ("a apply a simple display-er to each symbol in table\n");
- printf ("d die: destroy hashtable\n");
- printf ("f find value of nominated symbol\n");
- printf ("h this help\n");
- printf ("i insert value into symbol\n");
- printf ("j jam value into symbol\n");
- printf ("n new hashtable\n");
- printf ("r replace a value with another\n");
- printf ("s say what %% of table is used\n");
- printf ("q exit this program\n");
- printf ("x delete a symbol from table, report its value\n");
- break;
- case 'i':
- p = hash_insert (h, name = what ("symbol"), value = what ("value"));
- if (p)
- {
- printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value,
- p);
- }
- break;
- case 'j':
- p = hash_jam (h, name = what ("symbol"), value = what ("value"));
- if (p)
- {
- printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value, p);
- }
- break;
- case 'n':
- h = hashtable[number] = (char *) hash_new ();
- break;
- case 'q':
- exit (EXIT_SUCCESS);
- case 'r':
- p = hash_replace (h, name = what ("symbol"), value = what ("value"));
- printf ("old value was \"%s\"\n", p ? p : "{}");
- break;
- case 's':
- hash_say (h, statbuf, STATBUFSIZE);
- for (ip = statbuf; ip < statbuf + STATBUFSIZE; ip++)
- {
- printf ("%d ", *ip);
- }
- printf ("\n");
- break;
- case 'x':
- p = hash_delete (h, name = what ("symbol"));
- printf ("old value was \"%s\"\n", p ? p : "{}");
- break;
- default:
- printf ("I can't understand command \"%c\"\n", command);
- break;
- }
- }
- }
- char *
- what (description)
- char *description;
- {
- printf (" %s : ", description);
- gets (answer);
- return xstrdup (answer);
- }
- void
- destroy (string, value)
- char *string;
- char *value;
- {
- free (string);
- free (value);
- }
- void
- applicatee (string, value)
- char *string;
- char *value;
- {
- printf ("%.20s-%.20s\n", string, value);
- }
- /* Determine number: what hash table to use.
- Also determine h: points to hash_control. */
- void
- whattable ()
- {
- for (;;)
- {
- printf (" what hash table (%d:%d) ? ", 0, TABLES - 1);
- gets (answer);
- sscanf (answer, "%d", &number);
- if (number >= 0 && number < TABLES)
- {
- h = hashtable[number];
- if (!h)
- {
- printf ("warning: current hash-table-#%d. has no hash-control\n", number);
- }
- return;
- }
- else
- {
- printf ("invalid hash table number: %d\n", number);
- }
- }
- }
- #endif /* TEST */
|