OSDN Git Service

* MAINTAINERS (Write After Approval): Upgrade myself from
[pf3gnuchains/gcc-fork.git] / libiberty / hashtab.c
index fa0ffac..32067af 100644 (file)
@@ -1,5 +1,5 @@
 /* An expandable hash tables datatype.  
 /* An expandable hash tables datatype.  
-   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
    Contributed by Vladimir Makarov (vmakarov@cygnus.com).
 
 This file is part of the libiberty library.
    Contributed by Vladimir Makarov (vmakarov@cygnus.com).
 
 This file is part of the libiberty library.
@@ -41,6 +41,10 @@ Boston, MA 02111-1307, USA.  */
 #include <stdlib.h>
 #endif
 
 #include <stdlib.h>
 #endif
 
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
 #include <stdio.h>
 
 #include "libiberty.h"
 #include <stdio.h>
 
 #include "libiberty.h"
@@ -48,16 +52,18 @@ Boston, MA 02111-1307, USA.  */
 
 /* This macro defines reserved value for empty table entry. */
 
 
 /* This macro defines reserved value for empty table entry. */
 
-#define EMPTY_ENTRY    ((void *) 0)
+#define EMPTY_ENTRY    ((PTR) 0)
 
 /* This macro defines reserved value for table entry which contained
    a deleted element. */
 
 
 /* This macro defines reserved value for table entry which contained
    a deleted element. */
 
-#define DELETED_ENTRY  ((void *) 1)
+#define DELETED_ENTRY  ((PTR) 1)
 
 static unsigned long higher_prime_number PARAMS ((unsigned long));
 static hashval_t hash_pointer PARAMS ((const void *));
 static int eq_pointer PARAMS ((const void *, const void *));
 
 static unsigned long higher_prime_number PARAMS ((unsigned long));
 static hashval_t hash_pointer PARAMS ((const void *));
 static int eq_pointer PARAMS ((const void *, const void *));
+static int htab_expand PARAMS ((htab_t));
+static PTR *find_empty_slot_for_expand  PARAMS ((htab_t, hashval_t));
 
 /* At some point, we could make these be NULL, and modify the
    hash-table routines to handle NULL specially; that would avoid
 
 /* At some point, we could make these be NULL, and modify the
    hash-table routines to handle NULL specially; that would avoid
@@ -65,52 +71,86 @@ static int eq_pointer PARAMS ((const void *, const void *));
 htab_hash htab_hash_pointer = hash_pointer;
 htab_eq htab_eq_pointer = eq_pointer;
 
 htab_hash htab_hash_pointer = hash_pointer;
 htab_eq htab_eq_pointer = eq_pointer;
 
-/* The following function returns the nearest prime number which is
-   greater than a given source number, N. */
+/* The following function returns a nearest prime number which is
+   greater than N, and near a power of two. */
 
 static unsigned long
 higher_prime_number (n)
      unsigned long n;
 {
 
 static unsigned long
 higher_prime_number (n)
      unsigned long n;
 {
-  unsigned long i;
-
-  /* Ensure we have a larger number and then force to odd.  */
-  n++;  
-  n |= 0x01; 
-
-  /* All odd numbers < 9 are prime.  */
-  if (n < 9)
-    return n;
-
-  /* Otherwise find the next prime using a sieve.  */
-
- next:
+  /* These are primes that are near, but slightly smaller than, a
+     power of two.  */
+  static const unsigned long primes[] = {
+    (unsigned long) 7,
+    (unsigned long) 13,
+    (unsigned long) 31,
+    (unsigned long) 61,
+    (unsigned long) 127,
+    (unsigned long) 251,
+    (unsigned long) 509,
+    (unsigned long) 1021,
+    (unsigned long) 2039,
+    (unsigned long) 4093,
+    (unsigned long) 8191,
+    (unsigned long) 16381,
+    (unsigned long) 32749,
+    (unsigned long) 65521,
+    (unsigned long) 131071,
+    (unsigned long) 262139,
+    (unsigned long) 524287,
+    (unsigned long) 1048573,
+    (unsigned long) 2097143,
+    (unsigned long) 4194301,
+    (unsigned long) 8388593,
+    (unsigned long) 16777213,
+    (unsigned long) 33554393,
+    (unsigned long) 67108859,
+    (unsigned long) 134217689,
+    (unsigned long) 268435399,
+    (unsigned long) 536870909,
+    (unsigned long) 1073741789,
+    (unsigned long) 2147483647,
+                                       /* 4294967291L */
+    ((unsigned long) 2147483647) + ((unsigned long) 2147483644),
+  };
+
+  const unsigned long *low = &primes[0];
+  const unsigned long *high = &primes[sizeof(primes) / sizeof(primes[0])];
+
+  while (low != high)
+    {
+      const unsigned long *mid = low + (high - low) / 2;
+      if (n > *mid)
+       low = mid + 1;
+      else
+       high = mid;
+    }
 
 
-  for (i = 3; i * i <= n; i += 2)
-    if (n % i == 0)
-      {
-        n += 2;
-        goto next;
-       }
+  /* If we've run out of primes, abort.  */
+  if (n > *low)
+    {
+      fprintf (stderr, "Cannot find prime bigger than %lu\n", n);
+      abort ();
+    }
 
 
-  return n;
+  return *low;
 }
 
 /* Returns a hash code for P.  */
 
 static hashval_t
 hash_pointer (p)
 }
 
 /* Returns a hash code for P.  */
 
 static hashval_t
 hash_pointer (p)
