OSDN Git Service

* ChangeLog: Remove piece of diff output.
[pf3gnuchains/gcc-fork.git] / libiberty / hashtab.c
index ea9f9d3..36ad6e4 100644 (file)
@@ -1,5 +1,5 @@
 /* An expandable hash tables datatype.  
-   Copyright (C) 1999 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
    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
 
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
 #include <stdio.h>
 
 #include "libiberty.h"
@@ -48,59 +52,166 @@ Boston, MA 02111-1307, USA.  */
 
 /* 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. */
 
-#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 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
+   function-call overhead for the common case of hashing pointers.  */
+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 given source number. */
+/* 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;
 {
-  unsigned long i;
-
-  n |= 0x01;  /* Force N to be odd.  */
-  if (n < 9)
-    return n; /* All odd numbers < 9 are prime.  */
+  /* These are primes that are near, but slightly smaller than, a
+     power of two.  */
+  static const unsigned long primes[] = {
+    (unsigned long) 2,
+    (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;
+    }
 
- next:
-  n += 2;
-  i = 3;
-  do
+  /* If we've run out of primes, abort.  */
+  if (n > *low)
     {
-      if (n % i == 0)
-       goto next;
-      i += 2;
+      fprintf (stderr, "Cannot find prime bigger than %lu\n", n);
+      abort ();
     }
-  while ((i * i) <= n);
 
-  return n;
+  return *low;
+}
+
+/* Returns a hash code for P.  */
+
+static hashval_t
+hash_pointer (p)
+     const PTR p;
+{
+  return (hashval_t) ((long)p >> 3);
+}
+
+/* Returns non-zero if P1 and P2 are equal.  */
+
+static int
+eq_pointer (p1, p2)
+     const PTR p1;
+     const PTR p2;
+{
+  return 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
-   created hash table. */
+   created hash table.  Memory allocation must not fail.  */
 
 htab_t
-htab_create (size, hash_f, eq_f)
+htab_create (size, hash_f, eq_f, del_f)
      size_t size;
      htab_hash hash_f;
      htab_eq eq_f;
+     htab_del del_f;
 {
   htab_t result;
 
   size = higher_prime_number (size);
   result = (htab_t) xcalloc (1, sizeof (struct htab));
-  result->entries = (void **) xcalloc (size, sizeof (void *));
+  result->entries = (PTR *) xcalloc (size, sizeof (PTR));
   result->size = size;
   result->hash_f = hash_f;
   result->eq_f = eq_f;
+  result->del_f = del_f;
+  result->return_allocation_failure = 0;
+  return result;
+}
+
+/* This function creates table with length slightly longer than given
+   source length.  The created hash table is initiated as empty (all the
+   hash table entries are EMPTY_ENTRY).  The function returns the created
+   hash table.  Memory allocation may fail; it may return NULL.  */
+
+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;
+{
+  htab_t result;
+
+  size = higher_prime_number (size);
+  result = (htab_t) calloc (1, sizeof (struct htab));
+  if (result == NULL)
+    return NULL;
+
+  result->entries = (PTR *) calloc (size, sizeof (PTR));
+  if (result->entries == NULL)
+    {
+      free (result);
+      return NULL;
+    }
+
+  result->size = size;
+  result->hash_f = hash_f;
+  result->eq_f = eq_f;
+  result->del_f = del_f;
+  result->return_allocation_failure = 1;
   return result;
 }
 
@@ -111,6 +222,14 @@ void
 htab_delete (htab)
      htab_t htab;
 {
+  int i;
+
+  if (htab->del_f)
+    for (i = htab->size - 1; i >= 0; i--)
+      if (htab->entries[i] != EMPTY_ENTRY
+         && htab->entries[i] != DELETED_ENTRY)
+       (*htab->del_f) (htab->entries[i]);
+
   free (htab->entries);
   free (htab);
 }
@@ -121,28 +240,78 @@ void
 htab_empty (htab)
      htab_t htab;
 {
-  memset (htab->entries, 0, htab->size * sizeof (void *));
+  int i;
+
+  if (htab->del_f)
+    for (i = htab->size - 1; i >= 0; i--)
+      if (htab->entries[i] != EMPTY_ENTRY
+         && htab->entries[i] != DELETED_ENTRY)
+       (*htab->del_f) (htab->entries[i]);
+
+  memset (htab->entries, 0, htab->size * sizeof (PTR));
+}
+
+/* Similar to htab_find_slot, but without several unwanted side effects:
+    - Does not call htab->eq_f when it finds an existing entry.
+    - Does not change the count of elements/searches/collisions in the
+      hash table.
+   This function also assumes there are no deleted entries in the table.
+   HASH is the hash value for the element to be inserted.  */
+
+static PTR *
+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;
+
+  for (;;)
+    {
+      PTR *slot = htab->entries + index;
+
+      if (*slot == EMPTY_ENTRY)
+       return slot;
+      else if (*slot == DELETED_ENTRY)
+       abort ();
+
+      index += hash2;
+      if (index >= size)
+       index -= size;
+    }
 }
 
 /* The following function changes size of memory allocated for 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;
 {
-  void **oentries;
-  void **olimit;
-  void **p;
+  PTR *oentries;
+  PTR *olimit;
+  PTR *p;
 
   oentries = htab->entries;
   olimit = oentries + htab->size;
 
   htab->size = higher_prime_number (htab->size * 2);
-  htab->entries = xcalloc (htab->size, sizeof (void **));
+
+  if (htab->return_allocation_failure)
+    {
+      PTR *nentries = (PTR *) calloc (htab->size, sizeof (PTR *));
+      if (nentries == NULL)
+       return 0;
+      htab->entries = nentries;
+    }
+  else
+    htab->entries = (PTR *) xcalloc (htab->size, sizeof (PTR *));
 
   htab->n_elements -= htab->n_deleted;
   htab->n_deleted = 0;
@@ -150,71 +319,98 @@ htab_expand (htab)
   p = oentries;
   do
     {
-      void *x = *p;
+      PTR x = *p;
+
       if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
        {
-         void **q = htab_find_slot (htab, x, 1);
+         PTR *q = find_empty_slot_for_expand (htab, (*htab->hash_f) (x));
+
          *q = x;
        }
+
       p++;
     }
   while (p < olimit);
+
   free (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.  */
 
