OSDN Git Service

2001-09-20 Benjamin Kosnik <bkoz@redhat.com>
[pf3gnuchains/gcc-fork.git] / libiberty / hashtab.c
index 9fce8cc..89bfe08 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.
@@ -35,82 +35,183 @@ Boston, MA 02111-1307, USA.  */
 #include "config.h"
 #endif
 
+#include <sys/types.h>
+
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
 
-#include "libiberty.h"
-#include "hashtab.h"
-
-/* The following variable is used for debugging. Its value is number
-   of all calls of `find_hash_table_entry' for all hash tables. */
-
-static int all_searches = 0;
-
-/* The following variable is used for debugging. Its value is number
-   of collisions fixed for time of work with all hash tables. */
-
-static int all_collisions = 0;
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
 
-/* The following variable is used for debugging. Its value is number
-   of all table expansions fixed for time of work with all hash
-   tables. */
+#include <stdio.h>
 
-static int all_expansions = 0;
+#include "libiberty.h"
+#include "hashtab.h"
 
 /* This macro defines reserved value for empty table entry. */
 
-#define EMPTY_ENTRY    NULL
+#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)
 
-/* The following function returns the nearest prime number which is
-   greater than given source number. */
+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 a nearest prime number which is
+   greater than N, and near a power of two. */
 
 static unsigned long
-higher_prime_number (number)
-     unsigned long number;
+higher_prime_number (n)
+     unsigned long n;
 {
-  unsigned long i;
+  /* These are primes that are near, but slightly smaller than, a
+     power of two.  */
+  static 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),
+  };
+
+  unsigned long* low = &primes[0];
+  unsigned long* high = &primes[sizeof(primes) / sizeof(primes[0])];
+
+  while (low != high)
+    {
+      unsigned long* mid = low + (high - low) / 2;
+      if (n > *mid)
+       low = mid + 1;
+      else
+       high = mid;
+    }
 
-  for (number = (number / 2) * 2 + 3;; number += 2)
+  /* If we've run out of primes, abort.  */
+  if (n > *low)
     {
-      for (i = 3; i * i <= number; i += 2)
-        if (number % i == 0)
-          break;
-      if (i * i > number)
-        return number;
+      fprintf (stderr, "Cannot find prime bigger than %lu\n", n);
+      abort ();
     }
+
+  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, del_f)
+     size_t size;
+     htab_hash hash_f;
+     htab_eq eq_f;
+     htab_del del_f;
+{
+  htab_t result;
 
-hash_table_t
-create_hash_table (size, hash_function, eq_function)
+  size = higher_prime_number (size);
+  result = (htab_t) xcalloc (1, sizeof (struct htab));
+  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;
-     unsigned (*hash_function) PARAMS ((hash_table_entry_t));
-     int (*eq_function) PARAMS ((hash_table_entry_t, hash_table_entry_t));
+     htab_hash hash_f;
+     htab_eq eq_f;
+     htab_del del_f;
 {
-  hash_table_t result;
+  htab_t result;
 
   size = higher_prime_number (size);
-  result = (hash_table_t) xmalloc (sizeof (*result));
-  result->entries
-    = (hash_table_entry_t *) xmalloc (size * sizeof (hash_table_entry_t));
+  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_function = hash_function;
-  result->eq_function = eq_function;
-  result->number_of_elements = 0;
-  result->number_of_deleted_elements = 0;
-  result->searches = 0;
-  result->collisions = 0;
-  memset (result->entries, 0, size * sizeof (hash_table_entry_t));
+  result->hash_f = hash_f;
+  result->eq_f = eq_f;
+  result->del_f = del_f;
+  result->return_allocation_failure = 1;
   return result;
 }
 
@@ -118,9 +219,17 @@ create_hash_table (size, hash_function, eq_function)
    Naturally the hash table must already exist. */
 
 void
-delete_hash_table (htab)
-     hash_table_t htab;
+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);
 }
@@ -128,134 +237,273 @@ delete_hash_table (htab)
 /* This function clears all entries in the given hash table.  */
 
 void
