csomh / source-git / rpm

Forked from source-git/rpm 4 years ago
Clone
2ff057
/* An expandable hash tables datatype.  
2ff057
   Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
2ff057
   Contributed by Vladimir Makarov (vmakarov@cygnus.com).
2ff057
2ff057
This file is part of the libiberty library.
2ff057
Libiberty is free software; you can redistribute it and/or
2ff057
modify it under the terms of the GNU Library General Public
2ff057
License as published by the Free Software Foundation; either
2ff057
version 2 of the License, or (at your option) any later version.
2ff057
2ff057
Libiberty is distributed in the hope that it will be useful,
2ff057
but WITHOUT ANY WARRANTY; without even the implied warranty of
2ff057
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
2ff057
Library General Public License for more details.
2ff057
2ff057
You should have received a copy of the GNU Library General Public
2ff057
License along with libiberty; see the file COPYING.LIB.  If
2ff057
not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
2ff057
Boston, MA 02111-1307, USA.  */
2ff057
2ff057
/* This package implements basic hash table functionality.  It is possible
2ff057
   to search for an entry, create an entry and destroy an entry.
2ff057
2ff057
   Elements in the table are generic pointers.
2ff057
2ff057
   The size of the table is not fixed; if the occupancy of the table
2ff057
   grows too high the hash table will be expanded.
2ff057
2ff057
   The abstract data implementation is based on generalized Algorithm D
2ff057
   from Knuth's book "The art of computer programming".  Hash table is
2ff057
   expanded by creation of new hash table and transferring elements from
2ff057
   the old table to the new table. */
2ff057
2ff057
#include <sys/types.h>
2ff057
#include <stdlib.h>
2ff057
#include <string.h>
2ff057
#include <stdio.h>
2ff057
#include "tools/hashtab.h"
2ff057
2ff057
/* This macro defines reserved value for empty table entry. */
2ff057
2ff057
#define EMPTY_ENTRY    ((void *) 0)
2ff057
2ff057
/* This macro defines reserved value for table entry which contained
2ff057
   a deleted element. */
2ff057
2ff057
#define DELETED_ENTRY  ((void *) 1)
2ff057
2ff057
static unsigned long higher_prime_number (unsigned long);
2ff057
static hashval_t hash_pointer (const void *);
2ff057
static int eq_pointer (const void *, const void *);
2ff057
static int htab_expand (htab_t);
2ff057
static void **find_empty_slot_for_expand  (htab_t, hashval_t);
2ff057
2ff057
/* At some point, we could make these be NULL, and modify the
2ff057
   hash-table routines to handle NULL specially; that would avoid
2ff057
   function-call overhead for the common case of hashing pointers.  */
2ff057
htab_hash htab_hash_pointer = hash_pointer;
2ff057
htab_eq htab_eq_pointer = eq_pointer;
2ff057
2ff057
/* The following function returns a nearest prime number which is
2ff057
   greater than N, and near a power of two. */
2ff057
2ff057
static unsigned long
2ff057
higher_prime_number (n)
2ff057
     unsigned long n;