-     const void *p;
+     const PTR p;
 {
 {
-  return (hashval_t) p;
+  return (hashval_t) ((long)p >> 3);
 }
 
 /* Returns non-zero if P1 and P2 are equal.  */
 
 static int
 eq_pointer (p1, p2)
 }
 
 /* Returns non-zero if P1 and P2 are equal.  */
 
 static int
 eq_pointer (p1, p2)
-     const void *p1;
-     const void *p2;
+     const PTR p1;
+     const PTR p2;
 {
   return p1 == p2;
 }
 {
   return p1 == p2;
 }
@@ -118,27 +158,119 @@ eq_pointer (p1, p2)
 /* This function creates table with length slightly longer than given
    source length.  Created hash table is initiated as empty (all the
    hash table entries are EMPTY_ENTRY).  The function returns the
 /* This function creates table with length slightly longer than given
    source length.  Created hash table is initiated as empty (all the
    hash table entries are EMPTY_ENTRY).  The function returns the
-   created hash table. */
+   created hash table, or NULL if memory allocation fails.  */
 
 htab_t
 
 htab_t
-htab_create (size, hash_f, eq_f, del_f)
+htab_create_alloc (size, hash_f, eq_f, del_f, alloc_f, free_f)
      size_t size;
      htab_hash hash_f;
      htab_eq eq_f;
      htab_del del_f;
      size_t size;
      htab_hash hash_f;
      htab_eq eq_f;
      htab_del del_f;
+     htab_alloc alloc_f;
+     htab_free free_f;
 {
   htab_t result;
 
   size = higher_prime_number (size);
 {
   htab_t result;
 
   size = higher_prime_number (size);
-  result = (htab_t) xcalloc (1, sizeof (struct htab));
-  result->entries = (void **) xcalloc (size, sizeof (void *));
+  result = (htab_t) (*alloc_f) (1, sizeof (struct htab));
+  if (result == NULL)
+    return NULL;
+  result->entries = (PTR *) (*alloc_f) (size, sizeof (PTR));
+  if (result->entries == NULL)
+    {
+      if (free_f != NULL)
+       (*free_f) (result);
+      return NULL;
+    }
   result->size = size;
   result->hash_f = hash_f;
   result->eq_f = eq_f;
   result->del_f = del_f;
   result->size = size;
   result->hash_f = hash_f;
   result->eq_f = eq_f;
   result->del_f = del_f;
+  result->alloc_f = alloc_f;
+  result->free_f = free_f;
   return result;
 }
 
   return result;
 }
 
