Blame misc/hsearch_r.c

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