Blame include/inline-hashtab.h

Packit 6c4009
/* Fully-inline hash table, used mainly for managing TLS descriptors.
Packit 6c4009
   Copyright (C) 1999-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Alexandre Oliva  <aoliva@redhat.com>
Packit 6c4009
Packit 6c4009
   This file is derived from a 2003's version of libiberty's
Packit 6c4009
   hashtab.c, contributed by Vladimir Makarov (vmakarov@cygnus.com),
Packit 6c4009
   but with most adaptation points and support for deleting elements
Packit 6c4009
   removed.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#ifndef INLINE_HASHTAB_H
Packit 6c4009
# define INLINE_HASHTAB_H 1
Packit 6c4009
Packit 6c4009
extern void weak_function free (void *ptr);
Packit 6c4009
Packit 6c4009
struct hashtab
Packit 6c4009
{
Packit 6c4009
  /* Table itself.  */
Packit 6c4009
  void **entries;
Packit 6c4009
Packit 6c4009
  /* Current size (in entries) of the hash table */
Packit 6c4009
  size_t size;
Packit 6c4009
Packit 6c4009
  /* Current number of elements.  */
Packit 6c4009
  size_t n_elements;
Packit 6c4009
Packit 6c4009
  /* Free function for the entries array.  This may vary depending on
Packit 6c4009
     how early the array was allocated.  If it is NULL, then the array
Packit 6c4009
     can't be freed.  */
