blob: 21d9458c8352f742ee0b11bcf7ca59e677643010 [file] [log] [blame]
Ben Elliston88e17b51998-09-21 01:22:07 +00001/* Hash tables for Objective C internal structures
Jakub Jelinek748086b2009-04-09 17:00:19 +02002 Copyright (C) 1993, 1996, 1997, 2004, 2009 Free Software Foundation, Inc.
Ben Elliston88e17b51998-09-21 01:22:07 +00003
Nathanael Nerode38709ca2003-05-23 20:25:39 +00004This file is part of GCC.
Ben Elliston88e17b51998-09-21 01:22:07 +00005
Nathanael Nerode38709ca2003-05-23 20:25:39 +00006GCC is free software; you can redistribute it and/or modify
Ben Elliston88e17b51998-09-21 01:22:07 +00007it under the terms of the GNU General Public License as published by
Jakub Jelinek748086b2009-04-09 17:00:19 +02008the Free Software Foundation; either version 3, or (at your option)
Ben Elliston88e17b51998-09-21 01:22:07 +00009any later version.
10
Nathanael Nerode38709ca2003-05-23 20:25:39 +000011GCC is distributed in the hope that it will be useful,
Ben Elliston88e17b51998-09-21 01:22:07 +000012but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
Jakub Jelinek748086b2009-04-09 17:00:19 +020016Under Section 7 of GPL version 3, you are granted additional
17permissions described in the GCC Runtime Library Exception, version
183.1, as published by the Free Software Foundation.
Ben Elliston88e17b51998-09-21 01:22:07 +000019
Jakub Jelinek748086b2009-04-09 17:00:19 +020020You should have received a copy of the GNU General Public License and
21a copy of the GCC Runtime Library Exception along with this program;
22see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23<http://www.gnu.org/licenses/>. */
Ben Elliston88e17b51998-09-21 01:22:07 +000024
25#include "assert.h"
26
David Ayers348a3442005-06-07 23:04:19 +020027#include "objc/hash.h"
Ben Elliston88e17b51998-09-21 01:22:07 +000028
David Ayers348a3442005-06-07 23:04:19 +020029#include "objc/runtime.h" /* for DEBUG_PRINTF */
Ben Elliston88e17b51998-09-21 01:22:07 +000030
31/* These two macros determine when a hash table is full and
32 by how much it should be expanded respectively.
33
34 These equations are percentages. */
35#define FULLNESS(cache) \
36 ((((cache)->size * 75) / 100) <= (cache)->used)
37#define EXPANSION(cache) \
38 ((cache)->size * 2)
39
40cache_ptr
David Ayers270a1282005-03-02 20:37:03 +010041objc_hash_new (unsigned int size, hash_func_type hash_func,
42 compare_func_type compare_func)
Ben Elliston88e17b51998-09-21 01:22:07 +000043{
44 cache_ptr cache;
45
46 /* Pass me a value greater than 0 and a power of 2. */
47 assert (size);
Rodney Brown40165632002-07-02 19:43:03 +000048 assert (! (size & (size - 1)));
Ben Elliston88e17b51998-09-21 01:22:07 +000049
50 /* Allocate the cache structure. calloc insures
51 its initialization for default values. */
52 cache = (cache_ptr) objc_calloc (1, sizeof (struct cache));
53 assert (cache);
54
55 /* Allocate the array of buckets for the cache.
56 calloc initializes all of the pointers to NULL. */
57 cache->node_table
58 = (node_ptr *) objc_calloc (size, sizeof (node_ptr));
59 assert (cache->node_table);
60
61 cache->size = size;
62
63 /* This should work for all processor architectures? */
64 cache->mask = (size - 1);
65
66 /* Store the hashing function so that codes can be computed. */
67 cache->hash_func = hash_func;
68
69 /* Store the function that compares hash keys to
70 determine if they are equal. */
71 cache->compare_func = compare_func;
72
73 return cache;
74}
75
76
77void
David Ayers270a1282005-03-02 20:37:03 +010078objc_hash_delete (cache_ptr cache)
Ben Elliston88e17b51998-09-21 01:22:07 +000079{
80 node_ptr node;
81 node_ptr next_node;
82 unsigned int i;
83
84 /* Purge all key/value pairs from the table. */
85 /* Step through the nodes one by one and remove every node WITHOUT
David Ayers270a1282005-03-02 20:37:03 +010086 using objc_hash_next. this makes objc_hash_delete much more efficient. */
Ben Elliston88e17b51998-09-21 01:22:07 +000087 for (i = 0;i < cache->size;i++) {
88 if ((node = cache->node_table[i])) {
89 /* an entry in the hash table has been found, now step through the
90 nodes next in the list and free them. */
91 while ((next_node = node->next)) {
David Ayers270a1282005-03-02 20:37:03 +010092 objc_hash_remove (cache,node->key);
Ben Elliston88e17b51998-09-21 01:22:07 +000093 node = next_node;
94 }
95
David Ayers270a1282005-03-02 20:37:03 +010096 objc_hash_remove (cache,node->key);
Ben Elliston88e17b51998-09-21 01:22:07 +000097 }
98 }
99
100 /* Release the array of nodes and the cache itself. */
101 objc_free(cache->node_table);
102 objc_free(cache);
103}
104
105
106void
David Ayers270a1282005-03-02 20:37:03 +0100107objc_hash_add (cache_ptr *cachep, const void *key, void *value)
Ben Elliston88e17b51998-09-21 01:22:07 +0000108{
109 size_t indx = (*(*cachep)->hash_func)(*cachep, key);
110 node_ptr node = (node_ptr) objc_calloc (1, sizeof (struct cache_node));
111
112
113 assert (node);
114
115 /* Initialize the new node. */
116 node->key = key;
117 node->value = value;
118 node->next = (*cachep)->node_table[indx];
119
120 /* Debugging.
121 Check the list for another key. */
122#ifdef DEBUG
123 { node_ptr node1 = (*cachep)->node_table[indx];
124
125 while (node1) {
126
127 assert (node1->key != key);
128 node1 = node1->next;
129 }
130 }
131#endif
132
133 /* Install the node as the first element on the list. */
134 (*cachep)->node_table[indx] = node;
135
136 /* Bump the number of entries in the cache. */
137 ++(*cachep)->used;
138
139 /* Check the hash table's fullness. We're going
140 to expand if it is above the fullness level. */
141 if (FULLNESS (*cachep)) {
142
143 /* The hash table has reached its fullness level. Time to
144 expand it.
145
146 I'm using a slow method here but is built on other
147 primitive functions thereby increasing its
148 correctness. */
149 node_ptr node1 = NULL;
David Ayers270a1282005-03-02 20:37:03 +0100150 cache_ptr new = objc_hash_new (EXPANSION (*cachep),
151 (*cachep)->hash_func,
152 (*cachep)->compare_func);
Ben Elliston88e17b51998-09-21 01:22:07 +0000153
154 DEBUG_PRINTF ("Expanding cache %#x from %d to %d\n",
Andrew Pinski435317e2004-05-25 19:10:54 +0000155 (int) *cachep, (*cachep)->size, new->size);
Ben Elliston88e17b51998-09-21 01:22:07 +0000156
157 /* Copy the nodes from the first hash table to the new one. */
David Ayers270a1282005-03-02 20:37:03 +0100158 while ((node1 = objc_hash_next (*cachep, node1)))
159 objc_hash_add (&new, node1->key, node1->value);
Ben Elliston88e17b51998-09-21 01:22:07 +0000160
161 /* Trash the old cache. */
David Ayers270a1282005-03-02 20:37:03 +0100162 objc_hash_delete (*cachep);
Ben Elliston88e17b51998-09-21 01:22:07 +0000163
164 /* Return a pointer to the new hash table. */
165 *cachep = new;
166 }
167}
168
169
170void
David Ayers270a1282005-03-02 20:37:03 +0100171objc_hash_remove (cache_ptr cache, const void *key)
Ben Elliston88e17b51998-09-21 01:22:07 +0000172{
173 size_t indx = (*cache->hash_func)(cache, key);
174 node_ptr node = cache->node_table[indx];
175
176
177 /* We assume there is an entry in the table. Error if it is not. */
178 assert (node);
179
180 /* Special case. First element is the key/value pair to be removed. */
181 if ((*cache->compare_func)(node->key, key)) {
182 cache->node_table[indx] = node->next;
183 objc_free(node);
184 } else {
185
186 /* Otherwise, find the hash entry. */
187 node_ptr prev = node;
188 BOOL removed = NO;
189
190 do {
191
192 if ((*cache->compare_func)(node->key, key)) {
193 prev->next = node->next, removed = YES;
194 objc_free(node);
195 } else
196 prev = node, node = node->next;
Rodney Brown40165632002-07-02 19:43:03 +0000197 } while (! removed && node);
Ben Elliston88e17b51998-09-21 01:22:07 +0000198 assert (removed);
199 }
200
201 /* Decrement the number of entries in the hash table. */
202 --cache->used;
203}
204
205
206node_ptr
David Ayers270a1282005-03-02 20:37:03 +0100207objc_hash_next (cache_ptr cache, node_ptr node)
Ben Elliston88e17b51998-09-21 01:22:07 +0000208{
209 /* If the scan is being started then reset the last node
210 visitied pointer and bucket index. */
Rodney Brown40165632002-07-02 19:43:03 +0000211 if (! node)
Ben Elliston88e17b51998-09-21 01:22:07 +0000212 cache->last_bucket = 0;
213
214 /* If there is a node visited last then check for another
215 entry in the same bucket; Otherwise step to the next bucket. */
216 if (node) {
217 if (node->next)
218 /* There is a node which follows the last node
219 returned. Step to that node and retun it. */
220 return node->next;
221 else
222 ++cache->last_bucket;
223 }
224
225 /* If the list isn't exhausted then search the buckets for
226 other nodes. */
227 if (cache->last_bucket < cache->size) {
228 /* Scan the remainder of the buckets looking for an entry
229 at the head of the list. Return the first item found. */
230 while (cache->last_bucket < cache->size)
231 if (cache->node_table[cache->last_bucket])
232 return cache->node_table[cache->last_bucket];
233 else
234 ++cache->last_bucket;
235
236 /* No further nodes were found in the hash table. */
237 return NULL;
238 } else
239 return NULL;
240}
241
242
243/* Given KEY, return corresponding value for it in CACHE.
244 Return NULL if the KEY is not recorded. */
245
246void *
David Ayers270a1282005-03-02 20:37:03 +0100247objc_hash_value_for_key (cache_ptr cache, const void *key)
Ben Elliston88e17b51998-09-21 01:22:07 +0000248{
249 node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)];
250 void *retval = NULL;
251
252 if (node)
253 do {
254 if ((*cache->compare_func)(node->key, key)) {
255 retval = node->value;
256 break;
257 } else
258 node = node->next;
Rodney Brown40165632002-07-02 19:43:03 +0000259 } while (! retval && node);
Ben Elliston88e17b51998-09-21 01:22:07 +0000260
261 return retval;
262}
263
264/* Given KEY, return YES if it exists in the CACHE.
265 Return NO if it does not */
266
267BOOL
David Ayers270a1282005-03-02 20:37:03 +0100268objc_hash_is_key_in_hash (cache_ptr cache, const void *key)
Ben Elliston88e17b51998-09-21 01:22:07 +0000269{
270 node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)];
271
272 if (node)
273 do {
274 if ((*cache->compare_func)(node->key, key))
275 return YES;
276 else
277 node = node->next;
278 } while (node);
279
280 return NO;
281}