Blame lib/hash.c

Packit Service fdd496
/* hash - hashing table processing.
Packit Service fdd496
Packit Service fdd496
   Copyright (C) 1998-2004, 2006-2007, 2009-2017 Free Software Foundation, Inc.
Packit Service fdd496
Packit Service fdd496
   Written by Jim Meyering, 1992.
Packit Service fdd496
Packit Service fdd496
   This program is free software: you can redistribute it and/or modify
Packit Service fdd496
   it under the terms of the GNU General Public License as published by
Packit Service fdd496
   the Free Software Foundation; either version 3 of the License, or
Packit Service fdd496
   (at your option) any later version.
Packit Service fdd496
Packit Service fdd496
   This program is distributed in the hope that it will be useful,
Packit Service fdd496
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service fdd496
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service fdd496
   GNU General Public License for more details.
Packit Service fdd496
Packit Service fdd496
   You should have received a copy of the GNU General Public License
Packit Service fdd496
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit Service fdd496
Packit Service fdd496
/* A generic hash table package.  */
Packit Service fdd496
Packit Service fdd496
/* Define USE_OBSTACK to 1 if you want the allocator to use obstacks instead
Packit Service fdd496
   of malloc.  If you change USE_OBSTACK, you have to recompile!  */
Packit Service fdd496
Packit Service fdd496
#include <config.h>
Packit Service fdd496
Packit Service fdd496
#include "hash.h"
Packit Service fdd496
Packit Service fdd496
#include "bitrotate.h"
Packit Service fdd496
#include "xalloc-oversized.h"
Packit Service fdd496
Packit Service fdd496
#include <stdint.h>
Packit Service fdd496
#include <stdio.h>
Packit Service fdd496
#include <stdlib.h>
Packit Service fdd496
Packit Service fdd496
#if USE_OBSTACK
Packit Service fdd496
# include "obstack.h"
Packit Service fdd496
# ifndef obstack_chunk_alloc
Packit Service fdd496
#  define obstack_chunk_alloc malloc
Packit Service fdd496
# endif
Packit Service fdd496
# ifndef obstack_chunk_free
Packit Service fdd496
#  define obstack_chunk_free free
Packit Service fdd496
# endif
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
struct hash_entry
Packit Service fdd496
  {
Packit Service fdd496
    void *data;
Packit Service fdd496
    struct hash_entry *next;
Packit Service fdd496
  };
Packit Service fdd496
Packit Service fdd496
struct hash_table
Packit Service fdd496
  {
Packit Service fdd496
    /* The array of buckets starts at BUCKET and extends to BUCKET_LIMIT-1,
Packit Service fdd496
       for a possibility of N_BUCKETS.  Among those, N_BUCKETS_USED buckets
Packit Service fdd496
       are not empty, there are N_ENTRIES active entries in the table.  */
Packit Service fdd496
    struct hash_entry *bucket;
Packit Service fdd496
    struct hash_entry const *bucket_limit;
Packit Service fdd496
    size_t n_buckets;
Packit Service fdd496
    size_t n_buckets_used;
Packit Service fdd496
    size_t n_entries;
Packit Service fdd496
Packit Service fdd496
    /* Tuning arguments, kept in a physically separate structure.  */
Packit Service fdd496
    const Hash_tuning *tuning;
Packit Service fdd496
Packit Service fdd496
    /* Three functions are given to 'hash_initialize', see the documentation
Packit Service fdd496
       block for this function.  In a word, HASHER randomizes a user entry
Packit Service fdd496
       into a number up from 0 up to some maximum minus 1; COMPARATOR returns
Packit Service fdd496
       true if two user entries compare equally; and DATA_FREER is the cleanup
Packit Service fdd496
       function for a user entry.  */
Packit Service fdd496
    Hash_hasher hasher;
Packit Service fdd496
    Hash_comparator comparator;
Packit Service fdd496
    Hash_data_freer data_freer;
Packit Service fdd496
Packit Service fdd496
    /* A linked list of freed struct hash_entry structs.  */
Packit Service fdd496
    struct hash_entry *free_entry_list;
Packit Service fdd496
Packit Service fdd496
#if USE_OBSTACK
Packit Service fdd496
    /* Whenever obstacks are used, it is possible to allocate all overflowed
Packit Service fdd496
       entries into a single stack, so they all can be freed in a single
Packit Service fdd496
       operation.  It is not clear if the speedup is worth the trouble.  */
Packit Service fdd496
    struct obstack entry_stack;
Packit Service fdd496
#endif
Packit Service fdd496
  };
Packit Service fdd496
Packit Service fdd496
/* A hash table contains many internal entries, each holding a pointer to
Packit Service fdd496
   some user-provided data (also called a user entry).  An entry indistinctly
Packit Service fdd496
   refers to both the internal entry and its associated user entry.  A user
Packit Service fdd496
   entry contents may be hashed by a randomization function (the hashing
Packit Service fdd496
   function, or just "hasher" for short) into a number (or "slot") between 0
Packit Service fdd496
   and the current table size.  At each slot position in the hash table,
Packit Service fdd496
   starts a linked chain of entries for which the user data all hash to this
Packit Service fdd496
   slot.  A bucket is the collection of all entries hashing to the same slot.
Packit Service fdd496
Packit Service fdd496
   A good "hasher" function will distribute entries rather evenly in buckets.
Packit Service fdd496
   In the ideal case, the length of each bucket is roughly the number of
Packit Service fdd496
   entries divided by the table size.  Finding the slot for a data is usually
Packit Service fdd496
   done in constant time by the "hasher", and the later finding of a precise
Packit Service fdd496
   entry is linear in time with the size of the bucket.  Consequently, a
Packit Service fdd496
   larger hash table size (that is, a larger number of buckets) is prone to
Packit Service fdd496
   yielding shorter chains, *given* the "hasher" function behaves properly.
Packit Service fdd496
Packit Service fdd496
   Long buckets slow down the lookup algorithm.  One might use big hash table
Packit Service fdd496
   sizes in hope to reduce the average length of buckets, but this might
Packit Service fdd496
   become inordinate, as unused slots in the hash table take some space.  The
Packit Service fdd496
   best bet is to make sure you are using a good "hasher" function (beware
Packit Service fdd496
   that those are not that easy to write! :-), and to use a table size
Packit Service fdd496
   larger than the actual number of entries.  */
Packit Service fdd496
Packit Service fdd496
/* If an insertion makes the ratio of nonempty buckets to table size larger
Packit Service fdd496
   than the growth threshold (a number between 0.0 and 1.0), then increase
Packit Service fdd496
   the table size by multiplying by the growth factor (a number greater than
Packit Service fdd496
   1.0).  The growth threshold defaults to 0.8, and the growth factor
Packit Service fdd496
   defaults to 1.414, meaning that the table will have doubled its size
Packit Service fdd496
   every second time 80% of the buckets get used.  */
Packit Service fdd496
#define DEFAULT_GROWTH_THRESHOLD 0.8f
Packit Service fdd496
#define DEFAULT_GROWTH_FACTOR 1.414f
Packit Service fdd496
Packit Service fdd496
/* If a deletion empties a bucket and causes the ratio of used buckets to
Packit Service fdd496
   table size to become smaller than the shrink threshold (a number between
Packit Service fdd496
   0.0 and 1.0), then shrink the table by multiplying by the shrink factor (a
Packit Service fdd496
   number greater than the shrink threshold but smaller than 1.0).  The shrink
Packit Service fdd496
   threshold and factor default to 0.0 and 1.0, meaning that the table never
Packit Service fdd496
   shrinks.  */
