Blame misc/hsearch_r.c

Packit 6c4009
/* Copyright (C) 1993-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1993.
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
#include <errno.h>
Packit 6c4009
#include <malloc.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
#include <search.h>
Packit 6c4009
#include <limits.h>
Packit 6c4009
Packit 6c4009
/* [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986
Packit 6c4009
   [Knuth]            The Art of Computer Programming, part 3 (6.4)  */
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* The reentrant version has no static variables to maintain the state.
Packit 6c4009
   Instead the interface of all functions is extended to take an argument
Packit 6c4009
   which describes the current status.  */
Packit 6c4009
typedef struct _ENTRY
Packit 6c4009
{
Packit 6c4009
  unsigned int used;
Packit 6c4009
  ENTRY entry;
Packit 6c4009
}
Packit 6c4009
_ENTRY;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* For the used double hash method the table size has to be a prime. To
Packit 6c4009
   correct the user given table size we need a prime test.  This trivial
Packit 6c4009
   algorithm is adequate because
Packit 6c4009
   a)  the code is (most probably) called a few times per program run and
Packit 6c4009
   b)  the number is small because the table must fit in the core  */
Packit 6c4009
static int
Packit 6c4009
isprime (unsigned int number)
Packit 6c4009
{
Packit 6c4009
  /* no even number will be passed */
Packit 6c4009
  for (unsigned int div = 3; div <= number / div; div += 2)
Packit 6c4009
    if (number % div == 0)
Packit 6c4009
      return 0;
Packit 6c4009
  return 1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Before using the hash table we must allocate memory for it.
Packit 6c4009
   Test for an existing table are done. We allocate one element
Packit 6c4009
   more as the found prime number says. This is done for more effective
Packit 6c4009
   indexing as explained in the comment for the hsearch function.
Packit 6c4009
   The contents of the table is zeroed, especially the field used
Packit 6c4009
   becomes zero.  */
Packit 6c4009
int
Packit 6c4009
__hcreate_r (size_t nel, struct hsearch_data *htab)
Packit 6c4009
{
Packit 6c4009
  /* Test for correct arguments.  */
Packit 6c4009
  if (htab == NULL)
Packit 6c4009
    {
Packit 6c4009
      __set_errno (EINVAL);
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* There is still another table active. Return with error. */
Packit 6c4009
  if (htab->table != NULL)
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  /* We need a size of at least 3.  Otherwise the hash functions we
Packit 6c4009
     use will not work.  */
Packit 6c4009
  if (nel < 3)
Packit 6c4009
    nel = 3;
Packit 6c4009
Packit 6c4009
  /* Change nel to the first prime number in the range [nel, UINT_MAX - 2],
Packit 6c4009
     The '- 2' means 'nel += 2' cannot overflow.  */
Packit 6c4009
  for (nel |= 1; ; nel += 2)
Packit 6c4009
    {
Packit 6c4009
      if (UINT_MAX - 2 < nel)
Packit 6c4009
	{
Packit 6c4009
	  __set_errno (ENOMEM);
Packit 6c4009
	  return 0;
Packit 6c4009
	}
Packit 6c4009
      if (isprime (nel))
Packit 6c4009
	break;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  htab->size = nel;
Packit 6c4009
  htab->filled = 0;
Packit 6c4009
Packit 6c4009
  /* allocate memory and zero out */
Packit 6c4009
  htab->table = (_ENTRY *) calloc (htab->size + 1, sizeof (_ENTRY));
Packit 6c4009
  if (htab->table == NULL)
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  /* everything went alright */
Packit 6c4009
  return 1;
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__hcreate_r)
Packit 6c4009
weak_alias (__hcreate_r, hcreate_r)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* After using the hash table it has to be destroyed. The used memory can
Packit 6c4009
   be freed and the local static variable can be marked as not used.  */
Packit 6c4009
void
Packit 6c4009
__hdestroy_r (struct hsearch_data *htab)
Packit 6c4009
{
Packit 6c4009
  /* Test for correct arguments.  */
Packit 6c4009
  if (htab == NULL)
Packit 6c4009
    {
Packit 6c4009
      __set_errno (EINVAL);
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Free used memory.  */
Packit 6c4009
  free (htab->table);
Packit 6c4009
Packit 6c4009
  /* the sign for an existing table is an value != NULL in htable */
Packit 6c4009
  htab->table = NULL;
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__hdestroy_r)
Packit 6c4009
weak_alias (__hdestroy_r, hdestroy_r)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* This is the search function. It uses double hashing with open addressing.
Packit 6c4009
   The argument item.key has to be a pointer to an zero terminated, most
Packit 6c4009
   probably strings of chars. The function for generating a number of the
Packit 6c4009
   strings is simple but fast. It can be replaced by a more complex function
Packit 6c4009
   like ajw (see [Aho,Sethi,Ullman]) if the needs are shown.
Packit 6c4009
Packit 6c4009
   We use an trick to speed up the lookup. The table is created by hcreate
Packit 6c4009
   with one more element available. This enables us to use the index zero
Packit 6c4009
   special. This index will never be used because we store the first hash
Packit 6c4009
   index in the field used where zero means not used. Every other value
Packit 6c4009
   means used. The used field can be used as a first fast comparison for
Packit 6c4009
   equality of the stored and the parameter value. This helps to prevent
Packit 6c4009
   unnecessary expensive calls of strcmp.  */
Packit 6c4009
int
Packit 6c4009
__hsearch_r (ENTRY item, ACTION action, ENTRY **retval,
Packit 6c4009
	     struct hsearch_data *htab)
Packit 6c4009
{
Packit 6c4009
  unsigned int hval;
Packit 6c4009
  unsigned int count;
Packit 6c4009
  unsigned int len = strlen (item.key);
Packit 6c4009
  unsigned int idx;
Packit 6c4009
Packit 6c4009
  /* Compute an value for the given string. Perhaps use a better method. */
Packit 6c4009
  hval = len;
Packit 6c4009
  count = len;
Packit 6c4009
  while (count-- > 0)
Packit 6c4009
    {
Packit 6c4009
      hval <<= 4;
Packit 6c4009
      hval += item.key[count];
Packit 6c4009
    }
Packit 6c4009
  if (hval == 0)
Packit 6c4009
    ++hval;
Packit 6c4009
Packit 6c4009
  /* First hash function: simply take the modul but prevent zero. */
Packit 6c4009
  idx = hval % htab->size + 1;
Packit 6c4009
Packit 6c4009
  if (htab->table[idx].used)
Packit 6c4009
    {
Packit 6c4009
      /* Further action might be required according to the action value. */
Packit 6c4009
      if (htab->table[idx].used == hval
Packit 6c4009
	  && strcmp (item.key, htab->table[idx].entry.key) == 0)
Packit 6c4009
	{
Packit 6c4009
	  *retval = &htab->table[idx].entry;
Packit 6c4009
	  return 1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Second hash function, as suggested in [Knuth] */
Packit 6c4009
      unsigned int hval2 = 1 + hval % (htab->size - 2);
Packit 6c4009
      unsigned int first_idx = idx;
Packit 6c4009
Packit 6c4009
      do
Packit 6c4009
	{
Packit 6c4009
	  /* Because SIZE is prime this guarantees to step through all
Packit 6c4009
             available indeces.  */
Packit 6c4009
          if (idx <= hval2)
Packit 6c4009
	    idx = htab->size + idx - hval2;
Packit 6c4009
	  else
Packit 6c4009
	    idx -= hval2;
Packit 6c4009
Packit 6c4009
	  /* If we visited all entries leave the loop unsuccessfully.  */
Packit 6c4009
	  if (idx == first_idx)
Packit 6c4009
	    break;
Packit 6c4009
Packit 6c4009
            /* If entry is found use it. */
Packit 6c4009
          if (htab->table[idx].used == hval
Packit 6c4009
	      && strcmp (item.key, htab->table[idx].entry.key) == 0)
Packit 6c4009
	    {
Packit 6c4009
	      *retval = &htab->table[idx].entry;
Packit 6c4009
	      return 1;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      while (htab->table[idx].used);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* An empty bucket has been found. */
Packit 6c4009
  if (action == ENTER)
Packit 6c4009
    {
Packit 6c4009
      /* If table is full and another entry should be entered return
Packit 6c4009
	 with error.  */
Packit 6c4009
      if (htab->filled == htab->size)
Packit 6c4009
	{
Packit 6c4009
	  __set_errno (ENOMEM);
Packit 6c4009
	  *retval = NULL;
Packit 6c4009
	  return 0;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      htab->table[idx].used  = hval;
Packit 6c4009
      htab->table[idx].entry = item;
Packit 6c4009
Packit 6c4009
      ++htab->filled;
Packit 6c4009
Packit 6c4009
      *retval = &htab->table[idx].entry;
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __set_errno (ESRCH);
Packit 6c4009
  *retval = NULL;
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__hsearch_r)
Packit 6c4009
weak_alias (__hsearch_r, hsearch_r)