diff options
Diffstat (limited to 'binutils-2.25/gas/hash.c')
-rw-r--r-- | binutils-2.25/gas/hash.c | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/binutils-2.25/gas/hash.c b/binutils-2.25/gas/hash.c new file mode 100644 index 00000000..bae83867 --- /dev/null +++ b/binutils-2.25/gas/hash.c @@ -0,0 +1,597 @@ +/* hash.c -- gas hash table code + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, + 2000, 2001, 2002, 2003, 2005, 2007, 2008, 2009, 2011, 2013 + 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 */ |