+/* As above, but use the variants of alloc_f and free_f which accept
+   an extra argument.  */
+
+htab_t
+htab_create_alloc_ex (size, hash_f, eq_f, del_f, alloc_arg, alloc_f,
+                     free_f)
+     size_t size;
+     htab_hash hash_f;
+     htab_eq eq_f;
+     htab_del del_f;
+     PTR alloc_arg;
+     htab_alloc_with_arg alloc_f;
+     htab_free_with_arg free_f;
+{
+  htab_t result;
+
+  size = higher_prime_number (size);
+  result = (htab_t) (*alloc_f) (alloc_arg, 1, sizeof (struct htab));
+  if (result == NULL)
+    return NULL;
+  result->entries = (PTR *) (*alloc_f) (alloc_arg, size, sizeof (PTR));
+  if (result->entries == NULL)
+    {
+      if (free_f != NULL)
+       (*free_f) (alloc_arg, result);
+      return NULL;
+    }
+  result->size = size;
+  result->hash_f = hash_f;
+  result->eq_f = eq_f;
+  result->del_f = del_f;
+  result->alloc_arg = alloc_arg;
+  result->alloc_with_arg_f = alloc_f;
+  result->free_with_arg_f = free_f;
+  return result;
+}
+
+/* Update the function pointers and allocation parameter in the htab_t.  */
+
+void
+htab_set_functions_ex (htab, hash_f, eq_f, del_f, alloc_arg, alloc_f, free_f)
+     htab_t htab;
+     htab_hash hash_f;
+     htab_eq eq_f;
+     htab_del del_f;
+     PTR alloc_arg;
+     htab_alloc_with_arg alloc_f;
+     htab_free_with_arg free_f;
+{
+  htab->hash_f = hash_f;
+  htab->eq_f = eq_f;
+  htab->del_f = del_f;
+  htab->alloc_arg = alloc_arg;
+  htab->alloc_with_arg_f = alloc_f;
+  htab->free_with_arg_f = free_f;
+}
+
+/* These functions exist solely for backward compatibility.  */
+
+#undef htab_create
+htab_t
+htab_create (size, hash_f, eq_f, del_f)
+     size_t size;
+     htab_hash hash_f;
+     htab_eq eq_f;
+     htab_del del_f;
+{
+  return htab_create_alloc (size, hash_f, eq_f, del_f, xcalloc, free);
+}
+
+htab_t
+htab_try_create (size, hash_f, eq_f, del_f)
+     size_t size;
+     htab_hash hash_f;
+     htab_eq eq_f;
+     htab_del del_f;
+{
+  return htab_create_alloc (size, hash_f, eq_f, del_f, calloc, free);
+}
+
 /* This function frees all memory allocated for given hash table.
    Naturally the hash table must already exist. */
 
 /* This function frees all memory allocated for given hash table.
    Naturally the hash table must already exist. */
 
@@ -154,8 +286,16 @@ htab_delete (htab)
          && htab->entries[i] != DELETED_ENTRY)
        (*htab->del_f) (htab->entries[i]);
 
          && htab->entries[i] != DELETED_ENTRY)
        (*htab->del_f) (htab->entries[i]);
 
-  free (htab->entries);
-  free (htab);
+  if (htab->free_f != NULL)
+    {
+      (*htab->free_f) (htab->entries);
+      (*htab->free_f) (htab);
+    }
+  else if (htab->free_with_arg_f != NULL)
+    {
+      (*htab->free_with_arg_f) (htab->alloc_arg, htab->entries);
+      (*htab->free_with_arg_f) (htab->alloc_arg, htab);
+    }
 }
 
 /* This function clears all entries in the given hash table.  */
 }
 
 /* This function clears all entries in the given hash table.  */