Packit 6c4009
  void (*free) (void *ptr);
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
inline static struct hashtab *
Packit 6c4009
htab_create (void)
Packit 6c4009
{
Packit 6c4009
  struct hashtab *ht = malloc (sizeof (struct hashtab));
Packit 6c4009
Packit 6c4009
  if (! ht)
Packit 6c4009
    return NULL;
Packit 6c4009
  ht->size = 3;
Packit 6c4009
  ht->entries = malloc (sizeof (void *) * ht->size);
Packit 6c4009
  ht->free = free;
Packit 6c4009
  if (! ht->entries)
Packit 6c4009
    {
Packit 6c4009
      if (ht->free)
Packit 6c4009
	ht->free (ht);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  ht->n_elements = 0;
Packit 6c4009
Packit 6c4009
  memset (ht->entries, 0, sizeof (void *) * ht->size);
Packit 6c4009
Packit 6c4009
  return ht;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* This is only called from _dl_unmap, so it's safe to call
Packit 6c4009
   free().  */
Packit 6c4009
inline static void
Packit 6c4009
htab_delete (struct hashtab *htab)
Packit 6c4009
{
Packit 6c4009
  int i;
Packit 6c4009
Packit 6c4009
  for (i = htab->size - 1; i >= 0; i--)
Packit 6c4009
    free (htab->entries[i]);
Packit 6c4009
Packit 6c4009
  if (htab->free)
Packit 6c4009
    htab->free (htab->entries);
Packit 6c4009
  free (htab);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Similar to htab_find_slot, but without several unwanted side effects:
Packit 6c4009
    - Does not call htab->eq_f when it finds an existing entry.
Packit 6c4009
    - Does not change the count of elements/searches/collisions in the
Packit 6c4009
      hash table.
Packit 6c4009
   This function also assumes there are no deleted entries in the table.
Packit 6c4009
   HASH is the hash value for the element to be inserted.  */
Packit 6c4009
Packit 6c4009
inline static void **
Packit 6c4009
find_empty_slot_for_expand (struct hashtab *htab, int hash)
Packit 6c4009
{
Packit 6c4009
  size_t size = htab->size;
Packit 6c4009
  unsigned int index = hash % size;
Packit 6c4009
  void **slot = htab->entries + index;
Packit 6c4009
  int hash2;
Packit 6c4009
Packit 6c4009
  if (! *slot)
Packit 6c4009
    return slot;
Packit 6c4009
Packit 6c4009
  hash2 = 1 + hash % (size - 2);
Packit 6c4009
  for (;;)
Packit 6c4009
    {
Packit 6c4009
      index += hash2;
Packit 6c4009
      if (index >= size)
Packit 6c4009
	index -= size;
Packit 6c4009
Packit 6c4009
      slot = htab->entries + index;
Packit 6c4009
      if (! *slot)
Packit 6c4009
	return slot;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* The following function changes size of memory allocated for the
Packit 6c4009
   entries and repeatedly inserts the table elements.  The occupancy
Packit 6c4009
   of the table after the call will be about 50%.  Naturally the hash
Packit 6c4009
   table must already exist.  Remember also that the place of the
Packit 6c4009
   table entries is changed.  If memory allocation failures are allowed,
Packit 6c4009
   this function will return zero, indicating that the table could not be
Packit 6c4009
   expanded.  If all goes well, it will return a non-zero value.  */
Packit 6c4009
Packit 6c4009
inline static int
Packit 6c4009
htab_expand (struct hashtab *htab, int (*hash_fn) (void *))
Packit 6c4009
{
Packit 6c4009
  void **oentries;
Packit 6c4009
  void **olimit;
Packit 6c4009
  void **p;
Packit 6c4009
  void **nentries;
Packit 6c4009
  size_t nsize;
Packit 6c4009
Packit 6c4009
  oentries = htab->entries;
Packit 6c4009
  olimit = oentries + htab->size;
Packit 6c4009
Packit 6c4009
  /* Resize only when table after removal of unused elements is either
Packit 6c4009
     too full or too empty.  */
Packit 6c4009
  if (htab->n_elements * 2 > htab->size)
Packit 6c4009
    nsize = _dl_higher_prime_number (htab->n_elements * 2);
Packit 6c4009
  else
Packit 6c4009
    nsize = htab->size;
Packit 6c4009
Packit 6c4009
  nentries = calloc (sizeof (void *), nsize);
Packit 6c4009
  if (nentries == NULL)
Packit 6c4009
    return 0;
Packit 6c4009
  htab->entries = nentries;
Packit 6c4009
  htab->size = nsize;
Packit 6c4009
Packit 6c4009
  p = oentries;
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
      if (*p)
Packit 6c4009
	*find_empty_slot_for_expand (htab, hash_fn (*p))
Packit 6c4009
	  = *p;
Packit 6c4009
Packit 6c4009
      p++;
Packit 6c4009
    }
Packit 6c4009
  while (p < olimit);
Packit 6c4009
Packit 6c4009
  /* Without recording the free corresponding to the malloc used to
Packit 6c4009
     allocate the table, we couldn't tell whether this was allocated
Packit 6c4009
     by the malloc() built into ld.so or the one in the main
Packit 6c4009
     executable or libc.  Calling free() for something that was
Packit 6c4009
     allocated by the early malloc(), rather than the final run-time
Packit 6c4009
     malloc() could do Very Bad Things (TM).  We will waste memory
Packit 6c4009
     allocated early as long as there's no corresponding free(), but
Packit 6c4009
     this isn't so much memory as to be significant.  */
Packit 6c4009
Packit 6c4009
  if (htab->free)
Packit 6c4009
    htab->free (oentries);
Packit 6c4009
Packit 6c4009
  /* Use the free() corresponding to the malloc() above to free this
Packit 6c4009
     up.  */
Packit 6c4009
  htab->free = free;
Packit 6c4009
Packit 6c4009
  return 1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* This function searches for a hash table slot containing an entry
Packit 6c4009
   equal to the given element.  To delete an entry, call this with
Packit 6c4009
   INSERT = 0, then call htab_clear_slot on the slot returned (possibly
Packit 6c4009
   after doing some checks).  To insert an entry, call this with
Packit 6c4009
   INSERT = 1, then write the value you want into the returned slot.
Packit 6c4009
   When inserting an entry, NULL may be returned if memory allocation
Packit 6c4009
   fails.  */
Packit 6c4009
Packit 6c4009
inline static void **
Packit 6c4009
htab_find_slot (struct hashtab *htab, void *ptr, int insert,
Packit 6c4009
		int (*hash_fn)(void *), int (*eq_fn)(void *, void *))
Packit 6c4009
{
Packit 6c4009
  unsigned int index;
Packit 6c4009
  int hash, hash2;
Packit 6c4009
  size_t size;
Packit 6c4009
  void **entry;
Packit 6c4009
Packit 6c4009
  if (htab->size * 3 <= htab->n_elements * 4
Packit 6c4009
      && htab_expand (htab, hash_fn) == 0)
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
  hash = hash_fn (ptr);
Packit 6c4009
Packit 6c4009
  size = htab->size;
Packit 6c4009
  index = hash % size;
Packit 6c4009
Packit 6c4009
  entry = &htab->entries[index];
Packit 6c4009
  if (!*entry)
Packit 6c4009
    goto empty_entry;
Packit 6c4009
  else if (eq_fn (*entry, ptr))
Packit 6c4009
    return entry;
Packit 6c4009
Packit 6c4009
  hash2 = 1 + hash % (size - 2);
Packit 6c4009
  for (;;)
Packit 6c4009
    {
Packit 6c4009
      index += hash2;
Packit 6c4009
      if (index >= size)
Packit 6c4009
	index -= size;
Packit 6c4009
Packit 6c4009
      entry = &htab->entries[index];
Packit 6c4009
      if (!*entry)
Packit 6c4009
	goto empty_entry;
Packit 6c4009
      else if (eq_fn (*entry, ptr))
Packit 6c4009
	return entry;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
 empty_entry:
Packit 6c4009
  if (!insert)
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
  htab->n_elements++;
Packit 6c4009
  return entry;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#endif /* INLINE_HASHTAB_H */