Packit Service fdd496
#define DEFAULT_SHRINK_THRESHOLD 0.0f
Packit Service fdd496
#define DEFAULT_SHRINK_FACTOR 1.0f
Packit Service fdd496
Packit Service fdd496
/* Use this to initialize or reset a TUNING structure to
Packit Service fdd496
   some sensible values. */
Packit Service fdd496
static const Hash_tuning default_tuning =
Packit Service fdd496
  {
Packit Service fdd496
    DEFAULT_SHRINK_THRESHOLD,
Packit Service fdd496
    DEFAULT_SHRINK_FACTOR,
Packit Service fdd496
    DEFAULT_GROWTH_THRESHOLD,
Packit Service fdd496
    DEFAULT_GROWTH_FACTOR,
Packit Service fdd496
    false
Packit Service fdd496
  };
Packit Service fdd496
Packit Service fdd496
/* Information and lookup.  */
Packit Service fdd496
Packit Service fdd496
/* The following few functions provide information about the overall hash
Packit Service fdd496
   table organization: the number of entries, number of buckets and maximum
Packit Service fdd496
   length of buckets.  */
Packit Service fdd496
Packit Service fdd496
/* Return the number of buckets in the hash table.  The table size, the total
Packit Service fdd496
   number of buckets (used plus unused), or the maximum number of slots, are
Packit Service fdd496
   the same quantity.  */