@@ -172,7 +312,7 @@ htab_empty (htab)
          && htab->entries[i] != DELETED_ENTRY)
        (*htab->del_f) (htab->entries[i]);
 
          && htab->entries[i] != DELETED_ENTRY)
        (*htab->del_f) (htab->entries[i]);
 
-  memset (htab->entries, 0, htab->size * sizeof (void *));
+  memset (htab->entries, 0, htab->size * sizeof (PTR));
 }
 
 /* Similar to htab_find_slot, but without several unwanted side effects:
 }
 
 /* Similar to htab_find_slot, but without several unwanted side effects:
@@ -182,27 +322,33 @@ htab_empty (htab)
    This function also assumes there are no deleted entries in the table.
    HASH is the hash value for the element to be inserted.  */
 
    This function also assumes there are no deleted entries in the table.
    HASH is the hash value for the element to be inserted.  */
 
-static void **
+static PTR *
 find_empty_slot_for_expand (htab, hash)
      htab_t htab;
      hashval_t hash;
 {
   size_t size = htab->size;
 find_empty_slot_for_expand (htab, hash)
      htab_t htab;
      hashval_t hash;
 {
   size_t size = htab->size;
-  hashval_t hash2 = 1 + hash % (size - 2);
   unsigned int index = hash % size;
   unsigned int index = hash % size;
+  PTR *slot = htab->entries + index;
+  hashval_t hash2;
+
+  if (*slot == EMPTY_ENTRY)
+    return slot;
+  else if (*slot == DELETED_ENTRY)
+    abort ();
 
 
+  hash2 = 1 + hash % (size - 2);
   for (;;)
     {
   for (;;)
     {
-      void **slot = htab->entries + index;
+      index += hash2;
+      if (index >= size)
+       index -= size;
 
 
+      slot = htab->entries + index;
       if (*slot == EMPTY_ENTRY)
        return slot;
       else if (*slot == DELETED_ENTRY)
        abort ();
       if (*slot == EMPTY_ENTRY)
        return slot;
       else if (*slot == DELETED_ENTRY)
        abort ();
-
-      index += hash2;
-      if (index >= size)
-       index -= size;
     }
 }
 
     }
 }
 
@@ -210,21 +356,41 @@ find_empty_slot_for_expand (htab, hash)
    entries and repeatedly inserts the table elements.  The occupancy
    of the table after the call will be about 50%.  Naturally the hash
    table must already exist.  Remember also that the place of the
    entries and repeatedly inserts the table elements.  The occupancy
    of the table after the call will be about 50%.  Naturally the hash
    table must already exist.  Remember also that the place of the