2ff057
{
2ff057
  /* These are primes that are near, but slightly smaller than, a
2ff057
     power of two.  */
2ff057
  static unsigned long primes[] = {
2ff057
    (unsigned long) 2,
2ff057
    (unsigned long) 7,
2ff057
    (unsigned long) 13,
2ff057
    (unsigned long) 31,
2ff057
    (unsigned long) 61,
2ff057
    (unsigned long) 127,
2ff057
    (unsigned long) 251,
2ff057
    (unsigned long) 509,
2ff057
    (unsigned long) 1021,
2ff057
    (unsigned long) 2039,
2ff057
    (unsigned long) 4093,
2ff057
    (unsigned long) 8191,
2ff057
    (unsigned long) 16381,
2ff057
    (unsigned long) 32749,
2ff057
    (unsigned long) 65521,
2ff057
    (unsigned long) 131071,
2ff057
    (unsigned long) 262139,
2ff057
    (unsigned long) 524287,
2ff057
    (unsigned long) 1048573,
2ff057
    (unsigned long) 2097143,
2ff057
    (unsigned long) 4194301,
2ff057
    (unsigned long) 8388593,
2ff057
    (unsigned long) 16777213,
2ff057
    (unsigned long) 33554393,
2ff057
    (unsigned long) 67108859,
2ff057
    (unsigned long) 134217689,
2ff057
    (unsigned long) 268435399,
2ff057
    (unsigned long) 536870909,
2ff057
    (unsigned long) 1073741789,
2ff057
    (unsigned long) 2147483647,
2ff057
					/* 4294967291L */
2ff057
    ((unsigned long) 2147483647) + ((unsigned long) 2147483644),
2ff057
  };
2ff057
2ff057
  unsigned long* low = &primes[0];
2ff057
  unsigned long* high = &primes[sizeof(primes) / sizeof(primes[0])];
2ff057
2ff057
  while (low != high)
2ff057
    {
2ff057
      unsigned long* mid = low + (high - low) / 2;
2ff057
      if (n > *mid)
2ff057
	low = mid + 1;
2ff057
      else
2ff057
	high = mid;
2ff057
    }
2ff057
2ff057
  /* If we've run out of primes, abort.  */
2ff057
  if (n > *low)
2ff057
    {
2ff057
      fprintf (stderr, "Cannot find prime bigger than %lu\n", n);
2ff057
      abort ();
2ff057
    }
2ff057
2ff057
  return *low;
2ff057
}
2ff057
2ff057
/* Returns a hash code for P.  */
2ff057
2ff057
static hashval_t
2ff057
hash_pointer (p)
2ff057
     const void * p;
2ff057
{
2ff057
  return (hashval_t) ((long)p >> 3);
2ff057
}
2ff057
2ff057
/* Returns non-zero if P1 and P2 are equal.  */
2ff057
2ff057
static int
2ff057
eq_pointer (p1, p2)
2ff057
     const void * p1;
2ff057
     const void * p2;
2ff057
{
2ff057
  return p1 == p2;
2ff057
}
2ff057
2ff057
/* This function creates table with length slightly longer than given
2ff057
   source length.  The created hash table is initiated as empty (all the
2ff057
   hash table entries are EMPTY_ENTRY).  The function returns the created
2ff057
   hash table.  Memory allocation may fail; it may return NULL.  */
2ff057
2ff057
htab_t
2ff057
htab_try_create (size, hash_f, eq_f, del_f)
2ff057
     size_t size;
2ff057
     htab_hash hash_f;
2ff057
     htab_eq eq_f;
2ff057
     htab_del del_f;
2ff057
{
2ff057
  htab_t result;
2ff057
2ff057
  size = higher_prime_number (size);
2ff057
  result = (htab_t) calloc (1, sizeof (struct htab));
2ff057
  if (result == NULL)
2ff057
    return NULL;
2ff057
2ff057
  result->entries = (void **) calloc (size, sizeof (void *));
2ff057
  if (result->entries == NULL)
2ff057
    {
2ff057
      free (result);
2ff057
      return NULL;
2ff057
    }
2ff057
2ff057
  result->size = size;
2ff057
  result->hash_f = hash_f;
2ff057
  result->eq_f = eq_f;
2ff057
  result->del_f = del_f;
2ff057
  result->return_allocation_failure = 1;
2ff057
  return result;
2ff057
}
2ff057
2ff057
/* This function frees all memory allocated for given hash table.
2ff057
   Naturally the hash table must already exist. */
2ff057
2ff057
void
2ff057
htab_delete (htab)
2ff057
     htab_t htab;