-empty_hash_table (htab)
-     hash_table_t htab;
+htab_empty (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]);
+
+  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;
 {
-  memset (htab->entries, 0, htab->size * sizeof (hash_table_entry_t));
+  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
-expand_hash_table (htab)
-     hash_table_t htab;
+static int
+htab_expand (htab)
+     htab_t htab;
 {
-  hash_table_t new_htab;
-  hash_table_entry_t *entry_ptr;
-  hash_table_entry_t *new_entry_ptr;
-
-  new_htab = create_hash_table (htab->number_of_elements * 2,
-                                htab->hash_function, htab->eq_function);
-  for (entry_ptr = htab->entries; entry_ptr < htab->entries + htab->size;
-       entry_ptr++)
-    if (*entry_ptr != EMPTY_ENTRY && *entry_ptr != DELETED_ENTRY)
-      {
-        new_entry_ptr = find_hash_table_entry (new_htab, *entry_ptr, 1);
-        *new_entry_ptr = (*entry_ptr);
-      }
-  free (htab->entries);
-  *htab = (*new_htab);
-  free (new_htab);
+  PTR *oentries;
+  PTR *olimit;
+  PTR *p;
+
+  oentries = htab->entries;
+  olimit = oentries + htab->size;
+
+  htab->size = higher_prime_number (htab->size * 2);
+
+  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;
+
+  p = oentries;
+  do
+    {
+      PTR x = *p;
+
+      if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
+       {
+         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 hash table entry which contains element
-   equal to given value or empty entry in which given value can be
-   placed (if the element with given value does not exist in the
-   table).  The function works in two regimes.  The first regime is
-   used only for search.  The second is used for search and
-   reservation empty entry for given value.  The table is expanded if
-   occupancy (taking into accout also deleted elements) is more than
-   75%.  Naturally the hash table must already exist.  If reservation
-   flag is TRUE then the element with given value should be inserted
-   into the table entry before another call of
-   `find_hash_table_entry'. */
-
-hash_table_entry_t *
-find_hash_table_entry (htab, element, reserve)
-     hash_table_t htab;
-     hash_table_entry_t element;
-     int reserve;
+/* This function searches for a hash table entry equal to the given
+   element.  It cannot be used to insert or delete an element.  */
+
+PTR
+htab_find_with_hash (htab, element, hash)
+     htab_t htab;
+     const PTR element;
+     hashval_t hash;
 {
-  hash_table_entry_t *entry_ptr;
-  hash_table_entry_t *first_deleted_entry_ptr;
-  unsigned index, hash_value, secondary_hash_value;
+  unsigned int index;
+  hashval_t hash2;
+  size_t size;
+  PTR entry;
+
+  htab->searches++;
+  size = htab->size;
+  index = hash % size;
 
-  if (htab->size * 3 <= htab->number_of_elements * 4)
+  entry = htab->entries[index];
+  if (entry == EMPTY_ENTRY
+      || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element)))
+    return entry;
+
+  hash2 = 1 + hash % (size - 2);
+
+  for (;;)
     {
-      all_expansions++;
-      expand_hash_table (htab);
+      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;
     }
-  hash_value = (*htab->hash_function) (element);
-  secondary_hash_value = 1 + hash_value % (htab->size - 2);
-  index = hash_value % htab->size;
+}
+
+/* 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.
+   When inserting an entry, NULL may be returned if memory allocation
+   fails.  */
+
+PTR *
+htab_find_slot_with_hash (htab, element, hash, insert)
+     htab_t htab;
+     const PTR element;
+     hashval_t hash;
+     enum insert_option insert;
+{
+  PTR *first_deleted_slot;
+  unsigned int index;
+  hashval_t hash2;
+  size_t size;
+
+  if (insert == INSERT && htab->size * 3 <= htab->n_elements * 4
+      && htab_expand (htab) == 0)
+    return NULL;
+
+  size = htab->size;
+  hash2 = 1 + hash % (size - 2);
+  index = hash % size;
+
   htab->searches++;
-  all_searches++;
-  first_deleted_entry_ptr = NULL;
-  for (;;htab->collisions++, all_collisions++)
+  first_deleted_slot = NULL;
+
+  for (;;)
     {
-      entry_ptr = htab->entries + index;
-      if (*entry_ptr == EMPTY_ENTRY)
-        {
-          if (reserve)
+      PTR entry = htab->entries[index];
+      if (entry == EMPTY_ENTRY)
+       {
+         if (insert == NO_INSERT)
+           return NULL;
+
+         htab->n_elements++;
+
+         if (first_deleted_slot)
            {
-             htab->number_of_elements++;
-             if (first_deleted_entry_ptr != NULL)
-               {
-                 entry_ptr = first_deleted_entry_ptr;
-                 *entry_ptr = EMPTY_ENTRY;
-               }
+             *first_deleted_slot = EMPTY_ENTRY;
+             return first_deleted_slot;
            }
-          break;
-        }
-      else if (*entry_ptr != DELETED_ENTRY)
-        {
-          if ((*htab->eq_function) (*entry_ptr, element))
-            break;
-        }
-      else if (first_deleted_entry_ptr == NULL)
-       first_deleted_entry_ptr = entry_ptr;
-      index += secondary_hash_value;
-      if (index >= htab->size)
-        index -= htab->size;
+
+         return &htab->entries[index];
+       }
+
+      if (entry == DELETED_ENTRY)
+       {
+         if (!first_deleted_slot)
+           first_deleted_slot = &htab->entries[index];
+       }
+      else  if ((*htab->eq_f) (entry, element))
+       return &htab->entries[index];
+      
+      htab->collisions++;
+      index += hash2;
+      if (index >= size)
+       index -= size;
     }
-  return entry_ptr;
 }
 
-/* This function deletes element with given value from hash table.
-   The hash table entry value will be `DELETED_ENTRY' after the
-   function call.  Naturally the hash table must already exist.  Hash
-   table entry for given value should be not empty (or deleted). */
+/* 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.  */
 
 void
-remove_element_from_hash_table_entry (htab, element)
-     hash_table_t htab;
-     hash_table_entry_t element;
+htab_remove_elt (htab, element)
+     htab_t htab;
+     PTR element;
 {
-  hash_table_entry_t *entry_ptr;
+  PTR *slot;
+
+  slot = htab_find_slot (htab, element, NO_INSERT);
+  if (*slot == EMPTY_ENTRY)
+    return;
 
-  entry_ptr = find_hash_table_entry (htab, element, 0);
-  *entry_ptr = DELETED_ENTRY;
-  htab->number_of_deleted_elements++;
+  if (htab->del_f)
+    (*htab->del_f) (*slot);
+
+  *slot = DELETED_ENTRY;
+  htab->n_deleted++;
 }
 
-/* This function clears a specified slot in a hash table.
-   It is useful when you've already done the lookup and don't want to
-   do it again.  */
+/* This function clears a specified slot in a hash table.  It is
+   useful when you've already done the lookup and don't want to do it
+   again.  */
 
 void
-clear_hash_table_slot (htab, slot)
-     hash_table_t htab;
-     hash_table_entry_t *slot;
+htab_clear_slot (htab, slot)
+     htab_t htab;
+     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->number_of_deleted_elements++;
+  htab->n_deleted++;
 }
 
 /* This function scans over the entire hash table calling
@@ -264,63 +512,91 @@ clear_hash_table_slot (htab, slot)
    argument.  */
 
 void
-traverse_hash_table (htab, callback, info)
-     hash_table_t htab;
-     int (*callback) (hash_table_entry_t, void *);
-     void *info;
+htab_traverse (htab, callback, info)
+     htab_t htab;
+     htab_trav callback;
+     PTR info;
 {
-  hash_table_entry_t *entry_ptr;
-  for (entry_ptr = htab->entries; entry_ptr < htab->entries + htab->size;
-       entry_ptr++)
-    if (*entry_ptr != EMPTY_ENTRY && *entry_ptr != DELETED_ENTRY)
-      if (!callback (*entry_ptr, info))
-       break;
+  PTR *slot = htab->entries;
+  PTR *limit = slot + htab->size;
+
+  do
+    {
+      PTR x = *slot;
+
+      if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
+       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
-hash_table_size (htab)
-     hash_table_t htab;
+htab_size (htab)
+     htab_t 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
-hash_table_elements_number (htab)
-     hash_table_t htab;
+htab_elements (htab)
+     htab_t htab;
 {
-  return htab->number_of_elements - htab->number_of_deleted_elements;
+  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. */
 
-int
-hash_table_collisions (htab)
-     hash_table_t htab;
+double
+htab_collisions (htab)
+     htab_t htab;
 {
-  int searches;
+  if (htab->searches == 0)
+    return 0.0;
 
-  searches = htab->searches;
-  if (searches == 0)
-    searches++;
-  return htab->collisions * 100 / searches;
+  return (double) htab->collisions / (double) htab->searches;
 }
 
-/* The following function returns number of percents of fixed
-   collisions during all work with all hash tables. */
-
-int
-all_hash_table_collisions ()
+/* 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;
 {
-  int searches;
+  const unsigned char *str = (const unsigned char *) p;
+  hashval_t r = 0;
+  unsigned char c;
+
+  while ((c = *str++) != 0)
+    r = r * 67 + c - 113;
 
-  searches = all_searches;
-  if (searches == 0)
-    searches++;
-  return all_collisions * 100 / searches;
+  return r;
 }