-   table entries is changed. */
+   table entries is changed.  If memory allocation failures are allowed,
+   this function will return zero, indicating that the table could not be
+   expanded.  If all goes well, it will return a non-zero value.  */
 
 
-static void
+static int
 htab_expand (htab)
      htab_t htab;
 {
 htab_expand (htab)
      htab_t htab;
 {
-  void **oentries;
-  void **olimit;
-  void **p;
+  PTR *oentries;
+  PTR *olimit;
+  PTR *p;
+  PTR *nentries;
+  size_t nsize;
 
   oentries = htab->entries;
   olimit = oentries + htab->size;
 
 
   oentries = htab->entries;
   olimit = oentries + htab->size;
 
-  htab->size = higher_prime_number (htab->size * 2);
-  htab->entries = (void **) xcalloc (htab->size, sizeof (void **));
+  /* Resize only when table after removal of unused elements is either
+     too full or too empty.  */
+  if ((htab->n_elements - htab->n_deleted) * 2 > htab->size
+      || ((htab->n_elements - htab->n_deleted) * 8 < htab->size
+         && htab->size > 32))
+    nsize = higher_prime_number ((htab->n_elements - htab->n_deleted) * 2);
+  else
+    nsize = htab->size;
+
+  if (htab->alloc_with_arg_f != NULL)
+    nentries = (PTR *) (*htab->alloc_with_arg_f) (htab->alloc_arg, nsize,
+                                                 sizeof (PTR *));
+  else
+    nentries = (PTR *) (*htab->alloc_f) (nsize, sizeof (PTR *));
+  if (nentries == NULL)
+    return 0;
+  htab->entries = nentries;
+  htab->size = nsize;
 
   htab->n_elements -= htab->n_deleted;
   htab->n_deleted = 0;
 
   htab->n_elements -= htab->n_deleted;
   htab->n_deleted = 0;
@@ -232,11 +398,11 @@ htab_expand (htab)
   p = oentries;
   do
     {
   p = oentries;
   do
     {
-      void *x = *p;
+      PTR x = *p;
 
       if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
        {
 
       if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
        {
-         void **q = find_empty_slot_for_expand (htab, (*htab->hash_f) (x));
+         PTR *q = find_empty_slot_for_expand (htab, (*htab->hash_f) (x));
 
          *q = x;
        }
 
          *q = x;
        }
@@ -245,22 +411,26 @@ htab_expand (htab)
     }
   while (p < olimit);
 
     }
   while (p < olimit);
 
-  free (oentries);
+  if (htab->free_f != NULL)
+    (*htab->free_f) (oentries);
+  else if (htab->free_with_arg_f != NULL)
+    (*htab->free_with_arg_f) (htab->alloc_arg, oentries);
+  return 1;
 }
 
 /* This function searches for a hash table entry equal to the given
    element.  It cannot be used to insert or delete an element.  */
 
 }
 
 /* This function searches for a hash table entry equal to the given
    element.  It cannot be used to insert or delete an element.  */
 
-void *
+PTR
 htab_find_with_hash (htab, element, hash)
      htab_t htab;
 htab_find_with_hash (htab, element, hash)
      htab_t htab;