2ff057
{
2ff057
  int i;
2ff057
2ff057
  if (htab->del_f)
2ff057
    for (i = htab->size - 1; i >= 0; i--)
2ff057
      if (htab->entries[i] != EMPTY_ENTRY
2ff057
	  && htab->entries[i] != DELETED_ENTRY)
2ff057
	(*htab->del_f) (htab->entries[i]);
2ff057
2ff057
  free (htab->entries);
2ff057
  free (htab);
2ff057
}
2ff057
2ff057
/* This function clears all entries in the given hash table.  */
2ff057
2ff057
void
2ff057
htab_empty (htab)
2ff057
     htab_t htab;
2ff057
{
2ff057
  int i;
2ff057
2ff057
  if (htab->del_f)
2ff057
    for (i = htab->size - 1; i >= 0; i--)
2ff057
      if (htab->entries[i] != EMPTY_ENTRY
2ff057
	  && htab->entries[i] != DELETED_ENTRY)
2ff057
	(*htab->del_f) (htab->entries[i]);
2ff057
2ff057
  memset (htab->entries, 0, htab->size * sizeof (void *));
2ff057
}
2ff057
2ff057
/* Similar to htab_find_slot, but without several unwanted side effects:
2ff057
    - Does not call htab->eq_f when it finds an existing entry.
2ff057
    - Does not change the count of elements/searches/collisions in the
2ff057
      hash table.
2ff057
   This function also assumes there are no deleted entries in the table.
2ff057
   HASH is the hash value for the element to be inserted.  */
2ff057
2ff057
static void **
2ff057
find_empty_slot_for_expand (htab, hash)
2ff057
     htab_t htab;
2ff057
     hashval_t hash;
2ff057
{
2ff057
  size_t size = htab->size;
2ff057
  hashval_t hash2 = 1 + hash % (size - 2);
2ff057
  unsigned int index = hash % size;
2ff057
2ff057
  for (;;)
2ff057
    {
2ff057
      void **slot = htab->entries + index;
2ff057
2ff057
      if (*slot == EMPTY_ENTRY)
2ff057
	return slot;
2ff057
      else if (*slot == DELETED_ENTRY)
2ff057
	abort ();
2ff057
2ff057
      index += hash2;
2ff057
      if (index >= size)
2ff057
	index -= size;
2ff057
    }
2ff057
}
2ff057
2ff057
/* The following function changes size of memory allocated for the
2ff057
   entries and repeatedly inserts the table elements.  The occupancy
2ff057
   of the table after the call will be about 50%.  Naturally the hash
2ff057
   table must already exist.  Remember also that the place of the
2ff057
   table entries is changed.  If memory allocation failures are allowed,
2ff057
   this function will return zero, indicating that the table could not be
2ff057
   expanded.  If all goes well, it will return a non-zero value.  */
2ff057
2ff057
static int
2ff057
htab_expand (htab)
2ff057
     htab_t htab;
2ff057
{
2ff057
  void **oentries;
2ff057
  void **olimit;
2ff057
  void **p;
2ff057
2ff057
  oentries = htab->entries;
2ff057
  olimit = oentries + htab->size;
2ff057
2ff057
  htab->size = higher_prime_number (htab->size * 2);
2ff057
2ff057
  if (htab->return_allocation_failure)
2ff057
    {
2ff057
      void **nentries = (void **) calloc (htab->size, sizeof (void **));
2ff057
      if (nentries == NULL)
2ff057
	return 0;
2ff057
      htab->entries = nentries;
2ff057
    }
2ff057
2ff057
  htab->n_elements -= htab->n_deleted;
2ff057
  htab->n_deleted = 0;
2ff057
2ff057
  p = oentries;
2ff057
  do
2ff057
    {
2ff057
      void * x = *p;
2ff057
2ff057
      if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
2ff057
	{
2ff057
	  void **q = find_empty_slot_for_expand (htab, (*htab->hash_f) (x));
2ff057
2ff057
	  *q = x;
2ff057
	}
2ff057
2ff057
      p++;
2ff057
    }
2ff057
  while (p < olimit);
2ff057
2ff057
  free (oentries);
2ff057
  return 1;
2ff057
}
2ff057
2ff057
/* This function searches for a hash table entry equal to the given
2ff057
   element.  It cannot be used to insert or delete an element.  */