Packit Service fdd496
Packit Service fdd496
size_t
Packit Service fdd496
hash_get_n_buckets (const Hash_table *table)
Packit Service fdd496
{
Packit Service fdd496
  return table->n_buckets;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return the number of slots in use (non-empty buckets).  */
Packit Service fdd496
Packit Service fdd496
size_t
Packit Service fdd496
hash_get_n_buckets_used (const Hash_table *table)
Packit Service fdd496
{
Packit Service fdd496
  return table->n_buckets_used;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return the number of active entries.  */
Packit Service fdd496
Packit Service fdd496
size_t
Packit Service fdd496
hash_get_n_entries (const Hash_table *table)
Packit Service fdd496
{
Packit Service fdd496
  return table->n_entries;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return the length of the longest chain (bucket).  */
Packit Service fdd496
Packit Service fdd496
size_t
Packit Service fdd496
hash_get_max_bucket_length (const Hash_table *table)
Packit Service fdd496
{
Packit Service fdd496
  struct hash_entry const *bucket;
Packit Service fdd496
  size_t max_bucket_length = 0;
Packit Service fdd496
Packit Service fdd496
  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
Packit Service fdd496
    {
Packit Service fdd496
      if (bucket->data)
Packit Service fdd496
        {
Packit Service fdd496
          struct hash_entry const *cursor = bucket;
Packit Service fdd496
          size_t bucket_length = 1;
Packit Service fdd496
Packit Service fdd496
          while (cursor = cursor->next, cursor)
Packit Service fdd496
            bucket_length++;
Packit Service fdd496
Packit Service fdd496
          if (bucket_length > max_bucket_length)
Packit Service fdd496
            max_bucket_length = bucket_length;
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return max_bucket_length;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Do a mild validation of a hash table, by traversing it and checking two
Packit Service fdd496
   statistics.  */
Packit Service fdd496
Packit Service fdd496
bool
Packit Service fdd496
hash_table_ok (const Hash_table *table)
Packit Service fdd496
{
Packit Service fdd496
  struct hash_entry const *bucket;
Packit Service fdd496
  size_t n_buckets_used = 0;
Packit Service fdd496
  size_t n_entries = 0;
Packit Service fdd496
Packit Service fdd496
  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
Packit Service fdd496
    {
Packit Service fdd496
      if (bucket->data)
Packit Service fdd496
        {
Packit Service fdd496
          struct hash_entry const *cursor = bucket;
Packit Service fdd496
Packit Service fdd496
          /* Count bucket head.  */
Packit Service fdd496
          n_buckets_used++;
Packit Service fdd496
          n_entries++;
Packit Service fdd496
Packit Service fdd496
          /* Count bucket overflow.  */
Packit Service fdd496
          while (cursor = cursor->next, cursor)
Packit Service fdd496
            n_entries++;
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  if (n_buckets_used == table->n_buckets_used && n_entries == table->n_entries)
Packit Service fdd496
    return true;
Packit Service fdd496
Packit Service fdd496
  return false;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
void
Packit Service fdd496
hash_print_statistics (const Hash_table *table, FILE *stream)
Packit Service fdd496
{
Packit Service fdd496
  size_t n_entries = hash_get_n_entries (table);
Packit Service fdd496
  size_t n_buckets = hash_get_n_buckets (table);
Packit Service fdd496
  size_t n_buckets_used = hash_get_n_buckets_used (table);
Packit Service fdd496
  size_t max_bucket_length = hash_get_max_bucket_length (table);
Packit Service fdd496
Packit Service fdd496
  fprintf (stream, "# entries:         %lu\n", (unsigned long int) n_entries);
Packit Service fdd496
  fprintf (stream, "# buckets:         %lu\n", (unsigned long int) n_buckets);
Packit Service fdd496
  fprintf (stream, "# buckets used:    %lu (%.2f%%)\n",
Packit Service fdd496
           (unsigned long int) n_buckets_used,
Packit Service fdd496
           (100.0 * n_buckets_used) / n_buckets);
Packit Service fdd496
  fprintf (stream, "max bucket length: %lu\n",
Packit Service fdd496
           (unsigned long int) max_bucket_length);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Hash KEY and return a pointer to the selected bucket.
Packit Service fdd496
   If TABLE->hasher misbehaves, abort.  */
Packit Service fdd496
static struct hash_entry *
Packit Service fdd496
safe_hasher (const Hash_table *table, const void *key)
Packit Service fdd496
{
Packit Service fdd496
  size_t n = table->hasher (key, table->n_buckets);
Packit Service fdd496
  if (! (n < table->n_buckets))
Packit Service fdd496
    abort ();
Packit Service fdd496
  return table->bucket + n;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* If ENTRY matches an entry already in the hash table, return the
Packit Service fdd496
   entry from the table.  Otherwise, return NULL.  */
Packit Service fdd496
Packit Service fdd496
void *
Packit Service fdd496
hash_lookup (const Hash_table *table, const void *entry)
Packit Service fdd496
{
Packit Service fdd496
  struct hash_entry const *bucket = safe_hasher (table, entry);
Packit Service fdd496
  struct hash_entry const *cursor;
Packit Service fdd496
Packit Service fdd496
  if (bucket->data == NULL)
Packit Service fdd496
    return NULL;
Packit Service fdd496
Packit Service fdd496
  for (cursor = bucket; cursor; cursor = cursor->next)
Packit Service fdd496
    if (entry == cursor->data || table->comparator (entry, cursor->data))
Packit Service fdd496
      return cursor->data;
Packit Service fdd496
Packit Service fdd496
  return NULL;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Walking.  */
Packit Service fdd496
Packit Service fdd496
/* The functions in this page traverse the hash table and process the
Packit Service fdd496
   contained entries.  For the traversal to work properly, the hash table
Packit Service fdd496
   should not be resized nor modified while any particular entry is being
Packit Service fdd496
   processed.  In particular, entries should not be added, and an entry
Packit Service fdd496
   may be removed only if there is no shrink threshold and the entry being
Packit Service fdd496
   removed has already been passed to hash_get_next.  */
Packit Service fdd496
Packit Service fdd496
/* Return the first data in the table, or NULL if the table is empty.  */
Packit Service fdd496
Packit Service fdd496
void *
Packit Service fdd496
hash_get_first (const Hash_table *table)
Packit Service fdd496
{
Packit Service fdd496
  struct hash_entry const *bucket;
Packit Service fdd496
Packit Service fdd496
  if (table->n_entries == 0)
Packit Service fdd496
    return NULL;
Packit Service fdd496
Packit Service fdd496
  for (bucket = table->bucket; ; bucket++)
Packit Service fdd496
    if (! (bucket < table->bucket_limit))
Packit Service fdd496
      abort ();
Packit Service fdd496
    else if (bucket->data)
Packit Service fdd496
      return bucket->data;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Return the user data for the entry following ENTRY, where ENTRY has been
Packit Service fdd496
   returned by a previous call to either 'hash_get_first' or 'hash_get_next'.
Packit Service fdd496
   Return NULL if there are no more entries.  */
Packit Service fdd496
Packit Service fdd496
void *
Packit Service fdd496
hash_get_next (const Hash_table *table, const void *entry)
Packit Service fdd496
{
Packit Service fdd496
  struct hash_entry const *bucket = safe_hasher (table, entry);
Packit Service fdd496
  struct hash_entry const *cursor;
Packit Service fdd496
Packit Service fdd496
  /* Find next entry in the same bucket.  */
Packit Service fdd496
  cursor = bucket;
Packit Service fdd496
  do
Packit Service fdd496
    {
Packit Service fdd496
      if (cursor->data == entry && cursor->next)
Packit Service fdd496
        return cursor->next->data;
Packit Service fdd496
      cursor = cursor->next;
Packit Service fdd496
    }
Packit Service fdd496
  while (cursor != NULL);
Packit Service fdd496
Packit Service fdd496
  /* Find first entry in any subsequent bucket.  */
Packit Service fdd496
  while (++bucket < table->bucket_limit)
Packit Service fdd496
    if (bucket->data)
Packit Service fdd496
      return bucket->data;
Packit Service fdd496
Packit Service fdd496
  /* None found.  */
Packit Service fdd496
  return NULL;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Fill BUFFER with pointers to active user entries in the hash table, then
Packit Service fdd496
   return the number of pointers copied.  Do not copy more than BUFFER_SIZE
Packit Service fdd496
   pointers.  */
Packit Service fdd496
Packit Service fdd496
size_t
Packit Service fdd496
hash_get_entries (const Hash_table *table, void **buffer,
Packit Service fdd496
                  size_t buffer_size)
Packit Service fdd496
{
Packit Service fdd496
  size_t counter = 0;
Packit Service fdd496
  struct hash_entry const *bucket;
Packit Service fdd496
  struct hash_entry const *cursor;
Packit Service fdd496
Packit Service fdd496
  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
Packit Service fdd496
    {
Packit Service fdd496
      if (bucket->data)
Packit Service fdd496
        {
Packit Service fdd496
          for (cursor = bucket; cursor; cursor = cursor->next)
Packit Service fdd496
            {
Packit Service fdd496
              if (counter >= buffer_size)
Packit Service fdd496
                return counter;
Packit Service fdd496
              buffer[counter++] = cursor->data;
Packit Service fdd496
            }
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return counter;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Call a PROCESSOR function for each entry of a hash table, and return the
Packit Service fdd496
   number of entries for which the processor function returned success.  A
Packit Service fdd496
   pointer to some PROCESSOR_DATA which will be made available to each call to
Packit Service fdd496
   the processor function.  The PROCESSOR accepts two arguments: the first is
Packit Service fdd496
   the user entry being walked into, the second is the value of PROCESSOR_DATA
Packit Service fdd496
   as received.  The walking continue for as long as the PROCESSOR function
Packit Service fdd496
   returns nonzero.  When it returns zero, the walking is interrupted.  */
Packit Service fdd496
Packit Service fdd496
size_t
Packit Service fdd496
hash_do_for_each (const Hash_table *table, Hash_processor processor,
Packit Service fdd496
                  void *processor_data)
Packit Service fdd496
{
Packit Service fdd496
  size_t counter = 0;
Packit Service fdd496
  struct hash_entry const *bucket;
Packit Service fdd496
  struct hash_entry const *cursor;
Packit Service fdd496
Packit Service fdd496
  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
Packit Service fdd496
    {
Packit Service fdd496
      if (bucket->data)
Packit Service fdd496
        {
Packit Service fdd496
          for (cursor = bucket; cursor; cursor = cursor->next)
Packit Service fdd496
            {
Packit Service fdd496
              if (! processor (cursor->data, processor_data))
Packit Service fdd496
                return counter;
Packit Service fdd496
              counter++;
Packit Service fdd496
            }
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return counter;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Allocation and clean-up.  */
Packit Service fdd496
Packit Service fdd496
/* Return a hash index for a NUL-terminated STRING between 0 and N_BUCKETS-1.
Packit Service fdd496
   This is a convenience routine for constructing other hashing functions.  */
Packit Service fdd496
Packit Service fdd496
#if USE_DIFF_HASH
Packit Service fdd496
Packit Service fdd496
/* About hashings, Paul Eggert writes to me (FP), on 1994-01-01: "Please see
Packit Service fdd496
   B. J. McKenzie, R. Harries & T. Bell, Selecting a hashing algorithm,
Packit Service fdd496
   Software--practice & experience 20, 2 (Feb 1990), 209-224.  Good hash
Packit Service fdd496
   algorithms tend to be domain-specific, so what's good for [diffutils'] io.c
Packit Service fdd496
   may not be good for your application."  */
Packit Service fdd496
Packit Service fdd496
size_t
Packit Service fdd496
hash_string (const char *string, size_t n_buckets)
Packit Service fdd496
{
Packit Service fdd496
# define HASH_ONE_CHAR(Value, Byte) \
Packit Service fdd496
  ((Byte) + rotl_sz (Value, 7))
Packit Service fdd496
Packit Service fdd496
  size_t value = 0;
Packit Service fdd496
  unsigned char ch;
Packit Service fdd496
Packit Service fdd496
  for (; (ch = *string); string++)
Packit Service fdd496
    value = HASH_ONE_CHAR (value, ch);
Packit Service fdd496
  return value % n_buckets;
Packit Service fdd496
Packit Service fdd496
# undef HASH_ONE_CHAR
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
#else /* not USE_DIFF_HASH */
Packit Service fdd496
Packit Service fdd496
/* This one comes from 'recode', and performs a bit better than the above as
Packit Service fdd496
   per a few experiments.  It is inspired from a hashing routine found in the
Packit Service fdd496
   very old Cyber 'snoop', itself written in typical Greg Mansfield style.
Packit Service fdd496
   (By the way, what happened to this excellent man?  Is he still alive?)  */
Packit Service fdd496
Packit Service fdd496
size_t
Packit Service fdd496
hash_string (const char *string, size_t n_buckets)
Packit Service fdd496
{
Packit Service fdd496
  size_t value = 0;
Packit Service fdd496
  unsigned char ch;
Packit Service fdd496
Packit Service fdd496
  for (; (ch = *string); string++)
Packit Service fdd496
    value = (value * 31 + ch) % n_buckets;
Packit Service fdd496
  return value;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
#endif /* not USE_DIFF_HASH */
Packit Service fdd496
Packit Service fdd496
/* Return true if CANDIDATE is a prime number.  CANDIDATE should be an odd
Packit Service fdd496
   number at least equal to 11.  */
Packit Service fdd496
Packit Service fdd496
static bool _GL_ATTRIBUTE_CONST
Packit Service fdd496
is_prime (size_t candidate)
Packit Service fdd496
{
Packit Service fdd496
  size_t divisor = 3;
Packit Service fdd496
  size_t square = divisor * divisor;
Packit Service fdd496
Packit Service fdd496
  while (square < candidate && (candidate % divisor))
Packit Service fdd496
    {
Packit Service fdd496
      divisor++;
Packit Service fdd496
      square += 4 * divisor;
Packit Service fdd496
      divisor++;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return (candidate % divisor ? true : false);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Round a given CANDIDATE number up to the nearest prime, and return that
Packit Service fdd496
   prime.  Primes lower than 10 are merely skipped.  */
Packit Service fdd496
Packit Service fdd496
static size_t _GL_ATTRIBUTE_CONST
Packit Service fdd496
next_prime (size_t candidate)
Packit Service fdd496
{
Packit Service fdd496
  /* Skip small primes.  */
Packit Service fdd496
  if (candidate < 10)
Packit Service fdd496
    candidate = 10;
Packit Service fdd496
Packit Service fdd496
  /* Make it definitely odd.  */
Packit Service fdd496
  candidate |= 1;
Packit Service fdd496
Packit Service fdd496
  while (SIZE_MAX != candidate && !is_prime (candidate))
Packit Service fdd496
    candidate += 2;
Packit Service fdd496
Packit Service fdd496
  return candidate;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
void
Packit Service fdd496
hash_reset_tuning (Hash_tuning *tuning)
Packit Service fdd496
{
Packit Service fdd496
  *tuning = default_tuning;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* If the user passes a NULL hasher, we hash the raw pointer.  */
Packit Service fdd496
static size_t
Packit Service fdd496
raw_hasher (const void *data, size_t n)
Packit Service fdd496
{
Packit Service fdd496
  /* When hashing unique pointers, it is often the case that they were
Packit Service fdd496
     generated by malloc and thus have the property that the low-order
Packit Service fdd496
     bits are 0.  As this tends to give poorer performance with small
Packit Service fdd496
     tables, we rotate the pointer value before performing division,
Packit Service fdd496
     in an attempt to improve hash quality.  */
Packit Service fdd496
  size_t val = rotr_sz ((size_t) data, 3);
Packit Service fdd496
  return val % n;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* If the user passes a NULL comparator, we use pointer comparison.  */
Packit Service fdd496
static bool
Packit Service fdd496
raw_comparator (const void *a, const void *b)
Packit Service fdd496
{
Packit Service fdd496
  return a == b;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
Packit Service fdd496
/* For the given hash TABLE, check the user supplied tuning structure for
Packit Service fdd496
   reasonable values, and return true if there is no gross error with it.
Packit Service fdd496
   Otherwise, definitively reset the TUNING field to some acceptable default
Packit Service fdd496
   in the hash table (that is, the user loses the right of further modifying
Packit Service fdd496
   tuning arguments), and return false.  */
Packit Service fdd496
Packit Service fdd496
static bool
Packit Service fdd496
check_tuning (Hash_table *table)
Packit Service fdd496
{
Packit Service fdd496
  const Hash_tuning *tuning = table->tuning;
Packit Service fdd496
  float epsilon;
Packit Service fdd496
  if (tuning == &default_tuning)
Packit Service fdd496
    return true;
Packit Service fdd496
Packit Service fdd496
  /* Be a bit stricter than mathematics would require, so that
Packit Service fdd496
     rounding errors in size calculations do not cause allocations to
Packit Service fdd496
     fail to grow or shrink as they should.  The smallest allocation
Packit Service fdd496
     is 11 (due to next_prime's algorithm), so an epsilon of 0.1
Packit Service fdd496
     should be good enough.  */
Packit Service fdd496
  epsilon = 0.1f;
Packit Service fdd496
Packit Service fdd496
  if (epsilon < tuning->growth_threshold
Packit Service fdd496
      && tuning->growth_threshold < 1 - epsilon
Packit Service fdd496
      && 1 + epsilon < tuning->growth_factor
Packit Service fdd496
      && 0 <= tuning->shrink_threshold
Packit Service fdd496
      && tuning->shrink_threshold + epsilon < tuning->shrink_factor
Packit Service fdd496
      && tuning->shrink_factor <= 1
Packit Service fdd496
      && tuning->shrink_threshold + epsilon < tuning->growth_threshold)
Packit Service fdd496
    return true;
Packit Service fdd496
Packit Service fdd496
  table->tuning = &default_tuning;
Packit Service fdd496
  return false;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Compute the size of the bucket array for the given CANDIDATE and
Packit Service fdd496
   TUNING, or return 0 if there is no possible way to allocate that
Packit Service fdd496
   many entries.  */
Packit Service fdd496
Packit Service fdd496
static size_t _GL_ATTRIBUTE_PURE
Packit Service fdd496
compute_bucket_size (size_t candidate, const Hash_tuning *tuning)
Packit Service fdd496
{
Packit Service fdd496
  if (!tuning->is_n_buckets)
Packit Service fdd496
    {
Packit Service fdd496
      float new_candidate = candidate / tuning->growth_threshold;
Packit Service fdd496
      if (SIZE_MAX <= new_candidate)
Packit Service fdd496
        return 0;
Packit Service fdd496
      candidate = new_candidate;
Packit Service fdd496
    }
Packit Service fdd496
  candidate = next_prime (candidate);
Packit Service fdd496
  if (xalloc_oversized (candidate, sizeof (struct hash_entry *)))
Packit Service fdd496
    return 0;
Packit Service fdd496
  return candidate;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Allocate and return a new hash table, or NULL upon failure.  The initial
Packit Service fdd496
   number of buckets is automatically selected so as to _guarantee_ that you
Packit Service fdd496
   may insert at least CANDIDATE different user entries before any growth of
Packit Service fdd496
   the hash table size occurs.  So, if have a reasonably tight a-priori upper
Packit Service fdd496
   bound on the number of entries you intend to insert in the hash table, you
Packit Service fdd496
   may save some table memory and insertion time, by specifying it here.  If
Packit Service fdd496
   the IS_N_BUCKETS field of the TUNING structure is true, the CANDIDATE
Packit Service fdd496
   argument has its meaning changed to the wanted number of buckets.
Packit Service fdd496
Packit Service fdd496
   TUNING points to a structure of user-supplied values, in case some fine
Packit Service fdd496
   tuning is wanted over the default behavior of the hasher.  If TUNING is
Packit Service fdd496
   NULL, the default tuning parameters are used instead.  If TUNING is
Packit Service fdd496
   provided but the values requested are out of bounds or might cause
Packit Service fdd496
   rounding errors, return NULL.
Packit Service fdd496
Packit Service fdd496
   The user-supplied HASHER function, when not NULL, accepts two
Packit Service fdd496
   arguments ENTRY and TABLE_SIZE.  It computes, by hashing ENTRY contents, a
Packit Service fdd496
   slot number for that entry which should be in the range 0..TABLE_SIZE-1.
Packit Service fdd496
   This slot number is then returned.
Packit Service fdd496
Packit Service fdd496
   The user-supplied COMPARATOR function, when not NULL, accepts two
Packit Service fdd496
   arguments pointing to user data, it then returns true for a pair of entries
Packit Service fdd496
   that compare equal, or false otherwise.  This function is internally called
Packit Service fdd496
   on entries which are already known to hash to the same bucket index,
Packit Service fdd496
   but which are distinct pointers.
Packit Service fdd496
Packit Service fdd496
   The user-supplied DATA_FREER function, when not NULL, may be later called
Packit Service fdd496
   with the user data as an argument, just before the entry containing the
Packit Service fdd496
   data gets freed.  This happens from within 'hash_free' or 'hash_clear'.
Packit Service fdd496
   You should specify this function only if you want these functions to free
Packit Service fdd496
   all of your 'data' data.  This is typically the case when your data is
Packit Service fdd496
   simply an auxiliary struct that you have malloc'd to aggregate several
Packit Service fdd496
   values.  */
Packit Service fdd496
Packit Service fdd496
Hash_table *
Packit Service fdd496
hash_initialize (size_t candidate, const Hash_tuning *tuning,
Packit Service fdd496
                 Hash_hasher hasher, Hash_comparator comparator,
Packit Service fdd496
                 Hash_data_freer data_freer)
Packit Service fdd496
{
Packit Service fdd496
  Hash_table *table;
Packit Service fdd496
Packit Service fdd496
  if (hasher == NULL)
Packit Service fdd496
    hasher = raw_hasher;
Packit Service fdd496
  if (comparator == NULL)
Packit Service fdd496
    comparator = raw_comparator;
Packit Service fdd496
Packit Service fdd496
  table = malloc (sizeof *table);
Packit Service fdd496
  if (table == NULL)
Packit Service fdd496
    return NULL;
Packit Service fdd496
Packit Service fdd496
  if (!tuning)
Packit Service fdd496
    tuning = &default_tuning;
Packit Service fdd496
  table->tuning = tuning;
Packit Service fdd496
  if (!check_tuning (table))
Packit Service fdd496
    {
Packit Service fdd496
      /* Fail if the tuning options are invalid.  This is the only occasion
Packit Service fdd496
         when the user gets some feedback about it.  Once the table is created,
Packit Service fdd496
         if the user provides invalid tuning options, we silently revert to
Packit Service fdd496
         using the defaults, and ignore further request to change the tuning
Packit Service fdd496
         options.  */
Packit Service fdd496
      goto fail;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  table->n_buckets = compute_bucket_size (candidate, tuning);
Packit Service fdd496
  if (!table->n_buckets)
Packit Service fdd496
    goto fail;
Packit Service fdd496
Packit Service fdd496
  table->bucket = calloc (table->n_buckets, sizeof *table->bucket);
Packit Service fdd496
  if (table->bucket == NULL)
Packit Service fdd496
    goto fail;
Packit Service fdd496
  table->bucket_limit = table->bucket + table->n_buckets;
Packit Service fdd496
  table->n_buckets_used = 0;
Packit Service fdd496
  table->n_entries = 0;
Packit Service fdd496
Packit Service fdd496
  table->hasher = hasher;
Packit Service fdd496
  table->comparator = comparator;
Packit Service fdd496
  table->data_freer = data_freer;
Packit Service fdd496
Packit Service fdd496
  table->free_entry_list = NULL;
Packit Service fdd496
#if USE_OBSTACK
Packit Service fdd496
  obstack_init (&table->entry_stack);
Packit Service fdd496
#endif
Packit Service fdd496
  return table;
Packit Service fdd496
Packit Service fdd496
 fail:
Packit Service fdd496
  free (table);
Packit Service fdd496
  return NULL;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Make all buckets empty, placing any chained entries on the free list.
Packit Service fdd496
   Apply the user-specified function data_freer (if any) to the datas of any
Packit Service fdd496
   affected entries.  */
Packit Service fdd496
Packit Service fdd496
void
Packit Service fdd496
hash_clear (Hash_table *table)
Packit Service fdd496
{
Packit Service fdd496
  struct hash_entry *bucket;
Packit Service fdd496
Packit Service fdd496
  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
Packit Service fdd496
    {
Packit Service fdd496
      if (bucket->data)
Packit Service fdd496
        {
Packit Service fdd496
          struct hash_entry *cursor;
Packit Service fdd496
          struct hash_entry *next;
Packit Service fdd496
Packit Service fdd496
          /* Free the bucket overflow.  */
Packit Service fdd496
          for (cursor = bucket->next; cursor; cursor = next)
Packit Service fdd496
            {
Packit Service fdd496
              if (table->data_freer)
Packit Service fdd496
                table->data_freer (cursor->data);
Packit Service fdd496
              cursor->data = NULL;
Packit Service fdd496
Packit Service fdd496
              next = cursor->next;
Packit Service fdd496
              /* Relinking is done one entry at a time, as it is to be expected
Packit Service fdd496
                 that overflows are either rare or short.  */
Packit Service fdd496
              cursor->next = table->free_entry_list;
Packit Service fdd496
              table->free_entry_list = cursor;
Packit Service fdd496
            }
Packit Service fdd496
Packit Service fdd496
          /* Free the bucket head.  */
Packit Service fdd496
          if (table->data_freer)
Packit Service fdd496
            table->data_freer (bucket->data);
Packit Service fdd496
          bucket->data = NULL;
Packit Service fdd496
          bucket->next = NULL;
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  table->n_buckets_used = 0;
Packit Service fdd496
  table->n_entries = 0;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Reclaim all storage associated with a hash table.  If a data_freer
Packit Service fdd496
   function has been supplied by the user when the hash table was created,
Packit Service fdd496
   this function applies it to the data of each entry before freeing that
Packit Service fdd496
   entry.  */
Packit Service fdd496
Packit Service fdd496
void
Packit Service fdd496
hash_free (Hash_table *table)
Packit Service fdd496
{
Packit Service fdd496
  struct hash_entry *bucket;
Packit Service fdd496
  struct hash_entry *cursor;
Packit Service fdd496
  struct hash_entry *next;
Packit Service fdd496
Packit Service fdd496
  /* Call the user data_freer function.  */
Packit Service fdd496
  if (table->data_freer && table->n_entries)
Packit Service fdd496
    {
Packit Service fdd496
      for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
Packit Service fdd496
        {
Packit Service fdd496
          if (bucket->data)
Packit Service fdd496
            {
Packit Service fdd496
              for (cursor = bucket; cursor; cursor = cursor->next)
Packit Service fdd496
                table->data_freer (cursor->data);
Packit Service fdd496
            }
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
#if USE_OBSTACK
Packit Service fdd496
Packit Service fdd496
  obstack_free (&table->entry_stack, NULL);
Packit Service fdd496
Packit Service fdd496
#else
Packit Service fdd496
Packit Service fdd496
  /* Free all bucket overflowed entries.  */
Packit Service fdd496
  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
Packit Service fdd496
    {
Packit Service fdd496
      for (cursor = bucket->next; cursor; cursor = next)
Packit Service fdd496
        {
Packit Service fdd496
          next = cursor->next;
Packit Service fdd496
          free (cursor);
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Also reclaim the internal list of previously freed entries.  */
Packit Service fdd496
  for (cursor = table->free_entry_list; cursor; cursor = next)
Packit Service fdd496
    {
Packit Service fdd496
      next = cursor->next;
Packit Service fdd496
      free (cursor);
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
  /* Free the remainder of the hash table structure.  */
Packit Service fdd496
  free (table->bucket);
Packit Service fdd496
  free (table);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Insertion and deletion.  */
Packit Service fdd496
Packit Service fdd496
/* Get a new hash entry for a bucket overflow, possibly by recycling a
Packit Service fdd496
   previously freed one.  If this is not possible, allocate a new one.  */
Packit Service fdd496
Packit Service fdd496
static struct hash_entry *
Packit Service fdd496
allocate_entry (Hash_table *table)
Packit Service fdd496
{
Packit Service fdd496
  struct hash_entry *new;
Packit Service fdd496
Packit Service fdd496
  if (table->free_entry_list)
Packit Service fdd496
    {
Packit Service fdd496
      new = table->free_entry_list;
Packit Service fdd496
      table->free_entry_list = new->next;
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
#if USE_OBSTACK
Packit Service fdd496
      new = obstack_alloc (&table->entry_stack, sizeof *new);
Packit Service fdd496
#else
Packit Service fdd496
      new = malloc (sizeof *new);
Packit Service fdd496
#endif
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return new;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Free a hash entry which was part of some bucket overflow,
Packit Service fdd496
   saving it for later recycling.  */
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
free_entry (Hash_table *table, struct hash_entry *entry)
Packit Service fdd496
{
Packit Service fdd496
  entry->data = NULL;
Packit Service fdd496
  entry->next = table->free_entry_list;
Packit Service fdd496
  table->free_entry_list = entry;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* This private function is used to help with insertion and deletion.  When
Packit Service fdd496
   ENTRY matches an entry in the table, return a pointer to the corresponding
Packit Service fdd496
   user data and set *BUCKET_HEAD to the head of the selected bucket.
Packit Service fdd496
   Otherwise, return NULL.  When DELETE is true and ENTRY matches an entry in
Packit Service fdd496
   the table, unlink the matching entry.  */
Packit Service fdd496
Packit Service fdd496
static void *
Packit Service fdd496
hash_find_entry (Hash_table *table, const void *entry,
Packit Service fdd496
                 struct hash_entry **bucket_head, bool delete)
Packit Service fdd496
{
Packit Service fdd496
  struct hash_entry *bucket = safe_hasher (table, entry);
Packit Service fdd496
  struct hash_entry *cursor;
Packit Service fdd496
Packit Service fdd496
  *bucket_head = bucket;
Packit Service fdd496
Packit Service fdd496
  /* Test for empty bucket.  */
Packit Service fdd496
  if (bucket->data == NULL)
Packit Service fdd496
    return NULL;
Packit Service fdd496
Packit Service fdd496
  /* See if the entry is the first in the bucket.  */
Packit Service fdd496
  if (entry == bucket->data || table->comparator (entry, bucket->data))
Packit Service fdd496
    {
Packit Service fdd496
      void *data = bucket->data;
Packit Service fdd496
Packit Service fdd496
      if (delete)
Packit Service fdd496
        {
Packit Service fdd496
          if (bucket->next)
Packit Service fdd496
            {
Packit Service fdd496
              struct hash_entry *next = bucket->next;
Packit Service fdd496
Packit Service fdd496
              /* Bump the first overflow entry into the bucket head, then save
Packit Service fdd496
                 the previous first overflow entry for later recycling.  */
Packit Service fdd496
              *bucket = *next;
Packit Service fdd496
              free_entry (table, next);
Packit Service fdd496
            }
Packit Service fdd496
          else
Packit Service fdd496
            {
Packit Service fdd496
              bucket->data = NULL;
Packit Service fdd496
            }
Packit Service fdd496
        }
Packit Service fdd496
Packit Service fdd496
      return data;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Scan the bucket overflow.  */
Packit Service fdd496
  for (cursor = bucket; cursor->next; cursor = cursor->next)
Packit Service fdd496
    {
Packit Service fdd496
      if (entry == cursor->next->data
Packit Service fdd496
          || table->comparator (entry, cursor->next->data))
Packit Service fdd496
        {
Packit Service fdd496
          void *data = cursor->next->data;
Packit Service fdd496
Packit Service fdd496
          if (delete)
Packit Service fdd496
            {
Packit Service fdd496
              struct hash_entry *next = cursor->next;
Packit Service fdd496
Packit Service fdd496
              /* Unlink the entry to delete, then save the freed entry for later
Packit Service fdd496
                 recycling.  */
Packit Service fdd496
              cursor->next = next->next;
Packit Service fdd496
              free_entry (table, next);
Packit Service fdd496
            }
Packit Service fdd496
Packit Service fdd496
          return data;
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* No entry found.  */
Packit Service fdd496
  return NULL;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Internal helper, to move entries from SRC to DST.  Both tables must
Packit Service fdd496
   share the same free entry list.  If SAFE, only move overflow
Packit Service fdd496
   entries, saving bucket heads for later, so that no allocations will
Packit Service fdd496
   occur.  Return false if the free entry list is exhausted and an
Packit Service fdd496
   allocation fails.  */
Packit Service fdd496
Packit Service fdd496
static bool
Packit Service fdd496
transfer_entries (Hash_table *dst, Hash_table *src, bool safe)
Packit Service fdd496
{
Packit Service fdd496
  struct hash_entry *bucket;
Packit Service fdd496
  struct hash_entry *cursor;
Packit Service fdd496
  struct hash_entry *next;
Packit Service fdd496
  for (bucket = src->bucket; bucket < src->bucket_limit; bucket++)
Packit Service fdd496
    if (bucket->data)
Packit Service fdd496
      {
Packit Service fdd496
        void *data;
Packit Service fdd496
        struct hash_entry *new_bucket;
Packit Service fdd496
Packit Service fdd496
        /* Within each bucket, transfer overflow entries first and
Packit Service fdd496
           then the bucket head, to minimize memory pressure.  After
Packit Service fdd496
           all, the only time we might allocate is when moving the
Packit Service fdd496
           bucket head, but moving overflow entries first may create
Packit Service fdd496
           free entries that can be recycled by the time we finally
Packit Service fdd496
           get to the bucket head.  */
Packit Service fdd496
        for (cursor = bucket->next; cursor; cursor = next)
Packit Service fdd496
          {
Packit Service fdd496
            data = cursor->data;
Packit Service fdd496
            new_bucket = safe_hasher (dst, data);
Packit Service fdd496
Packit Service fdd496
            next = cursor->next;
Packit Service fdd496
Packit Service fdd496
            if (new_bucket->data)
Packit Service fdd496
              {
Packit Service fdd496
                /* Merely relink an existing entry, when moving from a
Packit Service fdd496
                   bucket overflow into a bucket overflow.  */
Packit Service fdd496
                cursor->next = new_bucket->next;
Packit Service fdd496
                new_bucket->next = cursor;
Packit Service fdd496
              }
Packit Service fdd496
            else
Packit Service fdd496
              {
Packit Service fdd496
                /* Free an existing entry, when moving from a bucket
Packit Service fdd496
                   overflow into a bucket header.  */
Packit Service fdd496
                new_bucket->data = data;
Packit Service fdd496
                dst->n_buckets_used++;
Packit Service fdd496
                free_entry (dst, cursor);
Packit Service fdd496
              }
Packit Service fdd496
          }
Packit Service fdd496
        /* Now move the bucket head.  Be sure that if we fail due to
Packit Service fdd496
           allocation failure that the src table is in a consistent
Packit Service fdd496
           state.  */
Packit Service fdd496
        data = bucket->data;
Packit Service fdd496
        bucket->next = NULL;
Packit Service fdd496
        if (safe)
Packit Service fdd496
          continue;
Packit Service fdd496
        new_bucket = safe_hasher (dst, data);
Packit Service fdd496
Packit Service fdd496
        if (new_bucket->data)
Packit Service fdd496
          {
Packit Service fdd496
            /* Allocate or recycle an entry, when moving from a bucket
Packit Service fdd496
               header into a bucket overflow.  */
Packit Service fdd496
            struct hash_entry *new_entry = allocate_entry (dst);
Packit Service fdd496
Packit Service fdd496
            if (new_entry == NULL)
Packit Service fdd496
              return false;
Packit Service fdd496
Packit Service fdd496
            new_entry->data = data;
Packit Service fdd496
            new_entry->next = new_bucket->next;
Packit Service fdd496
            new_bucket->next = new_entry;
Packit Service fdd496
          }
Packit Service fdd496
        else
Packit Service fdd496
          {
Packit Service fdd496
            /* Move from one bucket header to another.  */
Packit Service fdd496
            new_bucket->data = data;
Packit Service fdd496
            dst->n_buckets_used++;
Packit Service fdd496
          }
Packit Service fdd496
        bucket->data = NULL;
Packit Service fdd496
        src->n_buckets_used--;
Packit Service fdd496
      }
Packit Service fdd496
  return true;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* For an already existing hash table, change the number of buckets through
Packit Service fdd496
   specifying CANDIDATE.  The contents of the hash table are preserved.  The
Packit Service fdd496
   new number of buckets is automatically selected so as to _guarantee_ that
Packit Service fdd496
   the table may receive at least CANDIDATE different user entries, including
Packit Service fdd496
   those already in the table, before any other growth of the hash table size
Packit Service fdd496
   occurs.  If TUNING->IS_N_BUCKETS is true, then CANDIDATE specifies the
Packit Service fdd496
   exact number of buckets desired.  Return true iff the rehash succeeded.  */
Packit Service fdd496
Packit Service fdd496
bool
Packit Service fdd496
hash_rehash (Hash_table *table, size_t candidate)
Packit Service fdd496
{
Packit Service fdd496
  Hash_table storage;
Packit Service fdd496
  Hash_table *new_table;
Packit Service fdd496
  size_t new_size = compute_bucket_size (candidate, table->tuning);
Packit Service fdd496
Packit Service fdd496
  if (!new_size)
Packit Service fdd496
    return false;
Packit Service fdd496
  if (new_size == table->n_buckets)
Packit Service fdd496
    return true;
Packit Service fdd496
  new_table = &storage;
Packit Service fdd496
  new_table->bucket = calloc (new_size, sizeof *new_table->bucket);
Packit Service fdd496
  if (new_table->bucket == NULL)
Packit Service fdd496
    return false;
Packit Service fdd496
  new_table->n_buckets = new_size;
Packit Service fdd496
  new_table->bucket_limit = new_table->bucket + new_size;
Packit Service fdd496
  new_table->n_buckets_used = 0;
Packit Service fdd496
  new_table->n_entries = 0;
Packit Service fdd496
  new_table->tuning = table->tuning;
Packit Service fdd496
  new_table->hasher = table->hasher;
Packit Service fdd496
  new_table->comparator = table->comparator;
Packit Service fdd496
  new_table->data_freer = table->data_freer;
Packit Service fdd496
Packit Service fdd496
  /* In order for the transfer to successfully complete, we need
Packit Service fdd496
     additional overflow entries when distinct buckets in the old
Packit Service fdd496
     table collide into a common bucket in the new table.  The worst
Packit Service fdd496
     case possible is a hasher that gives a good spread with the old
Packit Service fdd496
     size, but returns a constant with the new size; if we were to
Packit Service fdd496
     guarantee table->n_buckets_used-1 free entries in advance, then
Packit Service fdd496
     the transfer would be guaranteed to not allocate memory.
Packit Service fdd496
     However, for large tables, a guarantee of no further allocation
Packit Service fdd496
     introduces a lot of extra memory pressure, all for an unlikely
Packit Service fdd496
     corner case (most rehashes reduce, rather than increase, the
Packit Service fdd496
     number of overflow entries needed).  So, we instead ensure that
Packit Service fdd496
     the transfer process can be reversed if we hit a memory
Packit Service fdd496
     allocation failure mid-transfer.  */
Packit Service fdd496
Packit Service fdd496
  /* Merely reuse the extra old space into the new table.  */
Packit Service fdd496
#if USE_OBSTACK
Packit Service fdd496
  new_table->entry_stack = table->entry_stack;
Packit Service fdd496
#endif
Packit Service fdd496
  new_table->free_entry_list = table->free_entry_list;
Packit Service fdd496
Packit Service fdd496
  if (transfer_entries (new_table, table, false))
Packit Service fdd496
    {
Packit Service fdd496
      /* Entries transferred successfully; tie up the loose ends.  */
Packit Service fdd496
      free (table->bucket);
Packit Service fdd496
      table->bucket = new_table->bucket;
Packit Service fdd496
      table->bucket_limit = new_table->bucket_limit;
Packit Service fdd496
      table->n_buckets = new_table->n_buckets;
Packit Service fdd496
      table->n_buckets_used = new_table->n_buckets_used;
Packit Service fdd496
      table->free_entry_list = new_table->free_entry_list;
Packit Service fdd496
      /* table->n_entries and table->entry_stack already hold their value.  */
Packit Service fdd496
      return true;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* We've allocated new_table->bucket (and possibly some entries),
Packit Service fdd496
     exhausted the free list, and moved some but not all entries into
Packit Service fdd496
     new_table.  We must undo the partial move before returning
Packit Service fdd496
     failure.  The only way to get into this situation is if new_table
Packit Service fdd496
     uses fewer buckets than the old table, so we will reclaim some
Packit Service fdd496
     free entries as overflows in the new table are put back into
Packit Service fdd496
     distinct buckets in the old table.
Packit Service fdd496
Packit Service fdd496
     There are some pathological cases where a single pass through the
Packit Service fdd496
     table requires more intermediate overflow entries than using two
Packit Service fdd496
     passes.  Two passes give worse cache performance and takes
Packit Service fdd496
     longer, but at this point, we're already out of memory, so slow
Packit Service fdd496
     and safe is better than failure.  */
Packit Service fdd496
  table->free_entry_list = new_table->free_entry_list;
Packit Service fdd496
  if (! (transfer_entries (table, new_table, true)
Packit Service fdd496
         && transfer_entries (table, new_table, false)))
Packit Service fdd496
    abort ();
Packit Service fdd496
  /* table->n_entries already holds its value.  */
Packit Service fdd496
  free (new_table->bucket);
Packit Service fdd496
  return false;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Insert ENTRY into hash TABLE if there is not already a matching entry.
Packit Service fdd496
Packit Service fdd496
   Return -1 upon memory allocation failure.
Packit Service fdd496
   Return 1 if insertion succeeded.
Packit Service fdd496
   Return 0 if there is already a matching entry in the table,
Packit Service fdd496
   and in that case, if MATCHED_ENT is non-NULL, set *MATCHED_ENT
Packit Service fdd496
   to that entry.
Packit Service fdd496
Packit Service fdd496
   This interface is easier to use than hash_insert when you must
Packit Service fdd496
   distinguish between the latter two cases.  More importantly,
Packit Service fdd496
   hash_insert is unusable for some types of ENTRY values.  When using
Packit Service fdd496
   hash_insert, the only way to distinguish those cases is to compare
Packit Service fdd496
   the return value and ENTRY.  That works only when you can have two
Packit Service fdd496
   different ENTRY values that point to data that compares "equal".  Thus,
Packit Service fdd496
   when the ENTRY value is a simple scalar, you must use
Packit Service fdd496
   hash_insert_if_absent.  ENTRY must not be NULL.  */
Packit Service fdd496
int
Packit Service fdd496
hash_insert_if_absent (Hash_table *table, void const *entry,
Packit Service fdd496
                       void const **matched_ent)
Packit Service fdd496
{
Packit Service fdd496
  void *data;
Packit Service fdd496
  struct hash_entry *bucket;
Packit Service fdd496
Packit Service fdd496
  /* The caller cannot insert a NULL entry, since hash_lookup returns NULL
Packit Service fdd496
     to indicate "not found", and hash_find_entry uses "bucket->data == NULL"
Packit Service fdd496
     to indicate an empty bucket.  */
Packit Service fdd496
  if (! entry)
Packit Service fdd496
    abort ();
Packit Service fdd496
Packit Service fdd496
  /* If there's a matching entry already in the table, return that.  */
Packit Service fdd496
  if ((data = hash_find_entry (table, entry, &bucket, false)) != NULL)
Packit Service fdd496
    {
Packit Service fdd496
      if (matched_ent)
Packit Service fdd496
        *matched_ent = data;
Packit Service fdd496
      return 0;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* If the growth threshold of the buckets in use has been reached, increase
Packit Service fdd496
     the table size and rehash.  There's no point in checking the number of
Packit Service fdd496
     entries:  if the hashing function is ill-conditioned, rehashing is not
Packit Service fdd496
     likely to improve it.  */
Packit Service fdd496
Packit Service fdd496
  if (table->n_buckets_used
Packit Service fdd496
      > table->tuning->growth_threshold * table->n_buckets)
Packit Service fdd496
    {
Packit Service fdd496
      /* Check more fully, before starting real work.  If tuning arguments
Packit Service fdd496
         became invalid, the second check will rely on proper defaults.  */
Packit Service fdd496
      check_tuning (table);
Packit Service fdd496
      if (table->n_buckets_used
Packit Service fdd496
          > table->tuning->growth_threshold * table->n_buckets)
Packit Service fdd496
        {
Packit Service fdd496
          const Hash_tuning *tuning = table->tuning;
Packit Service fdd496
          float candidate =
Packit Service fdd496
            (tuning->is_n_buckets
Packit Service fdd496
             ? (table->n_buckets * tuning->growth_factor)
Packit Service fdd496
             : (table->n_buckets * tuning->growth_factor
Packit Service fdd496
                * tuning->growth_threshold));
Packit Service fdd496
Packit Service fdd496
          if (SIZE_MAX <= candidate)
Packit Service fdd496
            return -1;
Packit Service fdd496
Packit Service fdd496
          /* If the rehash fails, arrange to return NULL.  */
Packit Service fdd496
          if (!hash_rehash (table, candidate))
Packit Service fdd496
            return -1;
Packit Service fdd496
Packit Service fdd496
          /* Update the bucket we are interested in.  */
Packit Service fdd496
          if (hash_find_entry (table, entry, &bucket, false) != NULL)
Packit Service fdd496
            abort ();
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* ENTRY is not matched, it should be inserted.  */
Packit Service fdd496
Packit Service fdd496
  if (bucket->data)
Packit Service fdd496
    {
Packit Service fdd496
      struct hash_entry *new_entry = allocate_entry (table);
Packit Service fdd496
Packit Service fdd496
      if (new_entry == NULL)
Packit Service fdd496
        return -1;
Packit Service fdd496
Packit Service fdd496
      /* Add ENTRY in the overflow of the bucket.  */
Packit Service fdd496
Packit Service fdd496
      new_entry->data = (void *) entry;
Packit Service fdd496
      new_entry->next = bucket->next;
Packit Service fdd496
      bucket->next = new_entry;
Packit Service fdd496
      table->n_entries++;
Packit Service fdd496
      return 1;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Add ENTRY right in the bucket head.  */
Packit Service fdd496
Packit Service fdd496
  bucket->data = (void *) entry;
Packit Service fdd496
  table->n_entries++;
Packit Service fdd496
  table->n_buckets_used++;
Packit Service fdd496
Packit Service fdd496
  return 1;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* If ENTRY matches an entry already in the hash table, return the pointer
Packit Service fdd496
   to the entry from the table.  Otherwise, insert ENTRY and return ENTRY.
Packit Service fdd496
   Return NULL if the storage required for insertion cannot be allocated.
Packit Service fdd496
   This implementation does not support duplicate entries or insertion of
Packit Service fdd496
   NULL.  */
Packit Service fdd496
Packit Service fdd496
void *
Packit Service fdd496
hash_insert (Hash_table *table, void const *entry)
Packit Service fdd496
{
Packit Service fdd496
  void const *matched_ent;
Packit Service fdd496
  int err = hash_insert_if_absent (table, entry, &matched_ent);
Packit Service fdd496
  return (err == -1
Packit Service fdd496
          ? NULL
Packit Service fdd496
          : (void *) (err == 0 ? matched_ent : entry));
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* If ENTRY is already in the table, remove it and return the just-deleted
Packit Service fdd496
   data (the user may want to deallocate its storage).  If ENTRY is not in the
Packit Service fdd496
   table, don't modify the table and return NULL.  */
Packit Service fdd496
Packit Service fdd496
void *
Packit Service fdd496
hash_delete (Hash_table *table, const void *entry)
Packit Service fdd496
{
Packit Service fdd496
  void *data;
Packit Service fdd496
  struct hash_entry *bucket;
Packit Service fdd496
Packit Service fdd496
  data = hash_find_entry (table, entry, &bucket, true);
Packit Service fdd496
  if (!data)
Packit Service fdd496
    return NULL;
Packit Service fdd496
Packit Service fdd496
  table->n_entries--;
Packit Service fdd496
  if (!bucket->data)
Packit Service fdd496
    {
Packit Service fdd496
      table->n_buckets_used--;
Packit Service fdd496
Packit Service fdd496
      /* If the shrink threshold of the buckets in use has been reached,
Packit Service fdd496
         rehash into a smaller table.  */
Packit Service fdd496
Packit Service fdd496
      if (table->n_buckets_used
Packit Service fdd496
          < table->tuning->shrink_threshold * table->n_buckets)
Packit Service fdd496
        {
Packit Service fdd496
          /* Check more fully, before starting real work.  If tuning arguments
Packit Service fdd496
             became invalid, the second check will rely on proper defaults.  */
Packit Service fdd496
          check_tuning (table);
Packit Service fdd496
          if (table->n_buckets_used
Packit Service fdd496
              < table->tuning->shrink_threshold * table->n_buckets)
Packit Service fdd496
            {
Packit Service fdd496
              const Hash_tuning *tuning = table->tuning;
Packit Service fdd496
              size_t candidate =
Packit Service fdd496
                (tuning->is_n_buckets
Packit Service fdd496
                 ? table->n_buckets * tuning->shrink_factor
Packit Service fdd496
                 : (table->n_buckets * tuning->shrink_factor
Packit Service fdd496
                    * tuning->growth_threshold));
Packit Service fdd496
Packit Service fdd496
              if (!hash_rehash (table, candidate))
Packit Service fdd496
                {
Packit Service fdd496
                  /* Failure to allocate memory in an attempt to
Packit Service fdd496
                     shrink the table is not fatal.  But since memory
Packit Service fdd496
                     is low, we can at least be kind and free any
Packit Service fdd496
                     spare entries, rather than keeping them tied up
Packit Service fdd496
                     in the free entry list.  */
Packit Service fdd496
#if ! USE_OBSTACK
Packit Service fdd496
                  struct hash_entry *cursor = table->free_entry_list;
Packit Service fdd496
                  struct hash_entry *next;
Packit Service fdd496
                  while (cursor)
Packit Service fdd496
                    {
Packit Service fdd496
                      next = cursor->next;
Packit Service fdd496
                      free (cursor);
Packit Service fdd496
                      cursor = next;
Packit Service fdd496
                    }
Packit Service fdd496
                  table->free_entry_list = NULL;
Packit Service fdd496
#endif
Packit Service fdd496
                }
Packit Service fdd496
            }
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return data;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Testing.  */
Packit Service fdd496
Packit Service fdd496
#if TESTING
Packit Service fdd496
Packit Service fdd496
void
Packit Service fdd496
hash_print (const Hash_table *table)
Packit Service fdd496
{
Packit Service fdd496
  struct hash_entry *bucket = (struct hash_entry *) table->bucket;
Packit Service fdd496
Packit Service fdd496
  for ( ; bucket < table->bucket_limit; bucket++)
Packit Service fdd496
    {
Packit Service fdd496
      struct hash_entry *cursor;
Packit Service fdd496
Packit Service fdd496
      if (bucket)
Packit Service fdd496
        printf ("%lu:\n", (unsigned long int) (bucket - table->bucket));
Packit Service fdd496
Packit Service fdd496
      for (cursor = bucket; cursor; cursor = cursor->next)
Packit Service fdd496
        {
Packit Service fdd496
          char const *s = cursor->data;
Packit Service fdd496
          /* FIXME */
Packit Service fdd496
          if (s)
Packit Service fdd496
            printf ("  %s\n", s);
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
#endif /* TESTING */