/* hash.c -- hash table routines for BFD
- Copyright (C) 1993, 94 Free Software Foundation, Inc.
+ Copyright 1993, 1994, 1995, 1997, 1999, 2001, 2002, 2003
+ Free Software Foundation, Inc.
Written by Steve Chamberlain <sac@cygnus.com>
This file is part of BFD, the Binary File Descriptor library.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
-#include "obstack.h"
+#include "objalloc.h"
/*
SECTION
<<bfd_hash_table_init>> (if you know approximately how many
entries you will need, the function <<bfd_hash_table_init_n>>,
which takes a @var{size} argument, may be used).
- <<bfd_hash_table_init>> returns <<false>> if some sort of
+ <<bfd_hash_table_init>> returns <<FALSE>> if some sort of
error occurs.
@findex bfd_hash_newfunc
The function <<bfd_hash_table_init>> take as an argument a
function to use to create new entries. For a basic hash
table, use the function <<bfd_hash_newfunc>>. @xref{Deriving
- a New Hash Table Type} for why you would want to use a
+ a New Hash Table Type}, for why you would want to use a
different value for this argument.
@findex bfd_hash_allocate
- <<bfd_hash_table_init>> will create an obstack which will be
+ <<bfd_hash_table_init>> will create an objalloc which will be
used to allocate new entries. You may allocate memory on this
- obstack using <<bfd_hash_allocate>>.
+ objalloc using <<bfd_hash_allocate>>.
@findex bfd_hash_table_free
Use <<bfd_hash_table_free>> to free up all the memory that has
The function <<bfd_hash_lookup>> is used both to look up a
string in the hash table and to create a new entry.
- If the @var{create} argument is <<false>>, <<bfd_hash_lookup>>
+ If the @var{create} argument is <<FALSE>>, <<bfd_hash_lookup>>
will look up a string. If the string is found, it will
returns a pointer to a <<struct bfd_hash_entry>>. If the
string is not found in the table <<bfd_hash_lookup>> will
return <<NULL>>. You should not modify any of the fields in
the returns <<struct bfd_hash_entry>>.
- If the @var{create} argument is <<true>>, the string will be
+ If the @var{create} argument is <<TRUE>>, the string will be
entered into the hash table if it is not already there.
Either way a pointer to a <<struct bfd_hash_entry>> will be
returned, either to the existing structure or to a newly
created one. In this case, a <<NULL>> return means that an
error occurred.
- If the @var{create} argument is <<true>>, and a new entry is
+ If the @var{create} argument is <<TRUE>>, and a new entry is
created, the @var{copy} argument is used to decide whether to
- copy the string onto the hash table obstack or not. If
- @var{copy} is passed as <<false>>, you must be careful not to
+ copy the string onto the hash table objalloc or not. If
+ @var{copy} is passed as <<FALSE>>, you must be careful not to
deallocate or modify the string as long as the hash table
exists.
generic pointer passed to <<bfd_hash_traverse>>. The function
must return a <<boolean>> value, which indicates whether to
continue traversing the hash table. If the function returns
- <<false>>, <<bfd_hash_traverse>> will stop the traversal and
+ <<FALSE>>, <<bfd_hash_traverse>> will stop the traversal and
return immediately.
INODE
. {* Allocate the structure if it has not already been allocated by a
. derived class. *}
. if (ret == (@var{entry_type} *) NULL)
-. ret = ((@var{entry_type} *)
-. bfd_hash_allocate (table, sizeof (@var{entry_type})));
+. {
+. ret = ((@var{entry_type} *)
+. bfd_hash_allocate (table, sizeof (@var{entry_type})));
+. if (ret == (@var{entry_type} *) NULL)
+. return NULL;
+. }
.
. {* Call the allocation method of the base class. *}
. ret = ((@var{entry_type} *)
Write other derived routines
You will want to write other routines for your new hash table,
- as well.
+ as well.
You will want an initialization routine which calls the
initialization routine of the hash table you are deriving from
<<aout_link_hash_traverse>> in aoutx.h.
*/
-/* Obstack allocation and deallocation routines. */
-#define obstack_chunk_alloc malloc
-#define obstack_chunk_free free
-
/* The default number of entries to use when creating a hash table. */
#define DEFAULT_SIZE (4051)
/* Create a new hash table, given a number of entries. */
-boolean
+bfd_boolean
bfd_hash_table_init_n (table, newfunc, size)
struct bfd_hash_table *table;
struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *,
unsigned int alloc;
alloc = size * sizeof (struct bfd_hash_entry *);
- if (!obstack_begin (&table->memory, alloc))
+
+ table->memory = (PTR) objalloc_create ();
+ if (table->memory == NULL)
{
- bfd_error = no_memory;
- return false;
+ bfd_set_error (bfd_error_no_memory);
+ return FALSE;
}
table->table = ((struct bfd_hash_entry **)
- obstack_alloc (&table->memory, alloc));
- if (!table->table)
+ objalloc_alloc ((struct objalloc *) table->memory, alloc));
+ if (table->table == NULL)
{
- bfd_error = no_memory;
- return false;
+ bfd_set_error (bfd_error_no_memory);
+ return FALSE;
}
memset ((PTR) table->table, 0, alloc);
table->size = size;
table->newfunc = newfunc;
- return true;
+ return TRUE;
}
/* Create a new hash table with the default number of entries. */
-boolean
+bfd_boolean
bfd_hash_table_init (table, newfunc)
struct bfd_hash_table *table;
struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *,
bfd_hash_table_free (table)
struct bfd_hash_table *table;
{
- obstack_free (&table->memory, (PTR) NULL);
+ objalloc_free ((struct objalloc *) table->memory);
+ table->memory = NULL;
}
/* Look up a string in a hash table. */
bfd_hash_lookup (table, string, create, copy)
struct bfd_hash_table *table;
const char *string;
- boolean create;
- boolean copy;
+ bfd_boolean create;
+ bfd_boolean copy;
{
register const unsigned char *s;
register unsigned long hash;
struct bfd_hash_entry *hashp;
unsigned int len;
unsigned int index;
-
+
hash = 0;
len = 0;
s = (const unsigned char *) string;
{
hash += c + (c << 17);
hash ^= hash >> 2;
- ++len;
}
+ len = (s - (const unsigned char *) string) - 1;
hash += len + (len << 17);
hash ^= hash >> 2;
{
char *new;
- new = (char *) obstack_alloc (&table->memory, len + 1);
+ new = (char *) objalloc_alloc ((struct objalloc *) table->memory,
+ len + 1);
if (!new)
{
- bfd_error = no_memory;
+ bfd_set_error (bfd_error_no_memory);
return (struct bfd_hash_entry *) NULL;
}
- strcpy (new, string);
+ memcpy (new, string, len + 1);
string = new;
}
hashp->string = string;
return hashp;
}
+/* Replace an entry in a hash table. */
+
+void
+bfd_hash_replace (table, old, nw)
+ struct bfd_hash_table *table;
+ struct bfd_hash_entry *old;
+ struct bfd_hash_entry *nw;
+{
+ unsigned int index;
+ struct bfd_hash_entry **pph;
+
+ index = old->hash % table->size;
+ for (pph = &table->table[index];
+ (*pph) != (struct bfd_hash_entry *) NULL;
+ pph = &(*pph)->next)
+ {
+ if (*pph == old)
+ {
+ *pph = nw;
+ return;
+ }
+ }
+
+ abort ();
+}
+
/* Base method for creating a new hash table entry. */
-/*ARGSUSED*/
struct bfd_hash_entry *
bfd_hash_newfunc (entry, table, string)
struct bfd_hash_entry *entry;
struct bfd_hash_table *table;
- const char *string;
+ const char *string ATTRIBUTE_UNUSED;
{
if (entry == (struct bfd_hash_entry *) NULL)
entry = ((struct bfd_hash_entry *)
struct bfd_hash_table *table;
unsigned int size;
{
- return obstack_alloc (&table->memory, size);
+ PTR ret;
+
+ ret = objalloc_alloc ((struct objalloc *) table->memory, size);
+ if (ret == NULL && size != 0)
+ bfd_set_error (bfd_error_no_memory);
+ return ret;
}
/* Traverse a hash table. */
void
bfd_hash_traverse (table, func, info)
struct bfd_hash_table *table;
- boolean (*func) PARAMS ((struct bfd_hash_entry *, PTR));
+ bfd_boolean (*func) PARAMS ((struct bfd_hash_entry *, PTR));
PTR info;
{
unsigned int i;
}
}
}
+\f
+/* A few different object file formats (a.out, COFF, ELF) use a string
+ table. These functions support adding strings to a string table,
+ returning the byte offset, and writing out the table.
+
+ Possible improvements:
+ + look for strings matching trailing substrings of other strings
+ + better data structures? balanced trees?
+ + look at reducing memory use elsewhere -- maybe if we didn't have
+ to construct the entire symbol table at once, we could get by
+ with smaller amounts of VM? (What effect does that have on the
+ string table reductions?) */
+
+/* An entry in the strtab hash table. */
+
+struct strtab_hash_entry
+{
+ struct bfd_hash_entry root;
+ /* Index in string table. */
+ bfd_size_type index;
+ /* Next string in strtab. */
+ struct strtab_hash_entry *next;
+};
+
+/* The strtab hash table. */
+
+struct bfd_strtab_hash
+{
+ struct bfd_hash_table table;
+ /* Size of strtab--also next available index. */
+ bfd_size_type size;
+ /* First string in strtab. */
+ struct strtab_hash_entry *first;
+ /* Last string in strtab. */
+ struct strtab_hash_entry *last;
+ /* Whether to precede strings with a two byte length, as in the
+ XCOFF .debug section. */
+ bfd_boolean xcoff;
+};
+
+static struct bfd_hash_entry *strtab_hash_newfunc
+ PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
+
+/* Routine to create an entry in a strtab. */
+
+static struct bfd_hash_entry *
+strtab_hash_newfunc (entry, table, string)
+ struct bfd_hash_entry *entry;
+ struct bfd_hash_table *table;
+ const char *string;
+{
+ struct strtab_hash_entry *ret = (struct strtab_hash_entry *) entry;
+
+ /* Allocate the structure if it has not already been allocated by a
+ subclass. */
+ if (ret == (struct strtab_hash_entry *) NULL)
+ ret = ((struct strtab_hash_entry *)
+ bfd_hash_allocate (table, sizeof (struct strtab_hash_entry)));
+ if (ret == (struct strtab_hash_entry *) NULL)
+ return NULL;
+
+ /* Call the allocation method of the superclass. */
+ ret = ((struct strtab_hash_entry *)
+ bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
+
+ if (ret)
+ {
+ /* Initialize the local fields. */
+ ret->index = (bfd_size_type) -1;
+ ret->next = NULL;
+ }
+
+ return (struct bfd_hash_entry *) ret;
+}
+
+/* Look up an entry in an strtab. */
+
+#define strtab_hash_lookup(t, string, create, copy) \
+ ((struct strtab_hash_entry *) \
+ bfd_hash_lookup (&(t)->table, (string), (create), (copy)))
+
+/* Create a new strtab. */
+
+struct bfd_strtab_hash *
+_bfd_stringtab_init ()
+{
+ struct bfd_strtab_hash *table;
+ bfd_size_type amt = sizeof (struct bfd_strtab_hash);
+
+ table = (struct bfd_strtab_hash *) bfd_malloc (amt);
+ if (table == NULL)
+ return NULL;
+
+ if (! bfd_hash_table_init (&table->table, strtab_hash_newfunc))
+ {
+ free (table);
+ return NULL;
+ }
+
+ table->size = 0;
+ table->first = NULL;
+ table->last = NULL;
+ table->xcoff = FALSE;
+
+ return table;
+}
+
+/* Create a new strtab in which the strings are output in the format
+ used in the XCOFF .debug section: a two byte length precedes each
+ string. */
+
+struct bfd_strtab_hash *
+_bfd_xcoff_stringtab_init ()
+{
+ struct bfd_strtab_hash *ret;
+
+ ret = _bfd_stringtab_init ();
+ if (ret != NULL)
+ ret->xcoff = TRUE;
+ return ret;
+}
+
+/* Free a strtab. */
+
+void
+_bfd_stringtab_free (table)
+ struct bfd_strtab_hash *table;
+{
+ bfd_hash_table_free (&table->table);
+ free (table);
+}
+
+/* Get the index of a string in a strtab, adding it if it is not
+ already present. If HASH is FALSE, we don't really use the hash
+ table, and we don't eliminate duplicate strings. */
+
+bfd_size_type
+_bfd_stringtab_add (tab, str, hash, copy)
+ struct bfd_strtab_hash *tab;
+ const char *str;
+ bfd_boolean hash;
+ bfd_boolean copy;
+{
+ register struct strtab_hash_entry *entry;
+
+ if (hash)
+ {
+ entry = strtab_hash_lookup (tab, str, TRUE, copy);
+ if (entry == NULL)
+ return (bfd_size_type) -1;
+ }
+ else
+ {
+ entry = ((struct strtab_hash_entry *)
+ bfd_hash_allocate (&tab->table,
+ sizeof (struct strtab_hash_entry)));
+ if (entry == NULL)
+ return (bfd_size_type) -1;
+ if (! copy)
+ entry->root.string = str;
+ else
+ {
+ char *n;
+
+ n = (char *) bfd_hash_allocate (&tab->table, strlen (str) + 1);
+ if (n == NULL)
+ return (bfd_size_type) -1;
+ entry->root.string = n;
+ }
+ entry->index = (bfd_size_type) -1;
+ entry->next = NULL;
+ }
+
+ if (entry->index == (bfd_size_type) -1)
+ {
+ entry->index = tab->size;
+ tab->size += strlen (str) + 1;
+ if (tab->xcoff)
+ {
+ entry->index += 2;
+ tab->size += 2;
+ }
+ if (tab->first == NULL)
+ tab->first = entry;
+ else
+ tab->last->next = entry;
+ tab->last = entry;
+ }
+
+ return entry->index;
+}
+
+/* Get the number of bytes in a strtab. */
+
+bfd_size_type
+_bfd_stringtab_size (tab)
+ struct bfd_strtab_hash *tab;
+{
+ return tab->size;
+}
+
+/* Write out a strtab. ABFD must already be at the right location in
+ the file. */
+
+bfd_boolean
+_bfd_stringtab_emit (abfd, tab)
+ register bfd *abfd;
+ struct bfd_strtab_hash *tab;
+{
+ register bfd_boolean xcoff;
+ register struct strtab_hash_entry *entry;
+
+ xcoff = tab->xcoff;
+
+ for (entry = tab->first; entry != NULL; entry = entry->next)
+ {
+ const char *str;
+ size_t len;
+
+ str = entry->root.string;
+ len = strlen (str) + 1;
+
+ if (xcoff)
+ {
+ bfd_byte buf[2];
+
+ /* The output length includes the null byte. */
+ bfd_put_16 (abfd, (bfd_vma) len, buf);
+ if (bfd_bwrite ((PTR) buf, (bfd_size_type) 2, abfd) != 2)
+ return FALSE;
+ }
+
+ if (bfd_bwrite ((PTR) str, (bfd_size_type) len, abfd) != len)
+ return FALSE;
+ }
+
+ return TRUE;
+}