2ff057
2ff057
void *
2ff057
htab_find_with_hash (htab, element, hash)
2ff057
     htab_t htab;
2ff057
     const void * element;
2ff057
     hashval_t hash;
2ff057
{
2ff057
  unsigned int index;
2ff057
  hashval_t hash2;
2ff057
  size_t size;
2ff057
  void * entry;
2ff057
2ff057
  htab->searches++;
2ff057
  size = htab->size;
2ff057
  index = hash % size;
2ff057
2ff057
  entry = htab->entries[index];
2ff057
  if (entry == EMPTY_ENTRY
2ff057
      || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element)))
2ff057
    return entry;
2ff057
2ff057
  hash2 = 1 + hash % (size - 2);
2ff057
2ff057
  for (;;)
2ff057
    {
2ff057
      htab->collisions++;
2ff057
      index += hash2;
2ff057
      if (index >= size)
2ff057
	index -= size;
2ff057
2ff057
      entry = htab->entries[index];
2ff057
      if (entry == EMPTY_ENTRY
2ff057
	  || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element)))
2ff057
	return entry;
2ff057
    }
2ff057
}
2ff057
2ff057
/* Like htab_find_slot_with_hash, but compute the hash value from the
2ff057
   element.  */
2ff057
2ff057
void *
2ff057
htab_find (htab, element)
2ff057
     htab_t htab;
2ff057
     const void * element;
2ff057
{
2ff057
  return htab_find_with_hash (htab, element, (*htab->hash_f) (element));
2ff057
}
2ff057
2ff057
/* This function searches for a hash table slot containing an entry
2ff057
   equal to the given element.  To delete an entry, call this with
2ff057
   INSERT = 0, then call htab_clear_slot on the slot returned (possibly
2ff057
   after doing some checks).  To insert an entry, call this with
2ff057
   INSERT = 1, then write the value you want into the returned slot.
2ff057
   When inserting an entry, NULL may be returned if memory allocation
2ff057
   fails.  */
2ff057
2ff057
void **
2ff057
htab_find_slot_with_hash (htab, element, hash, insert)
2ff057
     htab_t htab;
2ff057
     const void * element;
2ff057
     hashval_t hash;
2ff057
     enum insert_option insert;
2ff057
{
2ff057
  void **first_deleted_slot;
2ff057
  unsigned int index;
2ff057
  hashval_t hash2;
2ff057
  size_t size;
2ff057
2ff057
  if (insert == INSERT && htab->size * 3 <= htab->n_elements * 4
2ff057
      && htab_expand (htab) == 0)
2ff057
    return NULL;
2ff057
2ff057
  size = htab->size;
2ff057
  hash2 = 1 + hash % (size - 2);
2ff057
  index = hash % size;
2ff057
2ff057
  htab->searches++;
2ff057
  first_deleted_slot = NULL;
2ff057
2ff057
  for (;;)
2ff057
    {
2ff057
      void * entry = htab->entries[index];
2ff057
      if (entry == EMPTY_ENTRY)
2ff057
	{
2ff057
	  if (insert == NO_INSERT)
2ff057
	    return NULL;
2ff057
2ff057
	  htab->n_elements++;
2ff057
2ff057
	  if (first_deleted_slot)
2ff057
	    {
2ff057
	      *first_deleted_slot = EMPTY_ENTRY;
2ff057
	      return first_deleted_slot;
2ff057
	    }
2ff057
2ff057
	  return &htab->entries[index];
2ff057
	}
2ff057
2ff057
      if (entry == DELETED_ENTRY)
2ff057
	{
2ff057
	  if (!first_deleted_slot)
2ff057
	    first_deleted_slot = &htab->entries[index];
2ff057
	}
2ff057
      else  if ((*htab->eq_f) (entry, element))
2ff057
	return &htab->entries[index];
2ff057
      
2ff057
      htab->collisions++;
2ff057
      index += hash2;
2ff057
      if (index >= size)
2ff057
	index -= size;
2ff057
    }
2ff057
}
2ff057
2ff057
/* Like htab_find_slot_with_hash, but compute the hash value from the
2ff057
   element.  */