-     const void *element;
+     const PTR element;
      hashval_t hash;
 {
   unsigned int index;
   hashval_t hash2;
   size_t size;
      hashval_t hash;
 {
   unsigned int index;
   hashval_t hash2;
   size_t size;
-  void *entry;
+  PTR entry;
 
   htab->searches++;
   size = htab->size;
 
   htab->searches++;
   size = htab->size;
@@ -290,10 +460,10 @@ htab_find_with_hash (htab, element, hash)
 /* Like htab_find_slot_with_hash, but compute the hash value from the
    element.  */
 
 /* Like htab_find_slot_with_hash, but compute the hash value from the
    element.  */
 
-void *
+PTR
 htab_find (htab, element)
      htab_t htab;
 htab_find (htab, element)
      htab_t htab;
-     const void *element;
+     const PTR element;
 {
   return htab_find_with_hash (htab, element, (*htab->hash_f) (element));
 }
 {
   return htab_find_with_hash (htab, element, (*htab->hash_f) (element));
 }
@@ -302,71 +472,83 @@ htab_find (htab, element)
    equal to the given element.  To delete an entry, call this with
    INSERT = 0, then call htab_clear_slot on the slot returned (possibly
    after doing some checks).  To insert an entry, call this with
    equal to the given element.  To delete an entry, call this with
    INSERT = 0, then call htab_clear_slot on the slot returned (possibly
    after doing some checks).  To insert an entry, call this with
-   INSERT = 1, then write the value you want into the returned slot.  */
+   INSERT = 1, then write the value you want into the returned slot.
+   When inserting an entry, NULL may be returned if memory allocation
+   fails.  */
 
 
-void **
+PTR *
 htab_find_slot_with_hash (htab, element, hash, insert)
      htab_t htab;
 htab_find_slot_with_hash (htab, element, hash, insert)
      htab_t htab;
-     const void *element;
+     const PTR element;
      hashval_t hash;
      enum insert_option insert;
 {
      hashval_t hash;
      enum insert_option insert;
 {
-  void **first_deleted_slot;
+  PTR *first_deleted_slot;
   unsigned int index;
   hashval_t hash2;
   size_t size;
   unsigned int index;
   hashval_t hash2;
   size_t size;
+  PTR entry;
 
 
-  if (insert == INSERT && htab->size * 3 <= htab->n_elements * 4)
-    htab_expand (htab);
+  if (insert == INSERT && htab->size * 3 <= htab->n_elements * 4
+      && htab_expand (htab) == 0)
+    return NULL;
 
   size = htab->size;
 
   size = htab->size;
-  hash2 = 1 + hash % (size - 2);
   index = hash % size;
 
   htab->searches++;
   first_deleted_slot = NULL;
 
   index = hash % size;
 
   htab->searches++;
   first_deleted_slot = NULL;
 
+  entry = htab->entries[index];
+  if (entry == EMPTY_ENTRY)
+    goto empty_entry;
+  else if (entry == DELETED_ENTRY)
+    first_deleted_slot = &htab->entries[index];
+  else if ((*htab->eq_f) (entry, element))
+    return &htab->entries[index];
+      
+  hash2 = 1 + hash % (size - 2);
   for (;;)
     {
   for (;;)
     {
-      void *entry = htab->entries[index];
+      htab->collisions++;
+      index += hash2;
+      if (index >= size)
+       index -= size;
+      
+      entry = htab->entries[index];
       if (entry == EMPTY_ENTRY)
       if (entry == EMPTY_ENTRY)
-       {
-         if (insert == NO_INSERT)
-           return NULL;
-
-         htab->n_elements++;
-
-         if (first_deleted_slot)
-           {
-             *first_deleted_slot = EMPTY_ENTRY;
-             return first_deleted_slot;
-           }
-
-         return &htab->entries[index];
-       }
-
-      if (entry == DELETED_ENTRY)
+       goto empty_entry;
+      else if (entry == DELETED_ENTRY)
        {
          if (!first_deleted_slot)
            first_deleted_slot = &htab->entries[index];
        }
        {
          if (!first_deleted_slot)
            first_deleted_slot = &htab->entries[index];
        }
-      else  if ((*htab->eq_f) (entry, element))
+      else if ((*htab->eq_f) (entry, element))
        return &htab->entries[index];
        return &htab->entries[index];
-      
-      htab->collisions++;
-      index += hash2;
-      if (index >= size)
-       index -= size;
     }
     }
+
+ empty_entry:
+  if (insert == NO_INSERT)
+    return NULL;
+
+  htab->n_elements++;
+
+  if (first_deleted_slot)
+    {
+      *first_deleted_slot = EMPTY_ENTRY;
+      return first_deleted_slot;
+    }
+
+  return &htab->entries[index];
 }
 
 /* Like htab_find_slot_with_hash, but compute the hash value from the
    element.  */
 
 }
 
 /* Like htab_find_slot_with_hash, but compute the hash value from the
    element.  */
 
-void **
+PTR *
 htab_find_slot (htab, element, insert)
      htab_t htab;
 htab_find_slot (htab, element, insert)
      htab_t htab;
-     const void *element;
+     const PTR element;
      enum insert_option insert;
 {
   return htab_find_slot_with_hash (htab, element, (*htab->hash_f) (element),
      enum insert_option insert;
 {
   return htab_find_slot_with_hash (htab, element, (*htab->hash_f) (element),
@@ -380,9 +562,9 @@ htab_find_slot (htab, element, insert)
 void
 htab_remove_elt (htab, element)
      htab_t htab;
 void
 htab_remove_elt (htab, element)
      htab_t htab;
-     void *element;
+     PTR element;
 {
 {
-  void **slot;
+  PTR *slot;
 
   slot = htab_find_slot (htab, element, NO_INSERT);
   if (*slot == EMPTY_ENTRY)
 
   slot = htab_find_slot (htab, element, NO_INSERT);
   if (*slot == EMPTY_ENTRY)
@@ -402,7 +584,7 @@ htab_remove_elt (htab, element)
 void
 htab_clear_slot (htab, slot)
      htab_t htab;
 void
 htab_clear_slot (htab, slot)
      htab_t htab;
-     void **slot;
+     PTR *slot;
 {
   if (slot < htab->entries || slot >= htab->entries + htab->size
       || *slot == EMPTY_ENTRY || *slot == DELETED_ENTRY)
 {
   if (slot < htab->entries || slot >= htab->entries + htab->size
       || *slot == EMPTY_ENTRY || *slot == DELETED_ENTRY)
@@ -421,17 +603,20 @@ htab_clear_slot (htab, slot)
    argument.  */
 
 void
    argument.  */
 
 void
-htab_traverse (htab, callback, info)
+htab_traverse_noresize (htab, callback, info)
      htab_t htab;
      htab_trav callback;
      htab_t htab;
      htab_trav callback;
-     void *info;
+     PTR info;
 {
 {
-  void **slot = htab->entries;
-  void **limit = slot + htab->size;
+  PTR *slot;
+  PTR *limit;
+
+  slot = htab->entries;
+  limit = slot + htab->size;
 
   do
     {
 
   do
     {
-      void *x = *slot;
+      PTR x = *slot;
 
       if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
        if (!(*callback) (slot, info))
 
       if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
        if (!(*callback) (slot, info))
@@ -440,6 +625,21 @@ htab_traverse (htab, callback, info)
   while (++slot < limit);
 }
 
   while (++slot < limit);
 }
 
+/* Like htab_traverse_noresize, but does resize the table when it is
+   too empty to improve effectivity of subsequent calls.  */
+
+void
+htab_traverse (htab, callback, info)
+     htab_t htab;
+     htab_trav callback;
+     PTR info;
+{
+  if ((htab->n_elements - htab->n_deleted) * 8 < htab->size)
+    htab_expand (htab);
+
+  htab_traverse_noresize (htab, callback, info);
+}
+
 /* Return the current size of given hash table. */
 
 size_t
 /* Return the current size of given hash table. */
 
 size_t
@@ -470,3 +670,42 @@ htab_collisions (htab)
 
   return (double) htab->collisions / (double) htab->searches;
 }
 
   return (double) htab->collisions / (double) htab->searches;
 }
+
+/* Hash P as a null-terminated string.
+
+   Copied from gcc/hashtable.c.  Zack had the following to say with respect
+   to applicability, though note that unlike hashtable.c, this hash table
+   implementation re-hashes rather than chain buckets.
+
+   http://gcc.gnu.org/ml/gcc-patches/2001-08/msg01021.html
+   From: Zack Weinberg <zackw@panix.com>
+   Date: Fri, 17 Aug 2001 02:15:56 -0400
+
+   I got it by extracting all the identifiers from all the source code
+   I had lying around in mid-1999, and testing many recurrences of
+   the form "H_n = H_{n-1} * K + c_n * L + M" where K, L, M were either
+   prime numbers or the appropriate identity.  This was the best one.
+   I don't remember exactly what constituted "best", except I was
+   looking at bucket-length distributions mostly.
+   
+   So it should be very good at hashing identifiers, but might not be
+   as good at arbitrary strings.
+   
+   I'll add that it thoroughly trounces the hash functions recommended
+   for this use at http://burtleburtle.net/bob/hash/index.html, both
+   on speed and bucket distribution.  I haven't tried it against the
+   function they just started using for Perl's hashes.  */
+
+hashval_t
+htab_hash_string (p)
+     const PTR p;
+{
+  const unsigned char *str = (const unsigned char *) p;
+  hashval_t r = 0;
+  unsigned char c;
+
+  while ((c = *str++) != 0)
+    r = r * 67 + c - 113;
+
+  return r;
+}