-void *
-htab_find (htab, element)
+PTR
+htab_find_with_hash (htab, element, hash)
      htab_t htab;
-     const void *element;
+     const PTR element;
+     hashval_t hash;
 {
-  unsigned int index, hash, hash2;
+  unsigned int index;
+  hashval_t hash2;
   size_t size;
+  PTR entry;
 
   htab->searches++;
   size = htab->size;
-  hash = (*htab->hash_f) (element);
-  hash2 = 1 + hash % (size - 2);
   index = hash % size;
 
+  entry = htab->entries[index];
+  if (entry == EMPTY_ENTRY
+      || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element)))
+    return entry;
+
+  hash2 = 1 + hash % (size - 2);
+
   for (;;)
     {
-      void *entry = htab->entries[index];
-      if (entry == EMPTY_ENTRY)
-       return NULL;
-      else if (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element))
-       return entry;
-
       htab->collisions++;
       index += hash2;
       if (index >= size)
        index -= size;
+
+      entry = htab->entries[index];
+      if (entry == EMPTY_ENTRY
+         || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element)))
+       return entry;
     }
 }
 
+/* Like htab_find_slot_with_hash, but compute the hash value from the
+   element.  */
+
+PTR
+htab_find (htab, element)
+     htab_t htab;
+     const PTR element;
+{
+  return htab_find_with_hash (htab, element, (*htab->hash_f) (element));
+}
+
 /* This function searches for a hash table slot containing an entry
    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 **
-htab_find_slot (htab, element, insert)
+PTR *
+htab_find_slot_with_hash (htab, element, hash, insert)
      htab_t htab;
-     const void *element;
-     int insert;
+     const PTR element;
+     hashval_t hash;
+     enum insert_option insert;
 {
-  void **first_deleted_slot;
-  unsigned int index, hash, hash2;
+  PTR *first_deleted_slot;
+  unsigned int index;
+  hashval_t hash2;
   size_t size;
 
-  if (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;
-  hash = (*htab->hash_f) (element);
   hash2 = 1 + hash % (size - 2);
   index = hash % size;
 
@@ -223,10 +419,10 @@ htab_find_slot (htab, element, insert)
 
   for (;;)
     {
-      void *entry = htab->entries[index];
+      PTR entry = htab->entries[index];
       if (entry == EMPTY_ENTRY)
        {
-         if (!insert)
+         if (insert == NO_INSERT)
            return NULL;
 
          htab->n_elements++;
@@ -245,11 +441,8 @@ htab_find_slot (htab, element, insert)
          if (!first_deleted_slot)
            first_deleted_slot = &htab->entries[index];
        }
-      else
-       {
-         if ((*htab->eq_f) (entry, element))
-           return &htab->entries[index];
-       }
+      else  if ((*htab->eq_f) (entry, element))
+       return &htab->entries[index];
       
       htab->collisions++;
       index += hash2;
@@ -258,6 +451,19 @@ htab_find_slot (htab, element, insert)
     }
 }
 
+/* Like htab_find_slot_with_hash, but compute the hash value from the
+   element.  */
+
+PTR *
+htab_find_slot (htab, element, insert)
+     htab_t htab;
+     const PTR element;
+     enum insert_option insert;
+{
+  return htab_find_slot_with_hash (htab, element, (*htab->hash_f) (element),
+                                  insert);
+}
+
 /* This function deletes an element with the given value from hash
    table.  If there is no matching element in the hash table, this
    function does nothing.  */