2ff057
2ff057
void **
2ff057
htab_find_slot (htab, element, insert)
2ff057
     htab_t htab;
2ff057
     const void * element;
2ff057
     enum insert_option insert;
2ff057
{
2ff057
  return htab_find_slot_with_hash (htab, element, (*htab->hash_f) (element),
2ff057
				   insert);
2ff057
}
2ff057
2ff057
/* This function deletes an element with the given value from hash
2ff057
   table.  If there is no matching element in the hash table, this
2ff057
   function does nothing.  */
2ff057
2ff057
void
2ff057
htab_remove_elt (htab, element)
2ff057
     htab_t htab;
2ff057
     void * element;
2ff057
{
2ff057
  void **slot;
2ff057
2ff057
  slot = htab_find_slot (htab, element, NO_INSERT);
2ff057
  if (*slot == EMPTY_ENTRY)
2ff057
    return;
2ff057
2ff057
  if (htab->del_f)
2ff057
    (*htab->del_f) (*slot);
2ff057
2ff057
  *slot = DELETED_ENTRY;
2ff057
  htab->n_deleted++;
2ff057
}
2ff057
2ff057
/* This function clears a specified slot in a hash table.  It is
2ff057
   useful when you've already done the lookup and don't want to do it
2ff057
   again.  */
2ff057
2ff057
void
2ff057
htab_clear_slot (htab, slot)
2ff057
     htab_t htab;
2ff057
     void **slot;
2ff057
{
2ff057
  if (slot < htab->entries || slot >= htab->entries + htab->size
2ff057
      || *slot == EMPTY_ENTRY || *slot == DELETED_ENTRY)
2ff057
    abort ();
2ff057
2ff057
  if (htab->del_f)
2ff057
    (*htab->del_f) (*slot);
2ff057
2ff057
  *slot = DELETED_ENTRY;
2ff057
  htab->n_deleted++;
2ff057
}
2ff057
2ff057
/* This function scans over the entire hash table calling
2ff057
   CALLBACK for each live entry.  If CALLBACK returns false,
2ff057
   the iteration stops.  INFO is passed as CALLBACK's second
2ff057
   argument.  */
2ff057
2ff057
void
2ff057
htab_traverse (htab, callback, info)
2ff057
     htab_t htab;
2ff057
     htab_trav callback;
2ff057
     void * info;
2ff057
{
2ff057
  void **slot = htab->entries;
2ff057
  void **limit = slot + htab->size;
2ff057
2ff057
  do
2ff057
    {
2ff057
      void * x = *slot;
2ff057
2ff057
      if (x != EMPTY_ENTRY && x != DELETED_ENTRY)
2ff057
	if (!(*callback) (slot, info))
2ff057
	  break;
2ff057
    }
2ff057
  while (++slot < limit);
2ff057
}
2ff057
2ff057
/* Return the current size of given hash table. */
2ff057
2ff057
size_t
2ff057
htab_size (htab)
2ff057
     htab_t htab;
2ff057
{
2ff057
  return htab->size;
2ff057
}
2ff057
2ff057
/* Return the current number of elements in given hash table. */
2ff057
2ff057
size_t
2ff057
htab_elements (htab)
2ff057
     htab_t htab;
2ff057
{
2ff057
  return htab->n_elements - htab->n_deleted;
2ff057
}
2ff057
2ff057
/* Return the fraction of fixed collisions during all work with given
2ff057
   hash table. */
2ff057
2ff057
double
2ff057
htab_collisions (htab)
2ff057
     htab_t htab;
2ff057
{
2ff057
  if (htab->searches == 0)
2ff057
    return 0.0;
2ff057
2ff057
  return (double) htab->collisions / (double) htab->searches;
2ff057
}