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