| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- /* Fully-inline hash table, used mainly for managing TLS descriptors.
- Copyright (C) 1999-2026 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- 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
- <https://www.gnu.org/licenses/>. */
- #ifndef INLINE_HASHTAB_H
- # define INLINE_HASHTAB_H 1
- 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 = __rtld_free;
- if (! ht->entries)
- {
- 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]);
- 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. */
- htab->free (oentries);
- /* Use the free() corresponding to the malloc() above to free this
- up. */
- htab->free = __rtld_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 */
|