Makefile.in (CFILES): Add hashtab.c
authorVladimir Makarov <vmakarov@loony.cygnus.com>
Fri, 15 Oct 1999 07:50:25 +0000 (07:50 +0000)
committerJeff Law <law@gcc.gnu.org>
Fri, 15 Oct 1999 07:50:25 +0000 (01:50 -0600)
        * Makefile.in (CFILES): Add hashtab.c
        (REQUIRED_OFILES): Add hashtab.o
        (hashtab.o): Add dependencies.
        * hashtab.c: New file

From-SVN: r30012

libiberty/ChangeLog
libiberty/Makefile.in
libiberty/hashtab.c [new file with mode: 0644]

index fcbc104b11ad4eb5a9dc15de450e9d547f33a559..167e4630318579f94fd50b2ce72ecc3b6e6a9077 100644 (file)
@@ -1,3 +1,10 @@
+Fri Oct 15 01:47:51 1999  Vladimir Makarov  <vmakarov@loony.cygnus.com>
+
+       * Makefile.in (CFILES): Add hashtab.c
+       (REQUIRED_OFILES): Add hashtab.o
+       (hashtab.o): Add dependencies.
+       * hashtab.c: New file
+
 Wed Oct 13 01:16:47 1999  Mumit Khan  <khan@xraylith.wisc.edu>
 
        * basename.c (DIR_SEPARATOR): New macro.
index adaf8a6a98c286bb161e3354749ef708698ac3a2..a42ea625ce51a755324fac9d8679a38e66b5e002 100644 (file)
@@ -123,7 +123,7 @@ HFILES = alloca-conf.h
 CFILES = asprintf.c alloca.c argv.c atexit.c basename.c bcmp.c bcopy.c \
        bzero.c calloc.c choose-temp.c clock.c concat.c cplus-dem.c fdmatch.c \
        fnmatch.c getcwd.c getpwd.c getopt.c getopt1.c getpagesize.c \
-       getruntime.c floatformat.c hex.c index.c insque.c memchr.c \
+       getruntime.c floatformat.c hashtab.c hex.c index.c insque.c memchr.c \
        memcmp.c memcpy.c memmove.c memset.c mkstemps.c objalloc.c obstack.c \
        pexecute.c putenv.c random.c rename.c rindex.c setenv.c sigsetmask.c \
        spaces.c splay-tree.c strcasecmp.c strncasecmp.c strchr.c strdup.c \
@@ -133,8 +133,8 @@ CFILES = asprintf.c alloca.c argv.c atexit.c basename.c bcmp.c bcopy.c \
 
 # These are always included in the library.
 REQUIRED_OFILES = argv.o choose-temp.o concat.o cplus-dem.o \
-  fdmatch.o fnmatch.o getopt.o getopt1.o getpwd.o getruntime.o hex.o \
-  floatformat.o objalloc.o obstack.o pexecute.o spaces.o \
+  fdmatch.o fnmatch.o getopt.o getopt1.o getpwd.o getruntime.o hashtab.o \
+  hex.o floatformat.o objalloc.o obstack.o pexecute.o spaces.o \
   splay-tree.o strerror.o strsignal.o xatexit.o xexit.o xmalloc.o \
   xmemdup.o xstrdup.o xstrerror.o
 
@@ -273,3 +273,4 @@ xmalloc.o: $(INCDIR)/libiberty.h
 xmemdup.o: config.h $(INCDIR)/libiberty.h
 xstrdup.o: config.h $(INCDIR)/libiberty.h
 xstrerror.o: config.h $(INCDIR)/libiberty.h
