(root)/
glibc-2.38/
include/
inline-hashtab.h
       1  /* Fully-inline hash table, used mainly for managing TLS descriptors.
       2     Copyright (C) 1999-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     This file is derived from a 2003's version of libiberty's
       6     hashtab.c, contributed by Vladimir Makarov (vmakarov@cygnus.com),
       7     but with most adaptation points and support for deleting elements
       8     removed.
       9  
      10     The GNU C Library is free software; you can redistribute it and/or
      11     modify it under the terms of the GNU Lesser General Public
      12     License as published by the Free Software Foundation; either
      13     version 2.1 of the License, or (at your option) any later version.
      14  
      15     The GNU C Library is distributed in the hope that it will be useful,
      16     but WITHOUT ANY WARRANTY; without even the implied warranty of
      17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      18     Lesser General Public License for more details.
      19  
      20     You should have received a copy of the GNU Lesser General Public
      21     License along with the GNU C Library; if not, see
      22     <https://www.gnu.org/licenses/>.  */
      23  
      24  #ifndef INLINE_HASHTAB_H
      25  # define INLINE_HASHTAB_H 1
      26  
      27  struct hashtab
      28  {
      29    /* Table itself.  */
      30    void **entries;
      31  
      32    /* Current size (in entries) of the hash table */
      33    size_t size;
      34  
      35    /* Current number of elements.  */
      36    size_t n_elements;
      37  
      38    /* Free function for the entries array.  This may vary depending on
      39       how early the array was allocated.  If it is NULL, then the array
      40       can't be freed.  */
      41    void (*free) (void *ptr);
      42  };
      43  
      44  inline static struct hashtab *
      45  htab_create (void)
      46  {
      47    struct hashtab *ht = malloc (sizeof (struct hashtab));
      48  
      49    if (! ht)
      50      return NULL;
      51    ht->size = 3;
      52    ht->entries = malloc (sizeof (void *) * ht->size);
      53    ht->free = __rtld_free;
      54    if (! ht->entries)
      55      {
      56        free (ht);
      57        return NULL;
      58      }
      59  
      60    ht->n_elements = 0;
      61  
      62    memset (ht->entries, 0, sizeof (void *) * ht->size);
      63  
      64    return ht;
      65  }
      66  
      67  /* This is only called from _dl_unmap, so it's safe to call
      68     free().  */
      69  inline static void
      70  htab_delete (struct hashtab *htab)
      71  {
      72    int i;
      73  
      74    for (i = htab->size - 1; i >= 0; i--)
      75      free (htab->entries[i]);
      76  
      77    htab->free (htab->entries);
      78    free (htab);
      79  }
      80  
      81  /* Similar to htab_find_slot, but without several unwanted side effects:
      82      - Does not call htab->eq_f when it finds an existing entry.
      83      - Does not change the count of elements/searches/collisions in the
      84        hash table.
      85     This function also assumes there are no deleted entries in the table.
      86     HASH is the hash value for the element to be inserted.  */
      87  
      88  inline static void **
      89  find_empty_slot_for_expand (struct hashtab *htab, int hash)
      90  {
      91    size_t size = htab->size;
      92    unsigned int index = hash % size;
      93    void **slot = htab->entries + index;
      94    int hash2;
      95  
      96    if (! *slot)
      97      return slot;
      98  
      99    hash2 = 1 + hash % (size - 2);
     100    for (;;)
     101      {
     102        index += hash2;
     103        if (index >= size)
     104  	index -= size;
     105  
     106        slot = htab->entries + index;
     107        if (! *slot)
     108  	return slot;
     109      }
     110  }
     111  
     112  /* The following function changes size of memory allocated for the
     113     entries and repeatedly inserts the table elements.  The occupancy
     114     of the table after the call will be about 50%.  Naturally the hash
     115     table must already exist.  Remember also that the place of the
     116     table entries is changed.  If memory allocation failures are allowed,
     117     this function will return zero, indicating that the table could not be
     118     expanded.  If all goes well, it will return a non-zero value.  */
     119  
     120  inline static int
     121  htab_expand (struct hashtab *htab, int (*hash_fn) (void *))
     122  {
     123    void **oentries;
     124    void **olimit;
     125    void **p;
     126    void **nentries;
     127    size_t nsize;
     128  
     129    oentries = htab->entries;
     130    olimit = oentries + htab->size;
     131  
     132    /* Resize only when table after removal of unused elements is either
     133       too full or too empty.  */
     134    if (htab->n_elements * 2 > htab->size)
     135      nsize = _dl_higher_prime_number (htab->n_elements * 2);
     136    else
     137      nsize = htab->size;
     138  
     139    nentries = calloc (sizeof (void *), nsize);
     140    if (nentries == NULL)
     141      return 0;
     142    htab->entries = nentries;
     143    htab->size = nsize;
     144  
     145    p = oentries;
     146    do
     147      {
     148        if (*p)
     149  	*find_empty_slot_for_expand (htab, hash_fn (*p))
     150  	  = *p;
     151  
     152        p++;
     153      }
     154    while (p < olimit);
     155  
     156    /* Without recording the free corresponding to the malloc used to
     157       allocate the table, we couldn't tell whether this was allocated
     158       by the malloc() built into ld.so or the one in the main
     159       executable or libc.  Calling free() for something that was
     160       allocated by the early malloc(), rather than the final run-time
     161       malloc() could do Very Bad Things (TM).  We will waste memory
     162       allocated early as long as there's no corresponding free(), but
     163       this isn't so much memory as to be significant.  */
     164  
     165    htab->free (oentries);
     166  
     167    /* Use the free() corresponding to the malloc() above to free this
     168       up.  */
     169    htab->free = __rtld_free;
     170  
     171    return 1;
     172  }
     173  
     174  /* This function searches for a hash table slot containing an entry
     175     equal to the given element.  To delete an entry, call this with
     176     INSERT = 0, then call htab_clear_slot on the slot returned (possibly
     177     after doing some checks).  To insert an entry, call this with
     178     INSERT = 1, then write the value you want into the returned slot.
     179     When inserting an entry, NULL may be returned if memory allocation
     180     fails.  */
     181  
     182  inline static void **
     183  htab_find_slot (struct hashtab *htab, void *ptr, int insert,
     184  		int (*hash_fn)(void *), int (*eq_fn)(void *, void *))
     185  {
     186    unsigned int index;
     187    int hash, hash2;
     188    size_t size;
     189    void **entry;
     190  
     191    if (htab->size * 3 <= htab->n_elements * 4
     192        && htab_expand (htab, hash_fn) == 0)
     193      return NULL;
     194  
     195    hash = hash_fn (ptr);
     196  
     197    size = htab->size;
     198    index = hash % size;
     199  
     200    entry = &htab->entries[index];
     201    if (!*entry)
     202      goto empty_entry;
     203    else if (eq_fn (*entry, ptr))
     204      return entry;
     205  
     206    hash2 = 1 + hash % (size - 2);
     207    for (;;)
     208      {
     209        index += hash2;
     210        if (index >= size)
     211  	index -= size;
     212  
     213        entry = &htab->entries[index];
     214        if (!*entry)
     215  	goto empty_entry;
     216        else if (eq_fn (*entry, ptr))
     217  	return entry;
     218      }
     219  
     220   empty_entry:
     221    if (!insert)
     222      return NULL;
     223  
     224    htab->n_elements++;
     225    return entry;
     226  }
     227  
     228  #endif /* INLINE_HASHTAB_H */