@@ -265,14 +471,17 @@ htab_find_slot (htab, element, insert)
 void
 htab_remove_elt (htab, element)
      htab_t htab;
-     void *element;
+     PTR element;
 {
-  void **slot;
+  PTR *slot;
 
-  slot = htab_find_slot (htab, element, 0);
+  slot = htab_find_slot (htab, element, NO_INSERT);
   if (*slot == EMPTY_ENTRY)
     return;
 
+  if (htab->del_f)
+    (*htab->del_f) (*slot);
+
   *slot = DELETED_ENTRY;
   htab->n_deleted++;
 }
@@ -284,11 +493,15 @@ htab_remove_elt (htab, element)
 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)
     abort ();
+
+  if (htab->del_f)
+    (*htab->del_f) (*slot);
+
   *slot = DELETED_ENTRY;
   htab->n_deleted++;
 }
@@ -302,22 +515,23 @@ void
 htab_traverse (htab, callback, info)
      htab_t htab;
      htab_trav callback;
-     void *info;
+     PTR info;
 {
-  void **slot, **limit;
-  slot = htab->entries;
-  limit = slot + htab->size;
+  PTR *slot = htab->entries;
+  PTR *limit = slot + htab->size;
+
   do
     {
-      void *x = *slot;
+      PTR x = *slot;
+
       if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
-       if (!(*callback) (x, info))
+       if (!(*callback) (slot, info))
          break;
     }
   while (++slot < limit);
 }
 
-/* The following function returns current size of given hash table. */
+/* Return the current size of given hash table. */
 
 size_t
 htab_size (htab)
@@ -326,8 +540,7 @@ htab_size (htab)
   return htab->size;
 }
 
-/* The following function returns current number of elements in given
-   hash table. */
+/* Return the current number of elements in given hash table. */
 
 size_t
 htab_elements (htab)
@@ -336,17 +549,54 @@ htab_elements (htab)
   return htab->n_elements - htab->n_deleted;
 }
 
-/* The following function returns number of percents of fixed
-   collisions during all work with given hash table. */
+/* Return the fraction of fixed collisions during all work with given
+   hash table. */
 
 double
 htab_collisions (htab)
      htab_t htab;
 {
-  int searches;
-
-  searches = htab->searches;
-  if (searches == 0)
+  if (htab->searches == 0)
     return 0.0;
-  return (double)htab->collisions / (double)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;
 }