+hashtab.o: config.h $(INCDIR)/libiberty.h $(INCDIR)/hashtab.h $(INCDIR)/ansidecl.h
diff --git a/libiberty/hashtab.c b/libiberty/hashtab.c
new file mode 100644 (file)
index 0000000..8137d77
--- /dev/null
@@ -0,0 +1,291 @@
+/* An expandable hash tables datatype.  
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   Contributed by Vladimir Makarov (vmakarov@cygnus.com).
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* This package implements basic hash table functionality.  It is possible
+   to search for an entry, create an entry and destroy an entry.
+
+   Elements in the table are generic pointers.
+
+   The size of the table is not fixed; if the occupancy of the table
+   grows too high the hash table will be expanded.
+
+   The abstract data implementation is based on generalized Algorithm D
+   from Knuth's book "The art of computer programming".  Hash table is
+   expanded by creation of new hash table and transferring elements from
+   the old table to the new table. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#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;
+
+/* The following variable is used for debugging. Its value is number
+   of all table expansions fixed for time of work with all hash
+   tables. */
+
+static int all_expansions = 0;
+
+/* This macro defines reserved value for empty table entry. */
+
+#define EMPTY_ENTRY    NULL
+
+/* This macro defines reserved value for table entry which contained
+   a deleted element. */
+
+#define DELETED_ENTRY  ((void *) 1)
+
+/* The following function returns the nearest prime number which is
+   greater than given source number. */
+
+static unsigned long
+higher_prime_number (number)
+     unsigned long number;
+{
+  unsigned long i;
+
+  for (number = (number / 2) * 2 + 3;; number += 2)
+    {
+      for (i = 3; i * i <= number; i += 2)
+        if (number % i == 0)
+          break;
+      if (i * i > number)
+        return number;
+    }
+}
+
+/* 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. */
+
+hash_table_t
+create_hash_table (size, hash_function, eq_function)
+     size_t size;
+     unsigned (*hash_function) PARAMS ((hash_table_entry_t));
+     int (*eq_function) PARAMS ((hash_table_entry_t, hash_table_entry_t));
+{
+  hash_table_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->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));
+  return result;
+}
+
+/* This function frees all memory allocated for given hash table.
+   Naturally the hash table must already exist. */
+
+void
+delete_hash_table (htab)
+     hash_table_t htab;
+{
+  free (htab->entries);
+  free (htab);
+}
+
+/* This function clears all entries in the given hash table.  */
+
+void
+empty_hash_table (htab)
+     hash_table_t htab;
+{
+  memset (htab->entries, 0, htab->size * sizeof (hash_table_entry_t));
+}
+
+/* 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. */
+
+static void
+expand_hash_table (htab)
+     hash_table_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);
+}
+
+/* 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;
+{
+  hash_table_entry_t *entry_ptr;
+  hash_table_entry_t *first_deleted_entry_ptr;
+  unsigned index, hash_value, secondary_hash_value;
+
+  if (htab->size * 3 <= htab->number_of_elements * 4)
+    {
+      all_expansions++;
+      expand_hash_table (htab);
+    }
+  hash_value = (*htab->hash_function) (element);
+  secondary_hash_value = 1 + hash_value % (htab->size - 2);
+  index = hash_value % htab->size;
+  htab->searches++;
+  all_searches++;
+  first_deleted_entry_ptr = NULL;
+  for (;;htab->collisions++, all_collisions++)
+    {
+      entry_ptr = htab->entries + index;
+      if (*entry_ptr == EMPTY_ENTRY)
+        {
+          if (reserve)
+           {
+             htab->number_of_elements++;
+             if (first_deleted_entry_ptr != NULL)
+               {
+                 entry_ptr = first_deleted_entry_ptr;
+                 *entry_ptr = DELETED_ENTRY;
+               }
+           }
+          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 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). */
+
+void
+remove_element_from_hash_table_entry (htab, element)
+     hash_table_t htab;
+     hash_table_entry_t element;
+{
+  hash_table_entry_t *entry_ptr;
+
+  entry_ptr = find_hash_table_entry (htab, element, 0);
+  *entry_ptr = DELETED_ENTRY;
+  htab->number_of_deleted_elements++;
+}
+
+/* The following function returns current size of given hash table. */
+
+size_t
+hash_table_size (htab)
+     hash_table_t htab;
+{
+  return htab->size;
+}
+
+/* The following function returns current number of elements in given
+   hash table. */
+
+size_t
+hash_table_elements_number (htab)
+     hash_table_t htab;
+{
+  return htab->number_of_elements - htab->number_of_deleted_elements;
+}
+
+/* The following function returns number of percents of fixed
+   collisions during all work with given hash table. */
+
+int
+hash_table_collisions (htab)
+     hash_table_t htab;
+{
+  int searches;
+
+  searches = htab->searches;
+  if (searches == 0)
+    searches++;
+  return htab->collisions * 100 / searches;
+}
+
+/* The following function returns number of percents of fixed
+   collisions during all work with all hash tables. */
+
+int
+all_hash_table_collisions ()
+{
+  int searches;
+
+  searches = all_searches;
+  if (searches == 0)
+    searches++;
+  return all_collisions * 100 / searches;
+}