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 */ |
229 | |